Version 1

    Here's a mini-blog from our experiences getting Hibernate 3 working with JBoss 3.2.6, using XDoclet (2.1 or so?). Some of this is basic, some are bugs you are likely to hit, some is undocumented behavior or configurations. MySQL (innodb) was the DB.

    It took us a while to come up to speed, but hopefully the new getCurrentSession() method on the SessionFactory will ease your JBoss integration, as will this document. Buy support if you can (Steve Ebersol helped us out a lot) and I recommend you hook into the Hibernate src code for debugging and browsing in case you have a problem understanding exactly what's going on.


    Some basic requirements for configuring

    • use JBossTransactionManager
    • use JTATransactionFactory (we have this, but not sure if it is required)
    • Use DataSource factory to get Hibernate to use DB connections managed by the container.
    • obtain session via SessionFactory.getCurrentSession() if ver>=3.0.1
      • this will auto-synch with container transaction, open, close, flush, and release session from ThreadLocal storage, and register a Synchronization w/ the tx.
    • set auto_close_session and flush_before_completion to true if ver<=3.0.1 (should be * always true for session obtained via getCurrentSession() in 3.0.1, but is not)
    • Be sure all code runs in a Tx, including anything invoked by a timer, callback or MDB.
    • Browse to the Environment.java source to see the proper config constants, which are listed incorrectly in some docs. (e.g. hibernate.transaction.XXX is often correct, but hibernate.XXX is documented.)

     

    Exact environment vars used in our hibernate.properties:

    hibernate.connection.provider_class org.hibernate.connection.DatasourceConnectionProvider
    hibernate.connection.datasource java:/DefaultDS
    hibernate.transaction.factory_class org.hibernate.transaction.JTATransactionFactory
    hibernate.transaction.manager_lookup_class org.hibernate.transaction.JBossTransactionManagerLookup
    hibernate.transaction.flush_before_completion true
    hibernate.transaction.auto_close_session true
    

    Quirks

    • Versioning/Optimistic locking.
      • If an optimistic lock fails you'll get a message "unexpected row count: [...]" prior to version 3.0.2 or so. This is a bug, and should be a different exception (StaleUpdate or something). Remember at times like that that all Hibernate Excepions are spec'd to be unrecoverable.
      • The optimistic lock error will occur on the operation that required a flush to get a coherent DB state, and will not inform you of the true nature of the problem. E.g. if an Employee has a stale version, you may get a message that your query for Accounts a couple seconds later did not work because that query triggered the flush that attempted to write out the Employee.
      • Turn on TRACE logging for org.hibernate.type to get a better idea of what the problem really is (this will show all data, including versions, being written/queried), and set the show-sql flag to true in your cfg or properties file. Look for successive updates using the same version.
      • optimistic lock problems can sometimes be alleviated by breaking entities into two separately-updated entities
      • for an entity updated field-by-field in a non-transactional way, do not use a version tag (effectively turning off optimistic locking for that entity) and use dynamic-update for the class to avoid overwriting other transactions' data.
      • See serialized type issues, below
    • Using iterate or scroll in version < 3.0.1 together with auto_flush/auto_close_session causes infinite recursion / stackOverflowException.
    • Both .cfg.xml and .properties files are used, and you can put data in either or both.
    • The write-behind feature will prevent some constraint violations, by re-ordering updates. However if a flush occurs between an update and another update that is required for there not to be a constraint violation, you'll get a constraint error. Such exceptions say that a constraint was violated and will name one of the entities involved. MySQL does not say which constraint, your DB may vary.
    • In our experience one-to-one associations with the many-to-one unique=true option (using a forgein key) works, while one-to-one does not (probably a config issue on our part). How to configure both of these are documented in Hibernate in Action.
    • XDoclet requires a version property set to 2.0 (even if the version is actually 2.1 see: http://opensource.atlassian.com/projects/xdoclet/browse/XDT-1166) to get inverse=true to generate properly for bidirectional associations if you're using XDoclet. (bug in XDoclet as of 4/22/05). This will cause duplicate key violations in many-many associations. (Christian: This is of course not a bug, and the version you are setting is not the XDoclet version, but the Hibernate version! By default XDoclet generates mappings for Hibernate 1.x!)
    • If you use a Synchronization instance to hook up to the Tx (e.g. if you are managing your session yourself, rather than use getCurrentSession()), beforeTransaction() is only called when the tx commits succesfully, and must have a flush() call, afterTransaction is called on commit or rollback, and should close/release the session.
    • Serialization is tricky.
      • the "serialized" type defaults to tinyBlob using the MySQLInnoDB dialect, which has size 255 bytes. Many serialized objects are larger, causing EOF exceptions. Use length="257" or some such to trigger mapping to a larger blob type.
      • large serialized objects seem to trigger false dirty checks, causing the same update to be done twice, which in turn is a versioning / optimistic lock error (unexpected row count: ...) Use a UserType rather than "serialized" to map larger objects.
    • Hibernate uses commons logging, which will pick up a log4j.properties file, but will not use your own log4j configuration if it is programmatically changed or loaded from a file with another name.
    • With UserTypes, also specify the sql-type.  In xdoclet, we map user types this way
    @hibernate.property type = "com.pfn.wirepower.srv.persistence.userTypes.OurCustomUserType" 
    @hibernate.column name = "CustomObjectCol" sql-type="VARCHAR(50)"