Version 4

    Advanced Problems

    Here are the solutions to some frequent advanced problems.


    I'm getting 'org.hibernate.MappingException: collection was not an association' when I try to join a collection of components with Criteria queries

    Hibernate currently does not support joining a collection of components or other value types with Criteria. Use HQL or submit a patch that implements this functionality.

     

    I get HibernateException: Don't dereference a collection with cascade="all-delete-orphan"

    This will happen if you load an object with a cascade="all-delete-orphan" collection and then remove the reference to the collection. Don't replace this collection, use clear() so the orphan-deletion algorithm can detect your change.

     

    I'm seeing some strange behaviour with an embedded composite identifier or when using proxies.

    It is actually surprisingly hard to get your implementation of equals()  right when proxies are enabled. You must be prepared for the possibility that that a class instance is really a proxy for that class, so you can't access instance variables directly (use the accessors).

     

    And if you are using <key-many-to-one> you should compare entity equality by comparing identifiers instead of using ==.

     

    How do I use a custom type as a query parameter?

    Use Hibernate.custom(MyUserType.class) to obtain the Hibernate Type corresponding to your UserType or CompositeUserType.

     

    What column should I map the <index> tag of an array or List to?

    You need a seperate table column holding the array or List index (the i in foo[i])! If your relational model doesn't have an index column, use a Set instead. This seems to put people off who assume that List should just be a more convenient way of accessing an unordered collection. Hibernate collections strictly obey the actual semantics attached to the Set, List and Map interfaces. List elements don't just spontaneously rearrange themselves.

     

    On the other hand, people who planned to use the List to emulate "bag"-style semantics have a legitimate grievance here. Fortunately you can map a List or Collection with bag semantics.

    What are bags for?

    A bag is an unordered, unindexed collection which can contain the same element multiple times. The Java collections framework lacks a Bag interface (though you can emulate it with a List). Hibernate lets you map properties of type List or Collection with the <bag> element. Note that bag semantics are not really part of the Collection contract and they actually conflict with the semantics of List.

     

    Hibernate is violating a unique constraint!

    Hibernate isn't quite as clever with unique constraints as it is with foreign keys. Sometimes you might need to give a little hint.

     

    A unique constraint violation could occur if two objects are both being updated, one is "releasing" a value and the other is "obtaining" the same value. A workaround is to flush() the session manually after updating the first object and before updating the second.

     

    (This kind of problem occurs rarely in practice.)

     

    I am mixing identity id generation with another id generation strategy and Hibernate causes a not null constraint violation.

    Hibernate uses transactional write-behind for SQL inserts. Unfortunately, write-behind is impossible for the case if identity key generation. So, if you are mixing id generation strategies, you might need to:

    1. save the object referred to by the foreign key (the object that does not use identity)
    2. flush() manually
    3. save the object that is using identity generation

     

    Why does Hibernate always initialize a collection when I only want to add or remove an element?

    Unfortunately the collections API defines method return values that may only be computed by hitting the database. There are three exceptions to this: Hibernate can add to a <bag>, <idbag> or <list> declared with inverse="true" without initializing the collection; the return value must always be true.

     

    If you want to avoid extra database traffic (ie. in performance critical code), refactor your model to use only many-to-one associations. This is almost always possible. Then use queries in place of collection access.

     

    I tried to filter a query result List and recieved net.sf.hibernate.QueryException: The collection was unreferenced.

    Collection filters are for filtering persistent collections, not query results.

     

    Hibernate does not return distinct results for a query with outer join fetching enabled for a collection (even if I use the distinct keyword)?

    First, you need to understand SQL and how OUTER JOINs work in SQL. If you do not fully understand and comprehend outer joins in SQL, do not continue reading this FAQ item but consult a SQL manual or tutorial. Otherwise you will not understand the following explanation and you will complain about this behavior on the Hibernate forum.

     

    Typical examples that might return duplicate references of the same Order object:

     

    List result = session.createCriteria(Order.class)
                            .setFetchMode("lineItems", FetchMode.JOIN)
                            .list();
    

     

    <class name="Order">
        ...
        <set name="lineItems" fetch="join">
    
    List result = session.createCriteria(Order.class)
                            .list();
    

     

    List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();
    

     

    All of these examples produce the same SQL statement:

     

    SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID
    

     

    Want to know why the duplicates are there? Look at the SQL resultset, Hibernate does not hide these duplicates on the left side of the outer joined result but returns all the duplicates of the driving table. If you have 5 orders in the database, and each order has 3 line items, the resultset will be 15 rows. The Java result list of these queries will have 15 elements, all of type Order. Only 5 Order instances will be created by Hibernate, but duplicates of the SQL resultset are preserved as duplicate references to these 5 instances. If you do not understand this last sentence, you need to read up on Java and the difference between an instance on the Java heap and a reference to such an instance.

     

    (Why a left outer join? If you'd have an additional order with no line items, the result set would be 16 rows with NULL filling up the right side, where the line item data is for other order. You want orders even if they don't have line items, right? If not, use an inner join fetch in your HQL).

     

    Hibernate does not filter out these duplicate references by default. Some people (not you) actually want this. How can you filter them out?

     

    Like this:

     

    Collection result = new LinkedHashSet( session.create*(...).list() );
    

     

    A LinkedHashSet filteres out duplicate references (it's a set) and it preserves insertion order (order of elements in your result). That was too easy, so you can do it in many different and more difficult ways:

     

    List result = session.createCriteria(Order.class)
                            .setFetchMode("lineItems", FetchMode.JOIN)
                            .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                            .list();
    

     

    <class name="Order">
        ...
        <set name="lineItems" fetch="join">
    
    List result = session.createCriteria(Order.class)
                            .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                            .list();
    

     

    List result = session.createQuery("select o from Order o left join fetch o.lineItems")
                          .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) // Yes, really!
                          .list();
    

     

    List result = session.createQuery("select distinct o from Order o left join fetch o.lineItems").list();
    

     

    The last one is special. It looks like you are using the SQL DISTINCT keyword here. Of course, this is not SQL, this is HQL. This distinct is just a shortcut for the result transformer, in this case. Yes, in other cases an HQL distinct will translate straight into a SQL DISTINCT. Not in this case: you can not filter out duplicates at the SQL level, the very nature of a product/join forbids this - you want the duplicates or you don't get all the data you need.

     

    All of this filtering of duplicates happens in-memory, when the resultset is marshalled into objects.

     

    It should be also obvious why resultset row-based "limit" operations, such as setFirstResult(5) and setMaxResults(10)  do not work with these kind of eager fetch queries. If you limit the resultset to a certain number of rows, you cut off data randomly. One day Hibernate might be smart enough to know that if you call setFirstResult() or setMaxResults() it should not use a join, but a second SQL SELECT. Try it, your version of Hibernate might already be smart enough. If not, write two queries, one for limiting stuff, the other for eager fetching.

     

    Do you want to know why the example with the Criteria query did not ignore the fetch="join" setting in the mapping but HQL didn't care? Read the next FAQ item.

     

    Hibernate ignores my outer-join="true" or fetch="join" setting and fetches an association lazily, using n+1 selects!

    HQL queries always ignore the setting for outer-join or fetch="join" defined in mapping metadata. This setting applies only to associations fetched using get() or load(), Criteria queries, and graph navigation. If you need to enable eager fetching for a HQL query, use an explicit LEFT JOIN FETCH.

     

    Note also that Hibernate 2.x does not support outer join fetching of more than one many-valued association in the same query. Hibernate 3.x allows you to create Cartesian products, even if they are slower than two separate queries.

     

    Hibernate is leaking JDBC connections!

    There are three possible reasons why Hibernate might not be closing connections:

     

    • You are forgetting to call Session.close().

     

    This is the most common cause. Consider carefully how you are handling sessions. Are you sure you create only one Session per transaction? Are you certain you close the Session even if an exception occurs (ie. in a finally block).

     

    Hibernate issues a warning in the log when an unclosed session is garbage collected, so looking there is a good place to start.

     

    • Hibernate is doing connection pooling.

     

    Which ConnnectionProvider are you using? If its not DatasourceConnectionProvider, then it is probably doing connection pooling. This can cause problems in certain environments. Pooling may be disabled for DriverManagerConnectionProvider by setting hibernate.connection.pool_size=0 in hibernate.properties.

     

    • You are supplying your own connection.

     

    If you supply your own JDBC connection to SessionFactory.openSession(), Hibernate does not close it from Session.close().

     

    In any case, you can always bypass Hibernate's Connection and transaction handling altogether. For example, the following code block shows the application managing connections and transactions manually:

     

    Session s = sf.openSession( getMyJDBCConnection() ); //supply a JDBC connection
    try {
        // do some work
        s.connection().commit();
    }
    catch (Exception e) {
        s.connection().rollback();
    }
    finally {
        s.close().close(); //close the session and user-supplied JDBC connection
    }
    

     

    That way you can be certain that Hibernate is not to blame when you see these kinds of issues (we are already quite certain).

     

    When I leave Hibernate running overnight, I come back and find that it can no longer connect to the database.

    (This is particularly common in the case of MySQL, wich times out JDBC connections after a short time.)

     

    You should never use Hibernate's built-in connection pooling in production; it is non-scalable and non-fault-tolerant. Instead, use DBCP or C3P0 (or, even better, an application server datasource).

     

    I'm supplying my own connections to Hibernate and I see exceptions like: "too many opened cursors"

    You must disable PreparedStatement caching by setting

     

    hibernate.statement_cache.size=0

     

    My class has two one-to-many associations to different subclasses of the same root class, but Hibernate ignores the actual concrete class when loading the associations.

    The following mapping does not work:

     

    <class name="BalanceSheet">
        ....
        <set role="purchases">
            <key ....>
            <one-to-many class="Purchase" column="bsid"/>
        </set>
        <set role="sales">
            <key ....>
            <one-to-many class="Sale" column="bsid"/>
        </set>
    </class>
    
    <class name="Transaction">
        ....
        <subclass name="Purchase".../>
        <subclass name="Sale".../>
    </class>
    

     

    You must either:

    • use a where attribute in each <set> mapping
    • map each association to a different column of the Transaction table
    • map a single association and use a collection filter to retrieve instances of a particular subclass
    • use a table-per-concrete-class mapping strategy for the Transaction hierarchy

     

    How do I set up a 1-to-1 relationship as lazy?

    Use

     

    <one-to-one constrained="true" fetch="select" lazy="proxy" class="Foo"/>
    

     

    Actually, you do not usually need to explicitly specify fetch and lazy. The critical bit is constrained="true".

     

    Lazy fetching is only conceptually possible for a mandatory association since we have to hit the other table to determine whether the association is null or not!

     

    I have a non-lazy set of entities that override equals() and Hibernate throws a NullPointerException

    In certain special situations (the common one is a reflexive association) a non-lazy Set containing a persistent class that overrides equals(), or a SortedSet with a custom Comparator can cause a NullPointerException. This is a known issue that will probably not be fixed, because it is the result of an important performance optimization. The workaround is to re-map the set with lazy="true".

     

    The query language IS NULL syntax won't work with a one-to-one association!

    The following queries:

     

    FROM User u WHERE u.email IS NULL
    

     

    FROM User u WHERE u.email IS NOT NULL
    

     

    will not work for a one-to-one association! Use

     

    SELECT u FROM User u LEFT JOIN u.email e WHERE e IS NULL
    

     

    FROM User u WHERE NOT u.email IN (FROM Email e)
    

     

    instead of the simple IS NULL test. The second form cannot work for a database without subselects. Use

     

    SELECT u FROM User u INNER JOIN u.email
    

     

    FROM User u WHERE u.email IN (FROM Email e)
    

     

    instead of IS NOT NULL. The third form will not work for a database without subselects.

     

    Does HQL support UNION (or INTERSECT)?

    Not at this time. You may use a native SQL query for this.

     

    Does HQL support hierarchical queries (eg. Oracle CONNECT BY)?

    Not at this time. You may use a native SQL query for this.

     

    How do I use multiple databases?

    You must configure multiple SessionFactory instances.

     

    Optimistic locking doesn't seem to be working/problem with JDBC batching?

    Hibernate uses the updated row count to detect conflicting data modifications. Hibernate will automatically disable JDBC batch updates if you are using versioned data, since some JDBC drivers return incorrect values after UPDATEs (i.e. Oracle's). If you are sure that your driver works, set the hibernate.jdbc.batch_versioned_data configuration property to true.

     

    How can I provide my <subclass> mappings in a seperate file?

    Specify an extends attribute in the subclass mapping, naming a previously mapped superclass. Use of this feature makes the ordering of the mapping documents important!

     

    Hibernate throws: Flush during cascade is dangerous.

    This almost always happens because you forgot to remove a deleted instance from an association with cascading save enabled. You should remove deleted objects from associations before flushing the session. Basically, the object you're trying to delete would be resaved to the database by it being in a collection hanging off another object. You need to remove it from other collections before deleting it.

     

    How can I map a table (or view) with no primary key?

    Every table (or even view) should have some unique key. The mathematical definition of a relation is a set of tuples. (Sets do not permit duplicate elements.) Furthermore, truly identical rows can not possibly have any meaningful semantics from the user's perspective.

     

    In some cases it may be sensible to go as far as mapping all columns of a view as a <composite-id/>, but there is almost always some smaller business key. (Note that Hibernate does not require that this unique key be enforced by the database schema, of course.)

     

    How can I change the identifier value of an existing instance (ie. the primary key)

    It is part of the definition of a primary key that it is not only unique, but also constant throughout the life of the row. Hibernate does not, and should not, support changing identifier values.

     

    Hibernate generates too many outer joins. In MySQL I get Too many tables. MySQL can only use 31 tables in a join.

    You can set fetch="select" (or outer-join="false" in Hibernate2) on particular associations or set hibernate.max_fetch_depth to a smaller value.

     

    When initializing a many-to-many association, Hibernate fills the collection with only proxies instead of fully initialized objects, resulting in n+1 selects!

    You should set fetch="join" (or outer-join="true" in Hibernate2) on the <many-to-many> element. (This is now the default in Hibernate3.)

     

    I have enabled the second-level cache, but queries still hit the database!

    The second-level cache is used for lookup by identifier. If you want to cache query result sets, you must use the query cache. See the documentation for more information.

     

    Is the second-level cache enabled by default?

    No entities or collections will be cached in the second-level cache unless you supply <cache> elements in the mapping files or <class-cache> and/or <collection-cache> elements in hibernate.cfg.xml, even when EHCache or some other cache provider is configured.

     

    Likewise, queries are not cached in the query cache, unless you explicitly call Query.setCacheable(true), even when the query cache is enabled.

     

    When I delete an object that belongs to a cached collection, the collection cache is not invalidated!

    You must remove the deleted object from all collections it belongs to.

     

    I am experiencing slow performance or low memory when inserting or querying 10000 objects

    In Hibernate 3.0 or earlier you must manage the size of the session cache using clear() or evict(). See also:

     

    http://www.hibernate.org/hib_docs/v3/reference/en/html/batch.html

     

    In Hibernate 3.1, you can use StatelessSession.

     

    Even better, try to use HQL bulk queries where possible.

     

    I have run into some limitations using <key-many-to-one> mappings

    The <key-many-to-one> mapping has some minor limitations in current versions of Hibernate. We will fix these eventually but, in our view, <key-many-to-one> is not really convenient. It's much easier to use get() or load() if your identifier class does not hold any references to other entities.

     

    So, if you have this mapping:

     

    <class name="Child">
        <composite-id>
            <key-many-to-one name="parent" column="parent_id"/>
            <key-property name="childId" column="child_id"/>
        </composite-id>
    ...
    </class>
    

     

    Try using the following instead:

     

    <class name="Child">
        <composite-id>
            <key-property name="parentId" column="parent_id"/>
            <key-property name="childId" column="child_id"/>
        </composite-id>
        <many-to-one name="parent" column="parent_id" insert="false" update="false"/>
    ...
    </class>
    

     

    So we have an identifier property holding the id of the parent entity, together with an association, mapped to the same column, with insert="false" update="false".

     

    I have a composite key where one column holds a generated value - how can I get Hibernate to generate and assign the value?

    We regard this as an extremely strange thing to want to do. If you have a generated surrogate key, why not just make it be the primary key?

     

    However, if you must do this, you can do it by writing a CompositeUserType for the composite identifier class, and then defining a custom IdentifierGenerator that populates the generated value into the composite key class.

     

    Why doesn't Hibernate just go back to the server tier and fetch data lazily instead of throwing LazyInitializationException.

    Fetching data outside the scope of a well-defined persistence context is extremely dangerous.

     

    Transparently fetching data from the server would expose you to:

    1. massive n+1 requests problems
    2. loss of referential integrity in your client-side object graph
    3. data aliasing problems

     

    In some ways this idea is very similar to (but worse than) the olde EJB 1/2 idea of a "remote entity", which we all know is an evil construct that simply does not scale.

     

    How should users actually get the underlying connection from the session to do their driver specific operations ?

    A work around is the interface org.hibernate.jdbc.ConnectionWrapper, which defines exactly one method: Connection getWrappedConnection(). Two ways to use this:

     

    1. just call the new BorrowedConnectionProxy.getWrappedConnection method passing in the proxy
    2. cast the proxy to ConnectionWrapper, and call getWrappedConnection().