From 24f3e76c9abf9f7f47bc9aef1348886d3f956129 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 25 May 2005 02:56:10 +0000 Subject: [PATCH] doc'd natural-id updated best practices git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@6901 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- reference/en/modules/basic_mapping.xml | 38 +++++++ reference/en/modules/best_practices.xml | 107 +++++++++++------- reference/en/modules/query_criteria.xml | 142 ++++++++++++++++-------- 3 files changed, 199 insertions(+), 88 deletions(-) diff --git a/reference/en/modules/basic_mapping.xml b/reference/en/modules/basic_mapping.xml index 0735044266..3697ce5eca 100644 --- a/reference/en/modules/basic_mapping.xml +++ b/reference/en/modules/basic_mapping.xml @@ -1710,6 +1710,44 @@ + + natural-id + + + + + ...... +]]> + + + Even though we recommend the use of surrogate keys as primary keys, you should still try + to identify natural keys for all entities. A natural key is a property or combination of + properties that is unique and non-null. If it is also immutable, even better. Map the + properties of the natural key inside the <natural-id> element. + Hibernate will generate the necessary unique key and nullability constraints, and your + mapping will be more self-documenting. + + + + We strongly recommend that you implement equals() and + hashCode() to compare the natural key properties of the entity. + + + + This mapping is not intended for use with entities with natural primary keys. + + + + + + mutable (optional, defaults to false): + By default, natural identifier properties as assumed to be immutable (constant). + + + + + + component, dynamic-component diff --git a/reference/en/modules/best_practices.xml b/reference/en/modules/best_practices.xml index 7cd1c1cf3d..543cbaf9e5 100644 --- a/reference/en/modules/best_practices.xml +++ b/reference/en/modules/best_practices.xml @@ -18,9 +18,17 @@ Hibernate makes identifier properties optional. There are all sorts of reasons why you should use them. We recommend that identifiers be 'synthetic' (generated, with - no business meaning). It doesn't make a difference if you use long - or java.lang.Long; primitives might be syntactically easier to handle - though. + no business meaning). + + + + + Identify natural keys. + + + Identify natural keys for all entities, and map them using + <natural-id>. Implement equals() and + hashCode() to compare the properties that make up the natural key. @@ -47,7 +55,8 @@ This is a good practice if your queries call non-ANSI-standard SQL functions. - Externalising the query strings to mapping files will make the application more portable. + Externalising the query strings to mapping files will make the application more + portable. @@ -86,12 +95,11 @@ Use hand-coded JDBC in bottlenecks. - In performance-critical areas of the system, some kinds of operations (eg. mass update / - delete) might benefit from direct JDBC. But please, wait until you know - something is a bottleneck. And don't assume that direct JDBC is necessarily faster. If need to - use direct JDBC, it might be worth opening a Hibernate Session and using that SQL - connection. That way you can still use the same transaction strategy and underlying connection - provider. + In performance-critical areas of the system, some kinds of operations might benefit from + direct JDBC. But please, wait until you know something is a bottleneck. + And don't assume that direct JDBC is necessarily faster. If you need to use direct JDBC, it might + be worth opening a Hibernate Session and using that JDBC connection. That + way you can still use the same transaction strategy and underlying connection provider. @@ -107,27 +115,29 @@ - In a three tiered architecture, consider using saveOrUpdate(). + In a three tiered architecture, consider using detached objects. When using a servlet / session bean architecture, you could pass persistent objects loaded in the session bean to and from the servlet / JSP layer. Use a new session to service each request. - Use Session.update() or Session.saveOrUpdate() to update the - persistent state of an object. + Use Session.merge() or Session.saveOrUpdate() to + synchronize objects with the database. - In a two tiered architecture, consider using session disconnection. + In a two tiered architecture, consider using long persistence contexts. Database Transactions have to be as short as possible for best scalability. However, it is often - neccessary to implement long running Application Transactions, a single unit-of-work from the - point of view of a user. This Application Transaction might span several client requests and - response cycles. Either use Detached Objects or, in two tiered architectures, simply disconnect - the Hibernate Session from the JDBC connection and reconnect it for each subsequent request. Never - use a single Session for more than one Application Transaction usecase, otherwise, you will run - into stale data. + neccessary to implement long running application transactions, a single + unit-of-work from the point of view of a user. An application transaction might span several + client request/response cycles. It is common to use detached objects to implement application + transactions. An alternative, extremely appropriate in two tiered architecture, is to maintain + a single open persistence contact (session) for the whole lifecycle of the application transaction + and simply disconnect from the JDBC connection at the end of each request and reconnect at the + beginning of the subsequent request. Never share a single session across more than one application + transaction, or you will be working with stale data. @@ -139,7 +149,7 @@ the Transaction and close the Session. If you don't, Hibernate can't guarantee that in-memory state accurately represents persistent state. As a special case of this, do not use Session.load() to determine if an instance with the given identifier - exists on the database; use find() instead. + exists on the database; use Session.get() or a query instead. @@ -147,11 +157,30 @@ Prefer lazy fetching for associations. - Use eager (outer-join) fetching sparingly. Use proxies and/or lazy collections for most associations - to classes that are not cached at the JVM-level. For associations to cached classes, where there is - a high probability of a cache hit, explicitly disable eager fetching using - outer-join="false". When an outer-join fetch is appropriate to a particular use - case, use a query with a left join. + Use eager fetching sparingly. Use proxies and lazy collections for most associations to classes that + are not likely to be completely held in the second-level cache. For associations to cached classes, + where there is an a extremely high probability of a cache hit, explicitly disable eager fetching using + lazy="false". When an join fetching is appropriate to a particular use + case, use a query with a left join fetch. + + + + + + Use the open session in view pattern, or a disciplined + assembly phase to avoid problems with unfetched data. + + + + Hibernate frees the developer from writing tedious Data Transfer Objects (DTO). + In a traditional EJB architecture, DTOs serve dual purposes: first, they work around the problem + that entity beans are not serializable; second, they implicitly define an assembly phase where + all data to be used by the view is fetched and marshalled into the DTOs before returning control + to the presentation tier. Hibernate eliminates the first purpose. However, you will still need + an assembly phase (think of your business methods as having a strict contract with the presentation + tier about what data is available in the detached objects) unless you are prepared to hold the + persistence context (the session) open across the view rendering process. This is not a limitation + of Hibernate! It is a fundamental requirement of safe transactional data access. @@ -167,23 +196,6 @@ - - Implement equals() and hashCode() using a unique business key. - - - If you compare objects outside of the Session scope, you have to implement equals() - and hashCode(). Inside the Session scope, Java object identity is guaranteed. If - you implement these methods, never ever use the database identifier! A transient object doesn't have - an identifier value and Hibernate would assign a value when the object is saved. If the object - is in a Set while being saved, the hash code changes, breaking the contract. To implement - equals() and hashCode(), use a unique business key, that is, - compare a unique combination of class properties. Remember that this key has to be stable and unique - only while the object is in a Set, not for the whole lifetime (not as stable as a database primary - key). Never use collections in the equals() comparison (lazy loading) and be careful - with other associated classes that might be proxied. - - - Don't use exotic association mappings. @@ -196,6 +208,15 @@ + + Prefer bidirectional associations. + + + Unidirectional associations are more difficult to query. In a large application, almost + all associations must be navigable in both directions in queries. + + + diff --git a/reference/en/modules/query_criteria.xml b/reference/en/modules/query_criteria.xml index e7386b16ee..3cb774f981 100644 --- a/reference/en/modules/query_criteria.xml +++ b/reference/en/modules/query_criteria.xml @@ -52,9 +52,9 @@ List cats = crit.list();]]> .add( Restrictions.in( "name", new String[] { "Fritz", "Izi", "Pk" } ) ) .add( Restrictions.disjunction() .add( Restrictions.isNull("age") ) - .add( Restrictions.eq("age", new Integer(0) ) ) - .add( Restrictions.eq("age", new Integer(1) ) ) - .add( Restrictions.eq("age", new Integer(2) ) ) + .add( Restrictions.eq("age", new Integer(0) ) ) + .add( Restrictions.eq("age", new Integer(1) ) ) + .add( Restrictions.eq("age", new Integer(2) ) ) ) ) .list();]]> @@ -83,9 +83,9 @@ Property age = Property.forName("age"); List cats = sess.createCriteria(Cat.class) .add( Restrictions.disjunction() .add( age.isNull() ) - .add( age.eq( new Integer(0) ) ) - .add( age.eq( new Integer(1) ) ) - .add( age.eq( new Integer(2) ) ) + .add( age.eq( new Integer(0) ) ) + .add( age.eq( new Integer(1) ) ) + .add( age.eq( new Integer(2) ) ) ) ) .add( Property.forName("name").in( new String[] { "Fritz", "Izi", "Pk" } ) ) .list();]]> @@ -332,13 +332,13 @@ List results = session.createCriteria(Cat.class) - Detached queries and subqueries - - The DetachedCriteria class lets you create a query outside the scope - of a session, and then later execute it using some arbitrary Session. - - - Detached queries and subqueries + + The DetachedCriteria class lets you create a query outside the scope + of a session, and then later execute it using some arbitrary Session. + + + - - A DetachedCriteria may also be used to express a subquery. Criterion - instances involving subqueries may be obtained via Subqueries or - Property. - - - - - - - - Even correlated subqueries are possible: - + + A DetachedCriteria may also be used to express a subquery. Criterion + instances involving subqueries may be obtained via Subqueries or + Property. + - + + + + + Even correlated subqueries are possible: + + + - - - - + .add( Property.forName("weight).gt(avgWeightForSex) ) + .list();]]> + + + + + + + Queries by natural identifier + + + For most queries, including criteria queries, the query cache is not very efficient, + because query cache invalidation occurs too frequently. However, there is one special + kind of query where we can optimize the cache invalidation algorithm: lookups by a + constant natural key. In some applications, this kind of query occurs frequently. + The criteria API provides special provision for this use case. + + + + First, you should map the natural key of your entity using + <natural-id>, and enable use of the second-level cache. + + + + + + + + + + + + +]]> + + + Note that this functionality is not intended for use with entities with + mutable natural keys. + + + + Next, enable the Hibernate query cache. + + + + Now, Restrictions.naturalId() allows us to make use of + the more efficient cache algorithm. + + + + + +