can someone please explain me @MapsId in hibernate?
Here is a nice explanation from Object DB.
Designates a ManyToOne or OneToOne relationship attribute that provides the mapping for an EmbeddedId primary key, an attribute within an EmbeddedId primary key, or a simple primary key of the parent entity. The value element specifies the attribute within a composite key to which the relationship attribute corresponds. If the entity's primary key is of the same Java type as the primary key of the entity referenced by the relationship, the value attribute is not specified.
// parent entity has simple primary key
@Entity
public class Employee {
@Id long empId;
String name;
...
}
// dependent entity uses EmbeddedId for composite key
@Embeddable
public class DependentId {
String name;
long empid; // corresponds to primary key type of Employee
}
@Entity
public class Dependent {
@EmbeddedId DependentId id;
...
@MapsId("empid") // maps the empid attribute of embedded id
@ManyToOne Employee emp;
}
Read the API Docs here.
Purpose of @MapsId annotation in Hibernate
@MapsId will share main entity's primary key as the child entity's primary key no need to insert foreign key in child entity. Hibernate will map both entity's PK and return us a single object.
Hibernate @MapsId why getting this errors?
I finally found the answer to all the problems.
To understand the root of the problem we must remind how saveOrUpdate(object)
works.
1) If object
has ID set, saveOrUpdate
will Update
else it will Save
.
2) If hibernate decides it is a Save
but object is already on DB, you want to update, the Duplicate entry '123456' for key 'PRIMARY'
exception will occur.
3) If hibernate decides it is an Update
but object is not in DB, you want to save, the StaleStateException
Exception occurs.
The problem relies on the fact that if aInstance
exists in DB and already has an ID, @MapsId
will give that ID to B
ignoring the rules above, making Hibernate think B
also exists in DB when it may not. It only works properly when both A
and B
dont exist in DB or when they both exist.
Therefor the workaround solution is to make sure you Set
the ID only and only if each object exists in DB, and set ID to null when it does not:
B dbB = (B) unmarshaller.getDetachedSession().createCriteria(B.class).add(Restrictions.idEq(aInstance.getId())).uniqueResult();
if (dbB != null) //exists in DB
{
aInstance.getB().setId(aInstance.getId()); //Tell hibernate it is an Update
//Do the same for any other child classes to B with the same strategy if there are any in here
}
else
{
aInstance.getB().setId(null); //Tell hibernate it is a Save
}
unmarshaller.getDetachedSession().clear();
(using detached session, so that main session stays clear of unwanted objects, avoiding the "object with the same identifier in session
" exception)
If you dont need the DB object, and only want to know if it exists or not in the DB, you can use a Count, making it much lighter:
String query = "select count(*) from " + B.class.getName() + " where id = " + aInstance.getId();
Long count = DataAccessUtils.uniqueResult(hibernateTemplate.find(query));
if (count != null && count > 0)
{
aInstance.getB().setId(aInstance.getId()); // update
}
else
{
aInstance.getB().setId(null); // save
}
Now you can saveOrUpdate(aInstance);
But like i said, @MapsId
strategy is not very Hibernate friendly.
Auto generated id not cascading on EmbeddedId in child entity in Hibernate
I'm not quite sure why your configuration does not work. I have a similar configuration in one of my projects that works just fine. I was able to find a solution however, by adding a @MapsId annotation to the ManyToOne in the Task class. (see can someone please explain me @MapsId in hibernate? for an explanation about MapsId) I also removed insertable=false and updatable=false. See below for the code.
I didn't get MapsId to work with the StoryId class, so i changed the type of TaskPKID.storyId from StoryId to long. The StoryId class doesn't seem to add much, so hopefully this isn't to much of a problem. If you find a solution please let me know in the comments though!
By the way, your code has a lot of problems. There's a bunch of typo's, and there is a OneToMany mapping on a property that is not a Collection (which isn't allowed) This made it more difficult for me to debug the problem. Please make sure to post better quality code in your questions next time.
Here is the Task class the way I implemented it:
@Entity
@Table(name = "TASK_XREF")
class Task {
@EmbeddedId
TaskPKId taskPKId;
@Column(name = "TASK_NAME")
String name;
@MapsId("storyId")
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "STORY_ID")
Story story;
//getters, setters
}
And here is the TaskPKID class:
@Embeddable
class TaskPKId implements Serializable {
long taskId;
long taskTypeId;
@Column(name="STORY_ID")
long storyId;
public long getTaskId() {
return taskId;
}
public void setTaskId(long taskId) {
this.taskId = taskId;
}
public void setTaskTypeId(long taskTypeId) {
this.taskTypeId = taskTypeId;
}
}
Hibernate exception with @MapsId, @EmbeddedId
Solution Here: Got the same problem, and got it working now.
For it to work, the EmployeeId
of your Employee class should be instantiated either in the constructor of Employee, or in the SessionBean before making the persistence.
Otherwise, it tries to populates the values of your embeddedid with it being null.
Not sure if this is normal or if it's a bad implementation of JPA specifications. I think the latter since the constructor for your PK is defined and hibernate should be able to call it by itself.
Related Topics
Jbutton() Only Working When Mouse Hovers
How to Set Color to a Certain Row If Certain Conditions Are Met Using Java
Adding Jradiobutton into Jtable
Spark Spark-Submit --Jars Arguments Wants Comma List, How to Declare a Directory of Jars
Getting Xml Node Text Value with Java Dom
Maximum Size of a Method in Java 7 and 8
Hibernate Bidirectional @Manytoone, Updating the Not Owning Side Not Working
How to Enable Wire Logging for a Java Httpurlconnection Traffic
Java 8 Chained Method Reference
How to Get the Array Class for a Given Class in Java
Cannot Rerun Java JPAckage Installer If Already Installed, Second Time Just Exits Without Warning
String Parsing in Java with Delimiter Tab "\T" Using Split
Is There a Priorityqueue Implementation with Fixed Capacity and Custom Comparator
Javac Error: Class Names Are Only Accepted If Annotation Processing Is Explicitly Requested