| Recent Entries |
|
25. May 2013
|
||
|
22. May 2013
|
||
|
14. May 2013
|
||
|
03. May 2013
|
||
|
02. May 2013
|
||
|
25. Apr 2013
|
||
|
22. Apr 2013
|
||
|
15. Apr 2013
|
||
|
10. Apr 2013
|
||
|
03. Apr 2013
|
| Seam News | (211) |
| Hibernate | (137) |
| Seam | (131) |
| RichFaces | (92) |
| Contexts and Dependency Injection | (91) |
| News | (83) |
| Web Beans | (61) |
| JBoss Tools | (60) |
| Core Release | (59) |
| Eclipse | (56) |
| JavaServer Faces | (55) |
| Hibernate Search | (50) |
| Ceylon | (49) |
| JBoss Tools Eclipse | (44) |
| Weld | (43) |
One of the things in Hibernate in Action that needs serious improvement is the section about data access objects, the good old DAO pattern.
Things gone wrong
Some of the mistakes I've made in the first edition:
I assumed that DAO is not only a well known pattern, but that most people already had experience how to write /state-oriented/ data access object interfaces. It turns out that most developers are very familiar with /statement-oriented/ DAOs, like you would implement with plain JDBC or iBatis (or the new stateless Hibernate Session in Hibernate 3.1), but never thought about persistence services with active object state management before - and how to design an application data access interface for such a service.
All examples had internal exception handling. Now, in a real application I'd just take the Hibernate 2.x source code, replace HibernateException extends Exception with HibernateException extends RuntimeException and leave it to my controlling code, not the data access objects, to convert and handle exceptions (including transaction rollback).
But, in the book, I was trying to avoid this issue, something we should actually have resolved in Hibernate 2.x a long time ago. We finally switched to unchecked exceptions in Hibernate 3.x but I still see many DAO snippets that include some kind of exception handling (or even worse, an exception handling /framework/) for unchecked exceptions.
Finally, lazy transaction demarcation, that is, starting a transaction if the current thread doesn't already have one, was only shown as part of the DAO constructor. There are many other ways to do this. One would be the DAO factory, which I also skipped in the book, (wrongly) assuming that everybody knows how to write a factory.
Some other common mistakes I've seen in the past (even in recent other Hibernate books) and you should avoid when writing your DAOs:
Excessive exception handling and wrapping. You should never have to wrap and re-throw an unchecked exception in a DAO method, certainly not for logging, or worse to swallow the exception. Avoid, change, or wrap persistence services that only throw checked exceptions. The only exception states of interest, in higher level code, are retry the unit of work
(e.g. database connection failed, lock couldn't be aquired, etc.) or don't retry the unit of work
(constraint violation, SQL error, etc.). Everything else, like exceptions typed to the exact SQL error, are sugar on top - at most good for better looking error message screens. You can't use database exceptions for validation or any other regular data processing. Don't let yourself be fooled by the elaborate exception hierarchies in Hibernate and other frameworks.
Ignorance of object state management. If you have a full ORM solution like Hibernate, you get automatic object state management. If a property value of a business object instance is modified you don't have to call saveThisObject() on a DAO, automatic dirty checking is provided inside a transaction. Or, if you write your DAO with state management in mind, understand the state transitions to avoid unnecessary code: session.update(o) does not need to be called before session.delete(o), for example.
Session per operation anti-pattern. Never should you use a new Hibernate Session for each DAO operation. A unit of work has a larger scope and your Hibernate application will perform horrible with this anti-pattern. This is very easy to avoid, just setthe DAO's Session when it is constructed or look it up from a thread local. In fact, the scope of the Session often seems to be handled ad-hoc and on a this is better looking code
basis, when it should actually be a conscious design decision.
So, in the second edition I plan to include more than just 4 pages about the DAO pattern and write more about state/statement-oriented APIs, exception handling, how to best deal with lazy transactions, how to add factories into the mix, and so on. I'll even include DAOs written as stateless EJB 3.0 session beans.
New DAO pattern for JDK 5.0
I wanted to actually defer updating the DAO section until I would work on that chapter in the book. However, some guys made proposals for /generic/ DAOs on the CaveatEmptor forum and I realized it really is time to put some of the new JDK 5.0 features to good use (besides annotations, of course). If you are still using JDK 1.4 primarily you might want to stop reading now... but I strongly encourage you to read the Hibernate in Action DAO examples (and any other examples, for that matter) with the caveats mentioned above in mind.
If you are using JDK 5.0, get the CaveatEmptor alpha2 release now. Let's walk through some of the DAO source code.
This time I based the DAO example on interfaces. Tools like Hibernate already provide database portability, so persistence layer portability shouldn't be a driving motivation for interfaces. However, DAO interfaces make sense in more complex applications, when several persistence services are encapsulate in one persistence layer, and they deserve more than a note (which was all I did in HiA first edition).
I use one interface per persistent entity, with a super interface for common CRUD functionality:
public interface GenericDAO<T, ID extends Serializable> {
T findById(ID id, boolean lock);
List<T> findAll();
List<T> findByExample(T exampleInstance);
T makePersistent(T entity);
void makeTransient(T entity);
}
You can already see that this is going to be a pattern for a state-oriented data access API, with methods such as makePersistent() and makeTransient(). Furthermore, to implement a DAO you have to provide a type and an identifier argument. As for most ORM solutions, identifier types have to be serializable.
The DAO interface for a particular entity extends the generic interface and provides the type arguments:
public interface ItemDAO extends GenericDAO<Item, Long> {
public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";
Bid getMaxBid(Long itemId);
Bid getMinBid(Long itemId);
}
We basically separate generic CRUD operations and actual business-related data access operations from each other. (Ignore the named query constants for now, they are convenient if you use annotations.) However, even if only CRUD operations are needed for a particular entity, you should still write an interface for it, even it it is going to be empty. It is important to use a concrete DAO in your controller code, otherwise you will face some refactoring once you have to introduce specific data access operations for this entity.
An implementation of the interfaces could be done with any state-management capable persistence service. First, the generic CRUD implementation with Hibernate:
public class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {
private Class<T> persistentClass;
private Session session;
public GenericHibernateDAO(Class<T> persistentClass, Session session) {
this.persistentClass = persistentClass;
this.session = session;
}
protected Session getSession() {
return session;
}
public Class<T> getPersistentClass() {
return persistentClass;
}
public T findById(ID id, boolean lock) {
T entity;
if (lock)
entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
else
entity = (T) getSession().load(getPersistentClass(), id);
return entity;
}
@SuppressWarnings("unchecked")
public List<T> findAll() {
return findByCriteria();
}
@SuppressWarnings("unchecked")
public List<T> findByExample(T exampleInstance) {
return findByCriteria( Example.create(exampleInstance) );
}
@SuppressWarnings("unchecked")
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
public void makeTransient(T entity) {
getSession().delete(entity);
}
/**
* Use this inside subclasses as a convenience method.
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Criterion... criterion) {
Criteria crit = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
crit.add(c);
}
return crit.list();
}
}
There are some interesting things in this implementation. First, it clearly needs a Session to work, provided to the constructor. How you set the Session, and what scope this Session has is of no concern to the actual DAO implementation. What follows are the implementations of the generic CRUD operations, quite straightforward. The last method is quite nice, using another JDK 5.0 feature, /varargs/. It helps us to build Criteria queries in concrete entity DAOs. This is an example of a concrete DAO that extends the generic DAO implementation for Hibernate:
public class ItemDAOHibernate
extends GenericHibernateDAO<Item, Long>
implements ItemDAO {
public ItemDAOHibernate(Session session) {
super(Item.class, session);
}
public Bid getMaxBid(Long itemId) {
Query q = getSession().getNamedQuery(ItemDAO.QUERY_MAXBID);
q.setParameter("itemid", itemId);
return (Bid) q.uniqueResult();
}
public Bid getMinBid(Long itemId) {
Query q = getSession().getNamedQuery(ItemDAO.QUERY_MINBID);
q.setParameter("itemid", itemId);
return (Bid) q.uniqueResult();
}
}
Another example which uses the findByCriteria() method of the superclass with variable arguments:
public class CategoryDAOHibernate
extends GenericHibernateDAO<Category, Long>
implements CategoryDAO {
public CategoryDAOHibernate(Session session) {
super(Category.class, session);
}
public Collection<Category> findAll(boolean onlyRootCategories) {
if (onlyRootCategories)
return findByCriteria( Expression.isNull("parent") );
else
return findAll();
}
}
We bring it all together in a DAO factory, which not only sets the Session when a DAO is constructed, but can also take care of lazy transaction starting, and contains inline classes to implement CRUD-only DAOs with no business-related operations:
public class HibernateDAOFactory extends DAOFactory {
public ItemDAO getItemDAO() {
return new ItemDAOHibernate(getCurrentSession());
}
public CategoryDAO getCategoryDAO() {
return new CategoryDAOHibernate(getCurrentSession());
}
public UserDAO getUserDAO() {
// Inline implementation, constructor only
class UserDAOHibernate
extends GenericHibernateDAO<User, Long>
implements UserDAO {
public UserDAOHibernate(Session session) {
super(User.class, session);
}
}
return new UserDAOHibernate(getCurrentSession());
}
protected Session getCurrentSession() {
// Get a Session and begin a database transaction. If the current
// thread/EJB already has an open Session and an ongoing Transaction,
// this is a no-op and only returns a reference to the current Session.
HibernateUtil.beginTransaction();
return HibernateUtil.getCurrentSession();
}
}
This concrete factory for Hibernate DAOs extends the abstract factory, which is the interface we'll use in application code:
public abstract class DAOFactory {
public static final DAOFactory EJB3_PERSISTENCE =
new org.hibernate.ce.auction.dao.ejb3.Ejb3DAOFactory();
public static final DAOFactory HIBERNATE =
new org.hibernate.ce.auction.dao.hibernate.HibernateDAOFactory();
public static final DAOFactory DEFAULT = HIBERNATE;
// Add your DAO interfaces here
public abstract ItemDAO getItemDAO();
public abstract CategoryDAO getCategoryDAO();
public abstract CommentDAO getCommentDAO();
public abstract UserDAO getUserDAO();
public abstract CategorizedItemDAO getCategorizedItemDAO();
}
Of course, the DEFAULT could be externalized to some configuration setting if your application needs to switch persistence layers for each deployment. Note that this factory example is suitable for persistence layers which are primarily implemented with a single persistence service, such as Hibernate or EJB 3.0 persistence. If you have to mix persistence APIs, for example, Hibernate and plain JDBC, the pattern changes slightly. I'll write more about it in the book - keep in mind that you can also call session.connection() /inside/ a Hibernate-specific DAO, or use one of the many bulk operation/SQL support options in Hibernate 3.1 to avoid plain JDBC.
Finally, this is how data access now looks like in controller/command handler code:
ItemDAO itemDAO = DAOFactory.DEFAULT.getItemDAO();
UserDAO userDAO = DAOFactory.DEFAULT.getUserDAO();
public void execute() {
Bid currentMaxBid = itemDAO.getMaxBid(itemId);
Bid currentMinBid = itemDAO.getMinBid(itemId);
Item item = itemDAO.findById(itemId, true);
newBid = item.placeBid(userDAO.findById(userId, false),
bidAmount,
currentMaxBid,
currentMinBid);
}
Two links you might find useful: ThreadLocal Session and Open Session in View
I also plan to write a section about /integration testing/ of DAOs. What do you think about this pattern? Did I miss anything and what else do you want covered in the book?
P.S. Credit has to be given to Eric Burke, who first posted the basics for this pattern on his blog. Unfortunately, not even the Google cache is available anymore.
As promised, a current snapshot of my work on Hibernate in Action, second edition. The CaveatEmptor alpha2 release has some quite interesting new examples:
- New DAO pattern based on JDK 5.0 generics
- First implementation of a Nested Set model for tree-structured read-mostly data
I'll blog about both later this week. Progress on the second edition of HiA is good
but there is just so much new material to write about (esp. annotation mappings).
Let's see if we can keep the early Q4
release date.
A new updated version of the Hibernate Tools (http://tools.hibernate.org) project have been made available.
The tools are for both Eclipse and ANT, the ANT related docs are at http://www.hibernate.org/hib_docs/tools/ant/index.html
The eclipse plugins is now compatible with WTP 0.7 and contains a bunch of new features and improvements.
My personal favorite at the moment, is the Dynamic Query Translator
view which continously
shows you the SQL, Hibernate will generate for the query you are typing in the HQL editor.
Great for learning and tuning HQL queries - note that EJB3-QL works here too.
The HQL editor is also new and replaces the HQL view and introduces code formatting, syntax highlighting and code completion.
Furthermore we have a initial class diagram view as well as many other improvements to wizards, templates, and code generators. See the complete list with screenshots at class diagrams at http://www.hibernate.org/hib_docs/tools/newandnoteworthy/hibernate-eclipse-news-3.1.0.alpha5.html for more information.
As always feedback, ideas, contributions, patches, bug-reports are more than welcome by the usual means at http://forum.hibernate.org and JIRA.
New releases of both Hibernate Entity Manager and Hibernate Annotations are available.
Hibernate Entity Manager now supports full .par archive including entity and hbm files auto discovery (see the previous blog entry for more details on this feature) and fixes some critical bugs (see the release notes http://sourceforge.net/project/shownotes.php?release_id=346915).
Hibernate Annotations new release is focused on better support of Hibernate specific features (all Hibernate id generators support, column indexes generation, non PK referencedColumnName support for @OneToOne, @ManyToOne, and @OneToMany...) and of course bug fixes (See the release notes http://sourceforge.net/project/shownotes.php?release_id=346914 for more details).
These releases are compatible with the latest Hibernate core release (3.1 beta1).
Packaging has always been a manual operation in ORM world. In Hibernate, you have to list the mapped entities either through the configuration API or through the hibernate.cfg.xml file. For a while now, JBoss AS has introduced the notion of .har, basically an archive scanned by the deployer to discover the Hibernate configuration and the hbm.xml files in it.
Packaging inside an EJB3 container
The EJB3 expert group has introduced the same notion in the EJB3 public draft. A PAR archive is basically a jar file having the .par extension. All you have to do is put all your annotated entities in the archive and the container has the responsibility to scan it and find all annotated entities. A PAR archive is a persistence unit definition that will be used to create an EntityManagerFactory (aka SessionFactory in the Hibernate world). You will then be able to use your persistence unit (by looking up or injecting an EntityManager or an EntityManagerFactory) named by the name of the PAR file without the extension (ie mypersistenceunit.par will be referred as mypersistenceunit).
Since you might want to customize your persistence unit configuration, a persistence.xml file can be added in the META-INF directory.
<?xml version="1.0" encoding="UTF-8"?>
<entity-manager>
<name>FinancialPU</name>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/MyDB</jta-data-source>
<class>com.acme.MyClass</class>
<jar-file>externalEntities.jar</jar-file>
<properties>
<property name="hibernate.max_fetch_depth" value="4"/>
</properties>
</entity-manager>
Let's analyze this small but comprehensive example.
The name element allows you to override the persistence unit name (defaulted to the PAR file name minor the .par suffix).
The provider element allows you to express the Entity Manager implementation you want to use for this persistence unit. The value is defaulted to Hibernate Entity Manager if none is specified. This is a interesting one, this basically means that you can use several Entity Manager implementations in the same application or use the Hibernate Entity Manager implementation in lieu of your vendor EJB3 persistence implementation in a standard way!
The jta-data-source aside with the non-jta-data-source let you specify the datasource the persistence unit will work onto.
The class element, allows you to add explicitly some entities to be mapped. These entities are typically outside of the PAR archive and the Entity Manager will search them in the EAR classpath. This is particularly convenient to be able to share the same entity definition across several persistence unit.
The jar-file element, allows you to ask the entity manager implementation to add all the entities contained in a particular JAR and include them in the configuration. In the case of the Hibernate Entity Manager, it will also look at the hbm.xml files. This is particularly convenient to share a certain amount of entities definitions across several persistence units.
There is also a mapping-file element currently not supported in Hibernate Entity Manager's implementation.
The properties elements is a way to provide some implementation specific properties to your entity manager. In the case of Hibernate you can add most of the hibernate.* properties. You can also define the second level cache informations using hibernate.ejb.classcache.* and hibernate.ejb.collectioncache.*, please refer to the reference documentation for more information.
This is good news for JBoss users, the .har archive is now standardized. The packaging that has always been a strong concept in J2EE is now extended to the ORM world in a very ease of use manner.
Packaging in J2SE environment
The very new point is that the PAR packaging simplicity works in the exact same manner
in the J2SE world. The only difference is that you'll need to define your datasource
not through the jta-data-source element but through the classic hibernate.*
connection properties. The PAR archive is still scanned to find its contained entities and
hbm.xml files. In order to let the Hibernate Entity Manager discover the PAR files,
they need to have a persistence.xml file in the META-INF directory (Hibernate Entity Manager
basically request any resources named META-INF/persistence.xml
and deduces the PAR
archive location from it).
Let's imagine the following acmedomainmodel.par archive structure
com/acme/model/Animal.class (an @Entity annotated class) com/acme/model/Dog.class (an @Entity annotated class) com/acme/model/Cat.class (an @Entity annotated class) com/acme/model/Customer.class (a non annotated POJO) com/acme/model/Customer.hbm.xml (the metadata definitions of Customer) META-INF/persistence.xml
where persistence.xml is
<?xml version="1.0" encoding="UTF-8"?>
<entity-manager>
<properties>
<property name="hibernate.max_fetch_depth" value="4"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="emmanuel"/>
<property name="hibernate.connection.password" value="secret"/>
<property name="hibernate.connection.url" value="[=>jdbc:mysql:///test]"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
</properties>
</entity-manager>
My persistence unit named acmedomainmodel will then contains automatically Animal, Dog, Cat, Customer. Note that the Customer class don't really have to be in the PAR archive, it just need to be in the classpath as long as its hbm.xml definition itself is inside the PAR archive.
Note that you can tune the discovery mechanism through the hibernate.ejb.autodetection. The possible values are none (no auto detection), class (auto detection of the annotated entities), hbm (auto detection of the hbm files) and class,hbm (auto detection of both annotated entities and hbm files).
With a simple ant task you can then create a PAR archive which contains automatically your persistent domain model. No need to manually add the mapped entities inside the hibernate.cfg.xml file anymore.
Several persistent unit in my application
You can of course use several PARs archive in your application. The appropriate PAR archive will be processed based on the name your provide.
//create a keep the emf for later entity manager creations
EntityManagerFactory emf = Persistence.createEntityManagerFactory("acmedomainmodel");
...
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(customer);
wolfy = em.find(Dog.class, wolfyId);
em.getTransaction().commit();
em.close();
Note that if there is only one PAR archive in your classpath, you don't have to pass the name to the createEntityManagerFactory() method, but is considered good practice however.
The PAR archive mechanism offers a very convenient and standard way to package your ORM persistent units. By its autodiscovery mechanism, the packaging setting up scales in a very elegant manner.
|
|
|
Showing 1066 to 1070 of 1130 blog entries |
|
|