Hibernate is multiple #EmbeddedId supported? - java

Is it possible to have multiple #EmbeddedId composite keys defined for a #ManyToOne relationship between classes? Example:
Entity 1
#Entity
#AttributeOverrides({ #AttributeOverride(name="pk.method", column=#Column(name = "http_method", nullable = false)) })
#AssociationOverrides({ #AssociationOverride(name = "pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
public class Action {
#EmbeddedId
private ActionId pk = new ActionId();
}
#Embeddable
private class ActionId implements Serializable {
#ManyToOne private Operation operation;
private HttpMethod method;
}
Entity 2:
#Entity
#AssociationOverrides
// Need to override the embedded if attributes of the Action entity inside the UserActionId class
public class UserAction {
#EmbeddedId private UserActionId pk = new UserActionId();
}
#Embeddable
private class UserActionId implements Serializable {
// How to provide a #JoinColumn for the Action.pk composite key (action.pk.authURL) and #Column for (action.pk.httpMethod)
#ManyToOne private Action action;
#ManyToOne private User user;
}
Is this possible? I have tried but keep getting a Hibernate error saying that the FK must have the same number of columns as the ActionId pk.

Related

How to remove children from parent entity record in JPA?

I have Product entity and ProductRating entity, each Product can have many ProductRatings. When Product is deleted I want to have associated ratings deleted too, but nothing works so far (also orphanRemoval set to true)...
Classes:
#Getter
#Setter
#Entity
#Table(name = "PRODUCT")
public class Product extends AbstractEntity<Long> {
#Column(nullable = false)
private String name;
private String description;
#Column(nullable = false)
#Min(value = 0)
private Float cost;
#OneToMany(mappedBy = "product",
orphanRemoval = true, cascade = CascadeType.PERSIST,
fetch = FetchType.EAGER)
//#OnDelete(action = OnDeleteAction.CASCADE)
#Fetch(value = FetchMode.SELECT)
private Set<ProductRating> productRatings;
}
#Getter
#Setter
#Entity
#Table(name = "PRODUCT_RATING")
public class ProductRating extends Rating {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "product_id")
#NotNull(message = "Rating must be in context of Product")
private Product product;
}
After Product deletion ratings stay with deleted Product's ID
AbstractEntity implementation:
#Getter
#Setter
#MappedSuperclass
public abstract class AbstractEntity<I> implements Serializable {
private static final long serialVersionUID = 1700166770839683115L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false)
private I id;
}
In the #OneToMany relation you need to add the cascade type delete: cascade = {CascadeType.PERSIST, CascadeType.REMOVE}
Or if you don't mind having all cascade types you can just put: cascade = CascadeType.ALL
EDIT:
Also check the name of the Product primary key in the database.
It should match the defined in the #JoinColumn annotation of ProductRating
The default database field for the attribute id of the Product class would be product_id.
However you have defined the id in AbstractEntity as name = "ID" so the #JoinColumn should be something like: #JoinColumn(name = "ID")
My alternative approach to fix this problem is to:
On parent-side relation create method with #PreRemove annotation
in this method iterate over collection with #[One/Many]ToMany annotation and call delete(obj) method for corresponding repository on child
On child-side relation create method with #PreRemove annotation
In this method set parent to null

Hibernate Referencing Embedded ID columns in FK

