|
Hibernate3 Migration Guide
Hibernate 3.0Hibernate 3.0 is not source-compatible with Hibernate 2.1, and is not intended as a drop-in replacement for 2.1. However, the changes to 3.0 were carefully designed to allow straightforward migration of both code and metadata. Most projects will not experience many difficulties migrating. This document describes the changes between Hibernate 2.1 and Hibernate 3.0 that will affect existing applications, and mentions certain new features of Hibernate 3.0 that might be useful to existing applications. Changes are classified into API changes (affecting Java code), metadata changes (affecting the XML O/R mapping metadata), query language changes (affecting HQL queries), and config changes. Note that it is possible to run Hibernate 2 and Hibernate 3 side by side in the same application. API ChangesPackage namingThe Hibernate3 package structure is rooted at org.hibernate instead of net.sf.hibernate. This renaming was done to allow Hibernate2 and Hibernate3 to run side by side in the same application. The first step in Hibernate3 migration is a global text search/replace, ie. s/net.sf.hibernate/org.hibernate. The net.sf.hibernate.expression package was renamed org.hibernate.criterion. Applications using Criteria queries require a further text/search replace to accommodate this change. Be careful if you use external software that references Hibernate packages. For example, EHCache has its own CacheProvider in net.sf.ehcache.hibernate.Provider. Until all external software updates its references, you might have to patch and work around this issue. For EHCache you may also use the built-in provider in Hibernate: org.hibernate.cache.EhCacheProvider org.hibernate.classic packageCertain interfaces that have been deprecated in Hibernate3 were moved to the package org.hibernate.classic. Session interfaceCertain redundant methods were deprecated and removed from the org.hibernate.Session interface. However, to make migration easier, these methods are still available, via the org.hibernate.classic.Session subinterface. These deprecated methods are:
Hibernate3 applications should use createQuery() for all query execution, DELETE queries for bulk delete and merge() instead of saveOrUpdateCopy(). Existing applications may continue to use the deprecated methods. Note that DELETE queries respect the EJB3 semantic which is slightly different from the deprecated delete(hqlSelectQuery), and thus is not a straight replacement (esp when associations are involved). Note that session.delete(object) still exists and can be used to delete a single persistent instance including performing cascading of the delete operation to dependent entities. This can be used to do get the same semantics/result as the now depreacated delete(hqlSelectQuery) methods. createSQLQuery()The overloaded forms of createSQLQuery() which took arrays have been deprecated and moved to org.hibernate.classic.Session. There is a new SQLQuery interface which provides equivalent functionality (and more). Existing applications may continue to use the deprecated methods. Lifecycle and Validatable interfacesThe Lifecycle and Validatable interfaces were deprecated in Hibernate3 and moved to the org.hibernate.classic package. The Hibernate team does not consider it good practice to have domain model classes depend upon persistence-specific APIs. Hibernate3 applications should use Interceptor or the new Hibernate3 event framework. Existing applications may continue to use Lifecycle and Validatable. DependenciesPlease have a look at the required 3rd party libraries in lib/README.txt, some have been updated, some have been removed, and some have been newly added. Exception modelHibernateException and all other Hibernate exceptions are now unchecked runtime exceptions. Interceptor interfaceTwo new methods were added to the Interceptor interface. Existing interceptors will need to be ugraded to provide empty implementations of the two new methods. The signature of instantiate() was modified to take a String-valued entity name instead of a Class object. The isUnsaved() method was renamed isTransient(). To avoid issues with Interceptor migration (whether 2.x -> 3.x, or moving forward), just extend the new EmptyInterceptor class instead of writing your own empty implementation for all methods you don't need. UserType and CompositeUserType interfacesBoth UserType and CompositeUserType had several methods added, to support new functionality of Hibernate3. They were moved to the package org.hibernate.usertype. Existing user type classes will need to be upgraded to implement the new methods. Note: Hibernate3 provides the ParameterizedType interface to allow better re-useability of user type implementations. FetchModeFetchMode.LAZY and FetchMode.EAGER were deprecated. The more accurately named FetchMode.SELECT and FetchMode.JOIN have the same affect. PersistentEnumThe deprecated PersistentEnum was removed in Hibernate3. Existing applications should use UserType to handle persistent enumerated types. Blob and Clob supportHibernate now wraps Blob and Clob instances, to allow classes with a property of type Blob or Clob to be detached, serialized, deserialized, and passed to merge(). However, this means that the Blob or Clob cannot be cast to a vendor specific type (eg. oracle.sql.CLOB). You must use the getWrappedClob() or getWrappedBlob() methods: clob = (oracle.sql.CLOB) ( (org.hibernate.lob.SerializableClob) foo.getText() ).getWrappedClob(); We expect that this kind of thing will no longer be necessary when Oracle fixes their JDBC driver. Changes to extension APIsThe org.hibernate.criterion, org.hibernate.mapping, org.hibernate.persister and org.hibernate.collection packages feature heavy refactoring. Most Hibernate 2.1 applications do not depend upon these packages, and will not be affected. If your application does extend classes in these packages, you will need to carefully migrate the affected code. Metadata ChangesAssociation fetching strategiesSince it is best practice to map almost all classes and collections using lazy="true", that is now the default. Existing applications will need to explicitly specify lazy="false" on all non-lazy class and collection mappings. The outer-join attribute is deprecated. Use fetch="join" and fetch="select" instead of outer-join="true" and outer-join="false". Existing applications may continue to use the outer-join attribute, or may use a text search/replace to migrate to use of the fetch attribute. Beware, this means you have to put lazy="false" on all collection-mappings and classes which previously did not have a "lazy"-attribute. A quick and dirty alternative for migration is also to put default-lazy="false" on all your hibernate-mapping elements. Identifier mappingsThe unsaved-value attribute is now optional, in most cases. Hibernate will use unsaved-value="0" as the default, where that is sensible. Note: In Hibernate3, it is no longer necessary to implement Interceptor.isUnsaved() when using natural keys (assigned identifiers or composite identifiers) and detached objects. In the absence of any "hints", Hibernate will query the database to determine if an object is new or detached. However, the use of Interceptor.isUnsaved() might result in higher performance, since the database query can be avoided. Collection mappingsThe <index> element is semi-deprecated, <list-index> and <map-key> are now preferred. <map-key-many-to-many> is preferred to <key-many-to-many>. <composite-map-key> is preferred to <composite-index>. DTDUpdate the DTD reference in your hbm XML files. Change http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd to http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd in the DOCTYPE. If your application took 5 seconds to startup and now takes 50 with Hibernate3, you forgot to change the DTD. Hibernate will then try to lookup (and timeout) the DTD on the Internet for every mapping file instead of its JAR file. Query Language ChangesHibernate3 comes with a brand-new, ANTLR-based HQL/SQL query translator. However, the Hibernate 2.1 query parser is still available. The query parser may be selected by setting the Hibernate property hibernate.query.factory_class. The possible values are org.hibernate.hql.ast.ASTQueryTranslatorFactory, for the new query parser, and org.hibernate.hql.classic.ClassicQueryTranslatorFactory, for the old parser. We are working hard to make the new query parser support all queries allowed by Hibernate 2.1. However, we expect that many existing applications will need to use the Hibernate 2.1 parser during the migration phase. The Hibernate 1.x syntax "from f in class bar.Foo" is no longer supported, use "from bar.Foo as f" or "from bar.Foo f". Don't use dots in named HQL parameter names. Note: there is a known bug affecting dialects with theta-style outer joins (eg. OracleDialect for Oracle 8i, TimesTen dialect, Sybase11Dialect). Try to use a dialect which supports ANSI-style joins (eg. Oracle9Dialect), or fall back to the old query parser if you experience problems. indices() and elements() functionsThe indices() and elements() constructs may no longer appear in the HQL select clause (the semantics of this was very confusing to users). They may still be used in the where() clause. Use an explicit join instead of select elements(...). Configuration changesBEA Weblogic issuesHibernate3 uses ANTLR for the new query parser. Unfortunately BEA Weblogic includes a version of ANTLR in the system classpath which will be loaded before any application libraries and, because Weblogic doesn't seem to support proper class loader isolation, will not see the Hibernate classes in the application's context. BEA seems to solve this issue by prefixing package names, but the distributed ANTLR doesn't have this prefix. Another source for this issue is the usage of Class.forName() in ANTLR itself. Until both parties have solved these issues we can only provide workarounds: Place all your Hibernate and dependent libraries on the application server's boot classpath or use the old query parser as described above. The Hibernate Team has sent a fix for this issue to the ANTLR developers, and it should be included in the next release. We'll distribute this new version with a minor release of Hibernate in the next few weeks or months. For now we distribute a patched version of ANTLR with Hibernate 3.0 which uses the context classloader instead of Class.forName(). On the Weblogic side, this has since been resolved in Weblogic 9.2+. In the weblogic-application.xml descriptor, put the following: <prefer-application-packages> <package-name>antlr.*</package-name> </prefer-application-packages> Hibernate 3.1Hibernate 3.1 is a relatively minor upgrade to Hibernate 3.0 - with many new features. We expect it to be a drop-in replacement for Hibernate 3.0, in other words. You should be able to upgrade to 3.1 by using the new JAR (and after checking the 3rd party library dependencies). However, some changes made to the Hibernate 3.1 core might lead to problems with code relying on old side effects. We are trying to document them here: API changesUpdates to NamingStrategy interfaceIf you implemented a custom NamingStrategy you will have to add implementations of the new methods of that interface, or extend DefaultNamingStrategy or ImprovedNamingStrategy. Event listenersYou now can plug several event listeners per event, so the EventListeners API changed to accept an array of listeners instead of a single listener object per event. Your home made listeners extending the Hibernate default one are likely to work but you might want to consider using the ability to chain those listeners instead of inheriting from the default one. Configuration changesJDBC connection release modePrevious versions defaulted to ON_CLOSE for backwards compatibility with Hibernate 2.x. As of 3.1, however, the default is now "auto". Typically it is best to simply not override this default. If your application depends on some quirk of the old behavior (like having the same connection available beyond transaction boundaries, etc) then you may need to tweak this; but you should view this as a problem with your application logic and fix that. Or, if you believe that you can "read data without or outside of a transaction", you will likely face problems in Hibernate 3.1. Of course, there can be no data access outside of a transaction, be it read or write access, and Hibernate 3.1 makes it much more difficult to write bad code that relies on auto-commit side effects. See section 11.5. (Connection Release Modes) in the 3.1 documentation for more details. Important known issue: If you call session.connection() in Hibernate 3.1.0, you are responsible to close() the JDBC Connection yourself! Note that this might change again in a future release. Event listenersEvent listeners now support several event listeners per event. hibernate.cfg.xml should use the more appropriate <event> element to configure them instead of the <listener> one. Query language changesStricter checking of invalid queriesHibernate 3.1 has stricter checking for invalid queries, some of which actually worked purely by side effect in earlier versions. For example, Hibernate never supported "from Entity e where e.collection.property" but needs an explicit join for collection elements "from Entity e join e.collection c where c.property". Furthermore, a query that specifies join fetching, but the owner of the fetched association was not present in the select list throws an exception now: "select b from A join fetch a.bees b" - this query makes no sense, remove the "fetch". hbm2javahbm2java is no longer a independent tool as it has been rewritten and integrated into the http://tools.hibernate.org?. In that process some features have been removed because they were broken or not maintained, see Hbm2javaCompability. Hibernate 3.2API changesBytecodeProvider extension3.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. Behavioral changesNon-transactional accessAccessing 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. Query language changesImplicit joins are more deterministicWith such mapping: <class name='Currency' lazy='false'> .... </class> <class name='Asset'> <id name='id' ... </id> <many-to-one name='currency' class='Currency' fetch='select'/> </class> and such HQL query: 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 exceptionThe always illegal statement: 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 typesIn 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 guessingPrevious 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. Expanded component supportHQL 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();
The other 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();
Improved boolean literal and parameter handlingEarlier 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. Native SQL query changesSequence of return values from native sql queries (createSQLQuery() and getNamedQuery())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. 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 parameterIn 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, count, and param. 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. Hibernate 3.3API changesNew L2 Caching APIsThe old CacheProvider interfaces/impls are still around with bridges to the new APIs, so this is more of a heads-up. Currently only the integration with JBossCache 2.x via the hibernate-jbosscache2 module takes advantage of the new APIs. Dependenciesslf4jWe have migrated to use slf4j as our logging abstraction rather than commons-logging. A significant difference between the two is that slf4j requires that users (at least) select which logging backend they want to use. This is accomplished by defining a dependency on the appropriate logging bridge (slf4j-log4j for example) if using maven or by dropping any needed jars on your classpath. See http://in.relation.to/9384.lace for details. javassistJavassist is now the default bytecode provider rather than cglib. ModularizationAgain, see http://in.relation.to/9384.lace for details. TBC
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||