1

I have a table named Employee which has a complex primary key i.e. a combination of 3 of its column

firstName : String
SecondName : String
bossId : foreingKey of other table named Boss ( auto generated database sequence)

And here is my code:

@Entity
@Table(name = "Employee")
@org.hibernate.annotations.Entity(optimisticLock = OptimisticLockType.ALL, dynamicUpdate = true)
public class Employee {

private EmployeePk employeePk;  

private int age;
private String status;


@EmbeddedId
public EmployeePk getEmployeePk() {
    return employeePk;
}
public void setEmployeePk(EmployeePk employeePk) {
    this.employeePk = employeePk;
}


@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null ||
            !(o instanceof Employee)) {

        return false;
    }

    Employee other
    = (Employee)o;

    // if the id is missing, return false
    if (getEmployeePk() == null) return false;


    if( getEmployeePk().equals(other.getEmployeePk())) {            
        return true;
    } else {
        return false;
    }

}

@Override
public int hashCode() {
    if (getEmployeePk() != null) {
        return getEmployeePk().hashCode();
    } else {
        return super.hashCode();
    }
}

}

@Embeddable
public class EmployeePk implements Serializable{

private static final long serialVersionUID = -7827123517392541880L;
private String firstName;
private String secondName;
private Boss boss;

@ManyToOne
@JoinColumn(name = "boss_id",insertable= false, updatable= false)
public Boss getBoss() {
    return boss;
}
public void setBoss(
        Boss boss) {
    this.boss = boss;
}

/* setters and getters of all with @column annotation */

@Override  
public boolean equals(Object obj) {  
    if (this == obj) {  
        return true;  
    }  
    if (!(obj instanceof EmployeePk)) {  
        return false;  
    }  

    EmployeePk other = (EmployeePk) obj;  

    if( getfirstname() != null && 
            getFirstName().equals(other.getFirstName()) &&
            getSecondName() !=null && 
            getSecondName().equals(other.getSecondName()) &&
            getBoss() != null &&
            getBoss().getId() == other.getBoss().getId())
        return true;

    return false;  
}  

@Override
public int hashCode() {
    if (getFirstName() != null &&
            getSecondName() != null && 
            getBoss() != null) {
        return getFirstName().hashCode() + 
                getSecondName().hashCode();
    } else {
        return super.hashCode();
    }
}

}

Now , everything is running fine and I am able to create/update/delete the database rows in Employee table.

But when i am trying to update same rows in one single transaction I am getting this exception :

org.hibernate.event.def.AbstractFlushingEventLis
tener: Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect):
    at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:64)
    at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:996)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1141)
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:835)

I had similar issue with another table named Contractor but was able to solve this issue by overriding its equals and hashCode methods.

The thing is that in that table there was only one primary key named "id" which was a database auto generated sequence and hence there was no concept of EmbeddedId there.

I am not sure where I am going wrong here. I have spent several days in fixing this issue and followed several links for example: http://onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=1

Ryan Kohn
  • 13,079
  • 14
  • 56
  • 81
Scorpion
  • 633
  • 3
  • 11
  • 24

1 Answers1

0

A couple of thoughts:

1) Using optimisticLock = OptimisticLockType.ALL makes me suspicious. I've never used this myself, but I suspect it might have something to do with your error.

2) I don't think the equals method handles the case where any of the attributes are null. It will always return false, even if both instances are identical.

3) Hibernate really prefers when you use dedicated id (and version if required) columns rather than natural keys. Support for composite/natural keys seems to be cumbersome and buggy in my experience.

James Scriven
  • 7,784
  • 1
  • 32
  • 36
  • 1) so what should be done with optimisticLockType.ALL? shall i change it to something else? – Scorpion Aug 28 '12 at 13:01
  • 1) so what should be done with optimisticLockType.ALL? shall i change it to something else? 2) which equals you are talking about here. the null check is done first and then equals check is done. if equals returns false in ever y case then it should create entries but its not happening that way. I think the equals and hashcode methods are not getting overridden not sure why though 3) Is there any way that I can have a virtual id in the class. In database there is no such auto generated Id though and primary key is combination of 3 columns – Scorpion Aug 28 '12 at 13:23
  • 1) You should post the transaction code, so we can understand what you are trying to do. 2) I'm talking about the equals/hashcode on the pk object. I'm not surprised hashcode/equals are not being called. See http://stackoverflow.com/questions/1638723/equals-and-hashcode-in-hibernate, for example. 3) No, the best solution is to add an id/version column in the database. – James Scriven Aug 28 '12 at 16:00