Hi!
When a property of an element in a list is set to not-null and you try to insert a null, then you will get a SQL exception from the JDBC driver. The nullability is not checked before trying to persist the value. This will also cause the parent to be not stored in the DB while Hibernate believes it is, if you rollback your transaction based on the exception.
When issuing a flush() or saveOrUpdate() after correcting the invalid value, Hibernate issues an update instead of an insert for the parent, causing another SQL exception.
Mapping:Code:
<class name="test.data.A" table="tblA">
<id name="id" column="id" type="int">
<generator class="native"/>
</id>
<property name="value" column="value" type="string"/>
<list name="listOfB" table="BofA">
<key column="id"/>
<index column="idx"/>
<composite-element class="test.data.B">
<property name="value" type="string" not-null="true"/>
</composite-element>
</list>
</class>
Java Code:Code:
System.out.println("*** Creating A with invalid B as child ***");
A a = new A(new LinkedList());
a.setValue("Test");
B b = new B(null); // this B violates not-null
a.getListOfB().add(b); // this B violates not-null
Session s = sessionFactory.openSession();
Transaction t = s.beginTransaction();
try {
System.out.println("*** calling saveOrUpdate ***");
s.saveOrUpdate(a);
t.commit();
} catch (Exception e) {
e.printStackTrace();
t.rollback();
}
System.out.println("*** Changing A / Updating B to valid ***");
a.setValue("changed");
b.setValue("valid now"); // invalid, causes not-null violation
try {
System.out.println("*** Flushing ***");
s.flush();
t.commit();
} catch (Exception e) {
e.printStackTrace();
t.rollback();
}
Output:Code:
*** Creating A with invalid B as child ***
*** calling saveOrUpdate ***
Hibernate: insert into tblA (value, id) values (?, null)
Hibernate: call identity()
Hibernate: insert into BofA (id, idx, value) values (?, ?, ?)
2005-01-20 14:45:15,687 [main] WARN net.sf.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: null
2005-01-20 14:45:15,687 [main] ERROR net.sf.hibernate.util.JDBCExceptionReporter - failed batch
2005-01-20 14:45:15,687 [main] WARN net.sf.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: null
2005-01-20 14:45:15,687 [main] ERROR net.sf.hibernate.util.JDBCExceptionReporter - failed batch
2005-01-20 14:45:15,687 [main] ERROR net.sf.hibernate.impl.SessionImpl - Could not synchronize database state with session
net.sf.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at net.sf.hibernate.exception.ErrorCodeConverter.handledNonSpecificException(ErrorCodeConverter.java:90)
at net.sf.hibernate.exception.ErrorCodeConverter.convert(ErrorCodeConverter.java:79)
at net.sf.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:30)
at net.sf.hibernate.impl.BatcherImpl.convert(BatcherImpl.java:325)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:134)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2441)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2395)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2260)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at test.Tester.main(Tester.java:65)
Caused by: java.sql.BatchUpdateException: failed batch
at org.hsqldb.jdbc.jdbcStatement.executeBatch(Unknown Source)
at org.hsqldb.jdbc.jdbcPreparedStatement.executeBatch(Unknown Source)
at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:54)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:127)
... 5 more
*** Changing A / Updating B to valid ***
*** Flushing ***
Hibernate: update tblA set value=? where id=?
2005-01-20 14:45:15,703 [main] ERROR net.sf.hibernate.impl.SessionImpl - Could not synchronize database state with session
net.sf.hibernate.HibernateException: Batch update row count wrong: 0
at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:65)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:127)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2441)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2392)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2260)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at test.Tester.main(Tester.java:78)
2005-01-20 14:45:15,718 [Secondary finalizer] WARN net.sf.hibernate.impl.SessionImpl - unclosed connection, forgot to call close() on your session?
2005-01-20 14:45:15,906 [Secondary finalizer] INFO net.sf.hibernate.connection.DriverManagerConnectionProvider - cleaning up connection pool: jdbc:hsqldb:file:testdb
Hibernate version: 2.1.7c
Name and version of the database you are using:
hsqldb_1_7_2_4, reproducible also with MS SQL Server 2000
Is this a bug, or am I mistreating Hibernate by using flush() in the above example. The same output is produced for saveOrUpdate() as second call, too, however. Or is it not allowed to roll back the transaction after the first exception?
Thanks in advance,
Martin