So, I’m seeing major differences in behavior between OpenJPA (2.1.1) and Hibernate (3.3.1) with EntityManager.merge. Consider the following class:
@Entity(name = "ChoiceHolder")
@Table(name = "CHOICEHOLDER")
@Inheritance(strategy = InheritanceType.JOINED)
public class ChoiceHolder
implements Equals, HashCode
{
protected int id;
protected String choice3;
protected String choice4;
@Id
@Column(name = "ID", scale = 0)
public int getId() {
return id;
}
public void setId(int value) {
this.id = value;
}
@Basic
@Column(name = "CHOICE3", length = 255)
public String getChoice3() {
return choice3;
}
public void setChoice3(String value) {
this.choice3 = value;
}
@Basic
@Column(name = "CHOICE4", length = 255)
public String getChoice4() {
return choice4;
}
}
And consider the following client code (this is just an SSCCE – in my actual use case, the two calls to merge with the same ID reflect received data from two separate client operations, where the id is intended to be the same between the two ops):
ChoiceHolder holder1a = new ChoiceHolder();
holder1a.setId(1);
holder1a.setChoice3("foo");
ChoiceHolder holder1b = new ChoiceHolder();
holder1b.setId(1);
holder1b.setChoice4("bar");
EntityManager em1 = factory.createEntityManager();
em1.merge(holder1a);
em1.close();
EntityManager em2 = factory.createEntityManager();
em2.merge(holder1b);
em2.close();
EntityManager em1Find = factory.createEntityManager();
ChoiceHolder holder1result = em1Find.find(ChoiceHolder.class, 1);
em1find.close();
ChoiceHolder holder2a = new ChoiceHolder();
holder2a.setId(2);
holder2a.setChoice3("foo2");
ChoiceHolder holder2b = new ChoiceHolder();
holder2b.setId(2);
holder2b.setChoice4("bar2");
EntityManager em3 = factory.createEntityManager();
em3.merge(holder2a);
em3.merge(holder2b);
em3.close();
EntityManager em2Find = factory.createEntityManager();
ChoiceHolder holder2result = em2Find.find(ChoiceHolder.class, 2);
em2Find.close();
When I run the above code using Hibernate, holder1result has null for choice3, and “bar” for choice4. Likewise, holder2result has null for choice3, and “bar2” for choice4. However, when I run the same code using openJPA, I get something very different: holder1result has both choice3 and choice4 set, and the call to em3.merge(holder2b) blows up with a ReportingSQLException, because the INSERT it tries to do violates the primary key constraint on choiceholder’s ID.
If it matters, here are the relevant bits from the properties file I’m using to configure Hibernate:
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.hbm2ddl.auto=create-drop
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
hibernate.jdbc.batch_size=0
And here’s the corresponding bit from my persistence.xml that is being used to configure openJPA:
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema"/>
<property name="openjpa.jdbc.DBDictionary" value="postgres"/>
<property name="openjpa.ConnectionDriverName" value="org.postgresql.Driver"/>
<property name="openjpa.jdbc.MappingDefaults" value="DeferConstraints=true,ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/>
In my particular case, what I want is the behavior I’m seeing with Hibernate; however, I’m required by my environment (inside Karaf) to use OpenJPA instead. Are there flags I can set on OpenJPA to alter its behavior to match what I’m looking for?
Is this a horrific abuse of JPA, and if so, is there some other technique/sequence of calls I should be using?
Thanks in advance!
Well, I came up with some partial answers – but I’m not sure if there are any side effects I need to be concerned about.
For problem #1, where
choice3was not getting nulled out correctly, I had to setopenjpa.DetachStatetoallin my persistence.xml file.For problem #2, adding a call to
EntityManager.flushin between the calls tomergefixes the problem. Oddly, adding the call before the first call tomergealso fixes the problem, which leads me to suspect thatflushis setting some state on the EntityManager, but I’m not sure why that would be.