I'm getting unexpected updates that occur whenever I end of flush a transaction. The updates occur on classes unrelated to the query. The classes that are getting updated all have a userDefined type (called ACLUserType). If I remove any references to the userType the unexpected updates go away and everything works as expected. This is a problem because the updates are locking out other users from accessing those tables.
Hibernate version: 3.0.5
Mapping documents:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="gov.llnl.e3.model.E3User" table="E3_USER">
<id name="id" column="ID" type="long">
<generator class="native"/>
</id>
<property name="userId" unique="true" not-null="true" column="USER_ID" />
<property name="lastName" column="LAST_NAME" />
<property name="firstName" column="FIRST_NAME" />
<property name="emailAddress" column="EMAIL_ADDRESS" />
<property name="phoneNumber" column="PHONE_NUMBER" />
<many-to-one name="defaultGroup" column="DEFAULT_GROUP" not-null="true"/>
<property name="defaultACL" type="gov.llnl.e3.model.ACLUserType" not-null="true">
<column name="DEFAULT_ACL" sql-type="acl"/>
</property>
<set name="groups" table="USER_GROUP_SET" lazy="true" cascade="all">
<key column="USER_ID"/>
<many-to-many class="gov.llnl.e3.model.E3Group" column="GROUP_ID"/>
</set>
<set name="views" table="USER_VIEW_SET" lazy="true" cascade="all">
<key column="USER_ID"/>
<many-to-many class="gov.llnl.e3.model.E3View" column="VIEW_ID"/>
</set>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close(): I'm following session-per-request model. Here is how I start and end transactions:
Code:
public void startTransaction() {
start = System.currentTimeMillis();
if (session == null) {
throw new E3InitializationException(
"Attempt to start a transaction without initializing the session first!");
}
try {
transaction = session.beginTransaction();
logger.info("transaction started id: " + transaction.hashCode());
} catch (HibernateException e) {
logger.error("Exception while starting transaction: ", e);
throw (E3DatabaseAccessException) new E3DatabaseAccessException().initCause(e);
}
}
public void endTransaction(boolean trnxError) {
// make sure we're initialized...
if (session == null) {
throw new E3InitializationException("Attempt to end a transaction without initializing the session first!");
}
// ... and that we're in a transaction.
if (transaction == null) {
throw new E3InitializationException("Attempt to end a transaction before starting a transaction");
}
try {
if (trnxError) {
transaction.rollback();
} else {
transaction.commit();
session.flush();
logger.info("flushing transaction id: " + transaction.hashCode());
}
} catch (HibernateException e) {
logger.error("Exception while ending transaction: ", e);
// if commit failed, then try to rollback transaction
try {
transaction.rollback();
} catch (HibernateException e1) {
logger.error("Exception while trying to rollback transaction: ", e1);
throw new E3DatabaseAccessException("Error while attempting to rollback transaction.", e1);
}
throw new E3DatabaseAccessException("Error while attempting to end transaction with Hibernate.", e);
} finally {
// mark that we're not in a transaction anymore
if (transaction.isActive())
logger.info("!!!! Transaction still active");
transaction = null;
stop = System.currentTimeMillis() - start;
logger.info("***** End Transaction. Time: " + stop + "\n");
}
}
Name and version of the database you are using:E3DB - Oracle 10g
The generated SQL (show_sql=true):Here's the logging output and generated SQL that is produced from a simple query of the E3User table (notice the UPDATEs to E3User). The first update occurs as a result of transaction.commit(); in the endTransaction method above. The second update occurs as a result of the next statement (session.flush()):
Code:
transaction started id: 27104945
4172 [AWT-EventQueue-0] INFO (E3Manager.java:269) - transaction started id: 27104945
Hibernate: select e3user0_.ID as ID, e3user0_.USER_ID as USER2_17_, e3user0_.LAST_NAME as LAST3_17_, e3user0_.FIRST_NAME as FIRST4_17_, e3user0_.EMAIL_ADDRESS as EMAIL5_17_, e3user0_.PHONE_NUMBER as PHONE6_17_, e3user0_.DEFAULT_GROUP as DEFAULT7_17_, e3user0_.DEFAULT_ACL as DEFAULT8_17_ from E3_USER e3user0_ where e3user0_.USER_ID=?
[color=red]]Hibernate: update E3_USER set USER_ID=?, LAST_NAME=?, FIRST_NAME=?, EMAIL_ADDRESS=?, PHONE_NUMBER=?, DEFAULT_GROUP=?, DEFAULT_ACL=? where ID=?
Hibernate: update E3_USER set USER_ID=?, LAST_NAME=?, FIRST_NAME=?, EMAIL_ADDRESS=?, PHONE_NUMBER=?, DEFAULT_GROUP=?, DEFAULT_ACL=? where ID=?[/color]
flushing transaction id: 27104945
4781 [AWT-EventQueue-0] INFO (E3Manager.java:311) - flushing transaction id: 27104945
***** End Transaction. Time: 609
Here's the logging output and SQL from a second query to an unrelated table - BENUm (notice the same UPDATEs to E3User)::
Code:
transaction started id: 18690717
4828 [main] INFO (E3Manager.java:269) - transaction started id: 18690717
Hibernate: select benum0_.ID as ID, benum0_.BE_PREFIX as BE2_8_, benum0_.BE_SUFFIX as BE3_8_, benum0_.TARGET_NAME as TARGET4_8_, benum0_.CENTROID as CENTROID8_, benum0_.DEFAULT_IMAGE_ID as DEFAULT6_8_, benum0_.COUNTRY_CODE_ID as COUNTRY7_8_ from BE_NUM benum0_ inner join ABSTRACT_E3_ELEMENT benum0_1_ on benum0_.ID=benum0_1_.ID
Hibernate: update E3_USER set USER_ID=?, LAST_NAME=?, FIRST_NAME=?, EMAIL_ADDRESS=?, PHONE_NUMBER=?, DEFAULT_GROUP=?, DEFAULT_ACL=? where ID=?
Hibernate: update E3_USER set USER_ID=?, LAST_NAME=?, FIRST_NAME=?, EMAIL_ADDRESS=?, PHONE_NUMBER=?, DEFAULT_GROUP=?, DEFAULT_ACL=? where ID=?
flushing transaction id: 18690717
5063 [main] INFO (E3Manager.java:311) - flushing transaction id: 18690717
***** End Transaction. Time: 235
When I remove the ACLUserType from the E3User table the above updates don't occur.
Here's the definition of my ACLUserType:Code:
package gov.llnl.e3.model;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import oracle.jdbc.driver.OracleConnection;
import oracle.spatial.geometry.JGeometry;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import org.hibernate.HibernateException;
import org.hibernate.type.LongType;
import org.hibernate.usertype.UserType;
public class ACLUserType implements UserType {
private static final int[] SQL_TYPES = {Types.STRUCT};
private static final String DB_OBJECT_TYPE = "E3.ACL";
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class returnedClass() {
return STRUCT.class;
}
public int hashCode(Object o) throws HibernateException {
return ((STRUCT)o).hashCode();
}
public boolean equals(Object o1, Object o2) throws HibernateException {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
return false;
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
throws HibernateException, SQLException {
assert names.length == 1;
final STRUCT struct = (STRUCT) resultSet.getObject(names[0]);
if (resultSet.wasNull()) {
return null;
}
final ACL acl = new ACL();
acl.setUserOwnerId(new Long(((BigDecimal) struct.getAttributes()[0]).longValue()));
acl.setGroupOwnerId(new Long(((BigDecimal) struct.getAttributes()[1]).longValue()));
if (struct.getAttributes()[2] != null)
acl.setOwnerRead(new Integer(((BigDecimal) struct.getAttributes()[2]).intValue()));
if (struct.getAttributes()[3] != null)
acl.setGroupRead(new Integer(((BigDecimal) struct.getAttributes()[3]).intValue()));
if (struct.getAttributes()[4] != null)
acl.setWorldRead(new Integer(((BigDecimal) struct.getAttributes()[4]).intValue()));
if (struct.getAttributes()[5] != null)
acl.setOwnerWrite(new Integer(((BigDecimal) struct.getAttributes()[5]).intValue()));
if (struct.getAttributes()[6] != null)
acl.setGroupWrite(new Integer(((BigDecimal) struct.getAttributes()[6]).intValue()));
if (struct.getAttributes()[7] != null)
acl.setWorldWrite(new Integer(((BigDecimal) struct.getAttributes()[7]).intValue()));
if (struct.getAttributes()[8] != null)
acl.setOwnerDelete(new Integer(((BigDecimal) struct.getAttributes()[8]).intValue()));
if (struct.getAttributes()[9] != null)
acl.setGroupDelete(new Integer(((BigDecimal) struct.getAttributes()[9]).intValue()));
if (struct.getAttributes()[10] != null)
acl.setWorldDelete(new Integer(((BigDecimal) struct.getAttributes()[10]).intValue()));
return acl;
}
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
preparedStatement.setNull(index, Types.STRUCT, DB_OBJECT_TYPE);
} else {
final ACL acl = (ACL) value;
final Object[] values = new Object[] {
acl.getUserOwnerId(),
acl.getGroupOwnerId(),
acl.getOwnerRead(),
acl.getGroupRead(),
acl.getWorldRead(),
acl.getOwnerWrite(),
acl.getGroupWrite(),
acl.getWorldWrite(),
acl.getOwnerDelete(),
acl.getGroupDelete(),
acl.getWorldDelete() };
OracleConnection oc = (OracleConnection)preparedStatement.getConnection();
final STRUCT struct = new STRUCT(StructDescriptor.createDescriptor(DB_OBJECT_TYPE, oc), oc, values);
preparedStatement.setObject(index, struct);
}
}
public Object deepCopy(Object o) throws HibernateException {
if (o == null)
return null;
return ((ACL) o).clone();
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object o) throws HibernateException {
return (Serializable) deepCopy(o);
}
public Object assemble(Serializable serializable, Object o) throws HibernateException {
return deepCopy(serializable);
}
public Object replace(Object o, Object o1, Object o2) throws HibernateException {
return (STRUCT) o;
}
}