The main focus in Hibernate 3.2 is JPA support.
You may want to browse the Hibernate Core Migration Guide : 3.1 first.
Changes in this migration guide are classified into:
- API Changes (affecting application Java code)
- metadata changes (affecting the XML O/R mapping metadata)
- query language changes (affecting HQL queries)
- config changes
- native-sql changes
- behavior changes
API changes
BytecodeProvider extension
3.2 adds the ability to plug in various libraries for Hibernate's bytecode processing requirements. Traditionally, this role was served exclusively by CGLIB. This functionality has now moved behind a pluggable set of interfaces, with two default implementations out-of-the-box: CGLIB and Javassist.
Metadata changes
N/A
Query language changes
Implicit join determinism
Consider a mapping such as
<class name='Currency' lazy='false'> .... </class> <class name='Asset'> <id name='id' ... </id> <many-to-one name='currency' class='Currency' fetch='select'/> </class>
and an HQL query like
select a.id, a.currency from Asset a
Since Hibernate 3.2.2 this query generates an inner join on Asset with Currency which means that Assets with a NULL currency are not returned by the query anymore. This makes implicit joins more deterministic. To get Assets with a NULL currency just use
select a.id, c from Asset a left join a.currency as c
Path expressions joining across a collection throw exception
Implicit joins "across" a collection has never been a supported syntax. It happened to sorta kinda work in some previous releases. For example the following query is completely illegal and has never really been supported:
select f.bars.baz from Foo f
Now throws an exception. Despite the always present warning in the documentation that this is an illegal path expression if bars is a collection, users found this to be working in some cases with Hibernate 3.1. This is no longer the case, an error is thrown. As always, the correct syntax is an explicit join:
select b.baz from Foo f join f.bars b
Changed aggregation (count, sum, avg) function return types
In alignment with the JPA specification the count, sum and avg function now defaults to return types as specified by the specification. This can result in
ClassCastException's at runtime if you used aggregation in HQL queries. The new type rules are described at
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1538 If you cannot change to the JPA compliant type handling the following code can be used to provide "classic" Hibernate behavior for HQL aggregation:
Configuration classicCfg = new Configuration(); classicCfg.addSqlFunction( "count", new ClassicCountFunction()); classicCfg.addSqlFunction( "avg", new ClassicAvgFunction()); classicCfg.addSqlFunction( "sum", new ClassicSumFunction()); SessionFactory classicSf = classicCfg.buildSessionFactory();
Note that this Configuration.addSqlFunction() approach also allows users to add their own custom function definitions or override other standard functions without subclassing Dialect
Improved parameter type guessing
Previous versions guessed the type of a particular parameter (when the generic Query.setParameter() method is used) based on the incoming parameter value being bound. 3.2 implements a different guessing strategy based on the structure of the analyzed query; for example, given a query like from Animal a where a.pregnant = ? we can tell from the structural analysis that we should expect the type of the parameter to be the same as the underlying type of the Animal.pregnant property.
Improved boolean literal and parameter handling
Earlier versions introduced the notion of boolean literals as an actual HQL construct, and allowed users to do
List pregs = session.createQuery( "from Animal where pregnant = true" ).list();
However, these were always resolved to SQL literal values based on the dialect and were thus always non-contextually resolved (using Dialect.toBooleanValueString() to be precise). Using the same underlying infrastructure utilized to achieve the changes described in Improved parameter type guessing, these boolean literals are now resolved contextually. In the above query, the TRUE literal would be resolved to a SQL literal value using the type associated with the Animal.pregnant property. Regarding boolean parameters, we are talking about how Hibernate interprets Query.setBoolean(). Previously, this always bound the boolean parameter value using the org.hibernate.Hibernate.BOOLEAN type mapping which is an instance of org.hibernate.type.BooleanType which has a very specific meaning and expectation regarding the database column type. Now, Hibernate interprets this based on the type guessed from the query analysis.
Expanded component support
HQL now supports two ehanced ways to deal with components in HQL First is the ability to bind complete components as parameter values. This works partially on the Improved parameter type guessing described above. So for example, users can now do:
Name name = new Name(); name.setFirst( "John" ); name.setLast( "Doe" ); List johnDoes = session.createQuery( "from Person where name = :name" ) .setParameter( "name", name ) .list();
Second is use of the "row value constructor" syntax borrowed from ANSI SQL, which would allow users to do
List johnDoes = session.createQuery( "from Person where name = ('John', 'Doe')" ) .list();
Configuration changes
N/A
Native SQL query changes
Sequence of return values from native sql queries
In previous versions of Hibernate the return value from a native sql query that contains both scalars and entities were always scalar first, entities second. Starting in 3.2 the sequence is governed by the sequence in the mappings or in code. This has the effect that some code will break if you did not specify <return-scalar> or call addScalar first. The following code will break since addEntity is called before addScalar and this sequence is now obeyed in 3.2:
List result = s.createSQLQuery("select o.*, o.value as anumber from ORDER as o").addEntity(Order.class).addScalar("anumber").list(); Object[] row = (Object[])result.get(0); Integer number = (Integer)row[0]; // cast error since this will be an Order Order order = (Order)row[1]; // cast error since this will be an Integer
The following code will work in 3.2 and previous versions because addScalar's is called before addEntity:
List result = s.createSQLQuery("select o.*, o.value as anumber from ORDER as o").addScalar("anumber").addEntity(Order.class).list(); Object[] row = (Object[])result.get(0); Integer number = (Integer)row[0]; Order order = (Order)row[1];
Stored procedures no longer require OUT parameter
In the initial stored procedure support for sql-insert, sql-update, and sql-delete Hibernate required that the stored procedures define an out parameter which returned the affected row count to be able to tie into Hibernate's normal staleness checks. Starting with 3.2, this is no longer a requirement, although it is still supported for backwards compatibility. The expectation here is governed by a new attribute check
attribute on these mapping elements. The possible attributes are:
- none - specifies absolutely no checking should be performed. This is the recommended option for stored procedure support moving forward. The assumption here is that your stored procedure code performs the pertinent checks and propogates issues via SQLExceptions.
- count - perform checking based on the results returned by the JDBC operations; this is how Hibernate operates when stored procedured are not invloved.
- param - this is the legacy behaviour for stored procedure support. Essentially the same as check except that here the count value comes from the registered OUT parameter.
Behavior changes
Accessing the session outside the scope of a transaction has slightly different semantics in certain situations then in earlier versions. Two changes in particular are significant, but we feel much more correct.
- First, with FlushMode.AUTO, when a query is executed, Hibernate flushes any pertinent changes pending in the session to ensure correct query results. Starting with 3.2, this is no longer the case if we are outside a transaction; in that case the auto flush is skipped.
- Second has to do with identifiers generated via an "in-database" strategy (the so-called post-insert id generators). Saves to such entities in previous versions caused an immediate SQL INSERT to be issued in order to determine the generated identifier value. Starting with 3.2, these INSERTS will be delayed when done outside of a transaction if possible
Comments