I'm having a problem mapping multiple #EmbeddedId columns of different entity classes. I have an entity structure like the following (have simplified the entities for readability).
Entity: Action
#Entity
#AttributeOverrides({ #AttributeOverride(name="pk.method", column=#Column(name = "http_method", nullable = false)) })
#AssociationOverrides({ #AssociationOverride(name = "pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
public class Action {
#EmbeddedId
private ActionId pk = new ActionId();
}
This entity has an #EmbeddedId defined as the following:
#Embeddable
private class ActionId implements Serializable {
#ManyToOne private Operation operation;
private HttpMethod method;
}
The mappings here between the pk columns work fine. The problems come when I attempt to add another entity which is a link table to the Action entity. This link table also has an #EmbeddedId.
Entity: UserAction
#Entity
#AssociationOverrides .. ?
public class UserAction {
#EmbeddedId private UserActionId pk = new UserActionId();
}
The #EmbeddedId class looks like:
#Embeddable
private class UserActionId implements Serializable {
#ManyToOne private Action action;
#ManyToOne private User user;
}
How to I reference the #EmbeddedId fields of the Action class, but only one of the columns is a join column. The other column in the ActionId class is a regular #Column.
I have tried this:
#AssociationOverrides({
#AssociationOverride(name = "pk.action.pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
But this is giving me an error:
Caused by: org.hibernate.MappingException: Foreign key (FK_gwvslpytxm695kdw5lxqneyss:user_action [useraction])) must have same number of columns as the referenced primary key (useraction[id_operation,http_method])
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:110)
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:93)
at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:1816)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1739)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1424)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
How do i override the other non-join column method association of the ActionId class in the UserAction entity?

Mapping JPA entity with composite key

I am trying to map an entity with a composite key, but I need the composite key to be the id of other entity and a String, this is my class at the moment but I believe there may be something wrong.
#Entity
public class Permission implements Serializable {
#Id
#Column
private String permission;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "role_id", foreignKey = #ForeignKey(name = "fk_permission_role_id"))
private Role role;
.....
Assuming the ID in role is a simple Integer, you might use something like:
public class PermissionPK implements Serializable {
private String permission;
private Integer role;
}
And then add the #IdClass annotation to your entity:
#IdClass(PermissionPK.class)
#Entity
public class Permission implements Serializable {
#Id
private String permission;
#Id
#ManyToOne(optional = false)
#JoinColumn(name = "role_id")
private Role role;
}
This will allow you to uses EmployeePK instances in find operations, but it isn't needed for anything else.

JPA mapping annotation error org.hibernate.MappingException: Foreign key must have same number of columns as the referenced primary key

I can't propper map DB tables with JPA annotation.
Tables Subject and Place is ManyToMany through JoinTable.
Subject.java
#Entity
#Table(name = "SUBJECT")
public class Subject implements Serializable {
#Id
#Column(name = "SID")
private Integer sid;
#Column(name = "NAME")
private String name;
// getters and setters
}
SubjectPlace.java
#Entity
#Table(name = "SUBJECT_PLACE")
public class SubjectPlace implements Serializable {
#Id
#Column(name = "SPID")
private Integer spid;
#ManyToOne
#JoinColumn(name = "SUB_KEY") //Subject FK
private Subject subject;
#ManyToOne
#JoinColumn(name = "PLC_KEY") //Place FK
private Place place;
// getters and setters
}
Place.java
#Entity
#Table(name = "PLACE")
public class Place implements Serializable {
#Id
#Column(name = "PID")
private Integer pid;
#Column(name = "NAME")
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "SUBJECT_PLACE",
joinColumns = { #JoinColumn(name = "PLC_KEY", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "SUB_KEY", nullable = false, updatable = false) })
private Set<Subject> subjects;
// getters and setters
}
But than I need to link Person with Subject in selected Places. I mean that each Place has its own collection of Subject. And a Person have link to Subject whitch resides in particular Place.
like This:
Subject (M) -- (M) Place through JoinTable Subject (1) -- (M) Subject_Place (M) -- (1) Place
Person (M) -- (M) Subject_Place through JoinTable Person (1) -- (M) Person_Subject_Place (M) -- (1) Subject_Place
Person.java
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
#Column(name = "PRSID")
private Integer prsid;
#Column(name = "NAME")
private String name;
// How to annotate this code?
// I experience problem in this part of code
#OneToMany
#JoinColumn(name="SPID_KEY")
private List<SubjectPlace> subjectPlaces;
// getters and setters
}
PersonSubjectPlace.java
#Entity
#Table(name = "PERSON_SUBJECT_PLACE")
public class PersonSubjectPlace implements Serializable {
#Id
#Column(name = "PSPID") // Person_Subject_Place ID
private Integer pspid;
#ManyToOne
#JoinColumn(name = "PER_KEY") //Person FK
private Person person;
// How to annotate this code?
// I experience problem in this part of code
#ManyToOne
#JoinColumn(name = "SPID_KEY") //Subject_Place FK
private SubjectPlace subjectPlace;
// getters and setters
}
And when I try so get Persons and its Subjects, I get this error:
Caused by: org.hibernate.MappingException: Foreign key (FK2C3B79384AABC975:PERSON_SUBJECT_PLACE [SPID_KEY])) must have same number of columns as the referenced primary key (SUBJECT_PLACE [PLC_KEY,SUB_KEY])
What, How shoul I map?
In your OneToMany mapping you don't need to specify the foreign key, you just need to use mappedBy property to refer your mapping object, you can learn more about it in OneToMany Mapping Documentation, and here's what you need to map Person and PersonSubjectPlace entities:
In your Person class:
#OneToMany(mappedBy="person")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="PRSID") //Specify the primary key of Person
private Person person;
For further information about the difference between JoinColumn and mappedBy you can take a look at this answer.
EDIT:
For the mapping between SubjectPlace and PersonSubjectPlace:
In your SubjectPlace class:
#OneToMany(mappedBy="subjectPlace")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="SPID") //Specify the primary key of SubjectPerson
private SubjectPlace subjectPlace;
Note:
The best approach to map those classes is to use #JoinTable between Person and SubjectPlace, take a look at this #JoinTable example, because PersonSubjectPlace is pratically an asociation-entity between Person and SubjectPlace.
You should remove #Joincolumn annotation and add mappedBy variable to #OneToMany annotation.
#OneToMany(mappedBy = "spid")
You should have a variable in SubjectPlace that has a Person where you should put #JoinColumn annotation

