Can Someone Please Explain Me @Mapsid in Hibernate

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



Leave a reply



Submit