Hibernate Users FAQ - Advanced Problems
Advanced Problems
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).Query result lists are always exactly the same size as the underlying JDBC ResultSet. Try using uniqueResult() if appropriate, or simply distinctify the results yourself using, eg.
Collection results = new HashSet( session.createQuery("from Parent p left join fetch p.children").list() );
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" outer-join="false" class="Foo"/>
with a proxied class, Foo. This 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 NullPointerExceptionIn 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".
Hibernate ignores my outer-join="true" setting and fetches an association lazily, using n+1 selects!HQL queries always ignore the setting for outer-join defined in the mapping document. 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 does not support outer join fetching of more than one many-valued association in the same query.
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.
How do I use multiple databases?You must configure multiple SessionFactory instances.
Optimistic locking doesn't seem to be working!Disable JDBC batch updates. Hibernate uses the updated row count to detect conflict. If batch updates are enabled, the row count is unavailable.
How can I provide my <subclass> mappings in a seperate file?Use an XML enternal entity declaration. Since Hibernate 2.1, you can 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: Another object was associated with this idFor a particular session, there may be at most one object that represents a particular database row. So, if you already loaded an object with a particular identifier, you can't then try to associate a different object with the same identifier by calling update(). If you absolutely must do this, evict() the original instance first - but it is much better to simply avoid this situation.
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.
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 outer-join="false" on particular associations or, in Hibernate 2.1, use hibernate.max_fetch_depth to a smaller value.
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.
|