Hibernate delete row and foreign key row ManyToOne

I have the following two classes, one ReqCandAssociation can have many Comments and it is mapped like so. I need to figure out a way that when I delete a ReqCandAssociation it deletes all of its associated comments. Thanks
#Entity
#Table(name = "candidate_jobReq")
public class ReqCandAssociation implements Serializable {
#Id
private Integer candidateId;
#Id
private Integer jobId;
#Column(name = "reqStatus")
private String reqStatus;
#ManyToOne
#PrimaryKeyJoinColumn(name="candidateId", referencedColumnName="id")
private Candidate candidate;
#ManyToOne
#PrimaryKeyJoinColumn(name="jobId", referencedColumnName="id")
private JobReq jobReq;
public ReqCandAssociation(){
}
Second class
#Entity
#Table(name="comment")
public class Comment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="commentText")
private String commentText;
#Column(name="commentDate")
private Date commentDate;
#ManyToOne
#PrimaryKeyJoinColumn(name="reqCandAssociationId", referencedColumnName="id")
private ReqCandAssociation reqCandAssociation;
#ManyToOne
#PrimaryKeyJoinColumn(name="userId", referencedColumnName="id")
private User user;
Change this to the following, i'm making it bidirectional mapping.
#Entity
#Table(name = "candidate_jobReq")
public class ReqCandAssociation implements Serializable {
#Id
private Integer candidateId;
#Id
private Integer jobId;
#Column(name = "reqStatus")
private String reqStatus;
#OneToMany(cascade = { CascadeType.ALL }) //this is added here.
#JoinColumn(name ="reqCandAssociationId")
private Set<Comment> comments;
-----
Readup more on the cascade options. All cascade types are all|none|save-update|delete|all-delete-orphan|delete-orphan
The cascade all will delete all the comments associated to this class.

Categories