Software Development
Using the unsaved-value attribute to prevent TransientObjectExceptions in Hibernate Age Mooij 09 Aug, 2006
select children0_.PARENT_ID as PARENT2_1_, children0_.ID as ID1_, children0_.ID as ID1_0_, children0_.PARENT_ID as PARENT2_1_0_ from CHILD children0_ where children0_.PARENT_ID=?Partition strategy There are multiple partitioning strategies for Oracle, the two most frequently used are RANGE partitioning and HASH partitioning. See https://download-west.oracle.com/docs/cd/B10501_01/server.920/a96521/partiti.htm for more on partitioning in Oracle 9. Together with the DBA we chose the RANGE partitioning strategy for several reasons:
select … from B b0_ where b0_.A_ID=?The column B.A_ID has a foreign key restriction on the column A.ID. To benefit from partitioning we need the query to look like:
select … from B b0_ where b0_.A_ID=? and b0_.PARTITION_VALUE = ?To enforce this, we choose to add the column A.PARTITION_VALUE to the Hibernate Id of the entity mapped to table A. Hibernate will now use both columns to identify the associated rows in related tables, resulting in queries like the one above. We now need a composite-id that consists of a sequence value and the PARTITION_VALUE column. For some reason Hibernate does not support a generator for generating composite-id’s at save time, forcing the application to set the id value of each entity before saving it to the Hibernate session (this is similar to the “assigned” generator strategy for single valued ids). We do not want to reinvent the wheel so we reused Hibernate’s SequenceHiLoGenerator (which reduces the number of sequence.nextval calls to Oracle). Next we created a Spring FactoryBean that uses the SequenceHiLoGenerator to generate the sequence values. In the DAO that is used to save the Parent the id property is set with the new sequence value retrieved from the SequenceHiLoGenerator. The Spring bean which creates an instance of the SequenceHiLoGenerator looks as follows:
public class SequenceFactory extends AbstractFactoryBean { private String dialectName; private Properties properties; protected Dialect dialect; public Class getObjectType() { return SequenceHiLoGenerator.class; } public void afterPropertiesSet() throws Exception { try { getDialectFromName(); } catch (ClassCastException e) { throw new BeanInitializationException("The given class '" + dialectName + "' does not extend 'org.hibernate.dialect.Dialect'"); } catch (Exception e) { throw new BeanInitializationException("The given class '" + dialectName + "' does not exist or cannot be instantiated",e); } super.afterPropertiesSet(); } protected Object createInstance() throws Exception { SequenceHiLoGenerator generator = createNewSequenceHiLoGenerator(); generator.configure(Hibernate.LONG, properties, getDialectFromName()); return generator; } private Dialect getDialectFromName() throws InstantiationException, IllegalAccessException, ClassNotFoundException { if (dialect == null) { dialect = (Dialect) Class.forName(dialectName).newInstance(); } return dialect; } /** * created for test purposes. */ protected SequenceHiLoGenerator createNewSequenceHiLoGenerator() { return new SequenceHiLoGenerator(); } public void setDialectName(String dialectName) { this.dialectName = dialectName; } public void setProperties(Properties properties) { this.properties = properties; } }This bean is wired into the DAO as follows:
999 UKC_PARENT_SEQAs the SequenceFactory extends the AbstractFactoryBean Spring will use the method createInstance to create instances of the SequenceHiLoGenerator. The instance of the SequenceHiLoGenerator is then injected into the dao and is used in the save method to generate the new sequence value. The Hiberante mapping now looks like this:
999 CHILD_SEQAs you can see there is a class ParentId that holds the composite-id of Parent. The Child class now refers back to the Parent using the composite-id columns. If we look to the sql that Hibernate generates it takes the PARTITION_VALUE column in the where clause:
select children0_.PARENT_ID as PARENT2_1_, children0_.PARTITION_VALUE as PARTITION3_1_, children0_.ID as ID1_, children0_.ID as ID1_0_, children0_.PARENT_ID as PARENT2_1_0_, children0_.PARTITION_VALUE as PARTITION3_1_0_ from CHILD children0_ where children0_.PARENT_ID=? and children0_.PARTITION_VALUE=?Here you have it! Partitioning in Oracle with Hibernate. Bug in Hibernate There is one more thing we want to discuss. In our application in one case we used HQL to retrieve some entities. We found that using HQL queries a restriction on a composite-id does not work. Given the previous Parent Child example, executing the following HQL: "from Child c where c.parent = :parent", Hibernate produces this SQL:
select child0_.ID as ID1_, child0_.PARENT_ID as PARENT2_1_, child0_.PARTITION_VALUE as PARTITION3_1_ from CHILD child0_ where ( child0_.PARENT_ID, child0_.PARTITION_VALUE )=?This obviously does not work. I wonder if they say this is a feature .. :-) Maarten Winkels Lars Vonk