@OneToOne + Table-per-Concrete-Class = exception?
I'm new to Hibernate, and I can't get @OneToOne
to function in our code.
Suppose 3 classes: 1 abstract (Class_A) and 2 inheriting from it (Class_B / Class_C). Class_C has a unidirectional pointer to Class_B.
(I've prepared a diagram but the site wont let me post it :-/).Notes:
- Pure Java + Hibernate 3.6.0 Final + Oracle 11g.
- Inheritance strategy = Table per Concrete Class.
- Developed with
hibernate.hbm2ddl.auto=update
. - In our code Class_B needs its own table, thus no
@Embeddable
. - In our code Class_C is also abstract, not as presented here in the simplified example.
Code
Class_A
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Class_A {
@Id
开发者_运维问答 public long myId = 0;
}
Class_B
@Entity
@Table(name = "Class_B")
public class Class_B extends Class_A {
private String myString = "Hellos - I'm Class_B!";
}
Class_C
@Entity
@Table(name = "Class_C")
public class Class_C extends Class_A {
private String myString = "Hellos - I'm Class_C!";
@OneToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@NotNull
private Class_B classB;
public void setClassB(Class_B classB) {
this.classB = classB;
}
}
Hibernate Code
StatelessSession statelessSession = sessionFactory.openStatelessSession();
Class_C classC = new Class_C();
classC.myId = 92;
Class_B classB = new Class_B();
classB.myId = 8000;
classC.setClassB(classB);
statelessSession.beginTransaction();
statelessSession.insert(classC);
statelessSession.getTransaction().commit();
statelessSession.close();
Problems
At
Here's the SQL:insert(classC)
Hibernate only issues an SQL to insert Class_C. There is no SQL to insert Class_B. I see Class_C's details in Oracle, but Class_B's table is empty.Hibernate: insert into Class_C (classB_myId, myString, myId) values (?, ?, ?)
At
getTransaction().commit()
it explodes with
this:
java.sql.BatchUpdateException: ORA-02291: integrity constraint (NDP.FK9619CF1CAD47EF0F) violated - parent key not found
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:17660)
at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:771)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
at org.hibernate.impl.StatelessSessionImpl.managedFlush(StatelessSessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
...
Questions Please
- Why doesn't this work... what am I doing wrong?
- In our legacy code, the application allocates unique id numbers and have no intention of using generated Ids. Thus
@GenerateValue
for our@Id
isn't considered. Is this a reason why this fails? - Whats the difference between
@OneToOne(cascade = CascadeType.ALL)
vs.@OneToOne
+@Cascade({CascadeType.ALL})
?
MUCH thanks!
- Ten_of_a_Kind
I guess the cause is
StatelessSession statelessSession = sessionFactory.openStatelessSession();
Try to use a normal Session
instead:
Session session = sessionFactory.openSession();
StatelessSession
is a special purpose tool that should be used only in special circumstances. For regular operations you should always use Session
. From Hibenrate docs:
Alternatively, Hibernate provides a command-oriented API that can be used for streaming data to and from the database in the form of detached objects. A StatelessSession has no persistence context associated with it and does not provide many of the higher-level life cycle semantics. In particular, a stateless session does not implement a first-level cache nor interact with any second-level or query cache. It does not implement transactional write-behind or automatic dirty checking. Operations performed using a stateless session never cascade to associated instances.
Please check Hibernate docs. As stated there:
Operations performed using a stateless session never cascade to associated instances. http://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/batch.html#batch-statelesssession
With org.hibernate.StatelessSession, you have deal with object inter-dependencies when inserting objects. That's not the case when you are using a org.hibernate.Session.
In this case, you must persist classB object before classC object to make it work. Persisting order do counts. If you change the persistence order you will have org.hibernate.exception.ConstraintViolationException. Choose carefully when to use stateless sessions, as it has no persistence context.
statelessSession.beginTransaction();
statelessSession.insert(classB); // <- Persisting classB
statelessSession.insert(classC);
statelessSession.getTransaction().commit();
statelessSession.close();
Tested with Hibernate 3.6.0 Final + MySQL 5.0.51a-24
精彩评论