Java 1.5 Generic DAO
I am always looking for ways to limit the amount of typing I have to do. Hibernate helps me alot because I don't have to write all the SQL and I can concentrate on the less tedious aspects of designing my persistence model. Spring helps me alot because I don't have to write all those factories and other "glue" code.
In the interests of getting more done quickly, I started looking at how I can automate the generatin of the DAO pattern. Data Access Objects allow the service or session facade layer to be agnostic to the persistence layer used. As I have already said, I use hibernate but there might come a time when I need to use some other persistence layer and by keeping all the persistence layer specific code behind the DAO pattern, I can easily switch it out. I also love the fact that I can use jMock to test my service layer components without fooling around with actually changing database tables.
Back to automating the process of generating DAOs and their implementation. On one of my projects I am using AppFuse and it comes with a nifty tool to generate a full stack of components for managing persisted data. It can generate the classes either from a POJO or a database table. This is really nice but sometimes I don't want all of that. I have tables, and therefore pojos, that are not exposed past the service layer so I don't need the managers, controllers, and views that get generated by this tool. In this case, I really just want the DAO. I could use something like XDoclet, which BTW is what AppFuse's appgen uses, but Java 1.5 has added a new feature called Generics that I have been having lots of fun figuring out how to use. So here is what I did:
Firstly, I defined what I thought was the basic level of interface that all DAOs should provide. Essentially the CRUD methods:
01 package com.dhptech.dao;
02
03 import java.io.Serializable;
04 import java.util.List;
05
06 /**
07 * A generic DAO interface definition. This interface should be extended
08 * even if the new interface will add no additional functions.
09 *
10 * @author Dana P'Simer
11 *
12 * @param <T> The class of the pojo being persisted.
13 * @param <I> the class of the pojo's id property.
14 */
15 public interface GenericDao<T,I extends Serializable> {
16 /**
17 * Get the object with the id specified.
18 *
19 * @param id the id of the instance to retrieve.
20 *
21 * @return the instance with the given id.
22 */
23 T get(I id);
24
25 /**
26 * Get all instances that match the properties that are set in the given
27 * object using a standard Query by Example method.
28 *
29 * @param t the example bean
30 *
31 * @return a list of beans that match the example.
32 */
33 List<T> get(T t);
34
35 /**
36 * Get all instances of this bean that have been persisted.
37 *
38 * @return a list of all instances.
39 */
40 List<T> get();
41
42 /**
43 * Persist the given bean.
44 *
45 * @param t the bean to persist.
46 */
47 void save(T t);
48
49 /**
50 * Remove the bean with the given id.
51 *
52 * @param id the id of the bean to remove.
53 */
54 void remove(I id);
55
56 /**
57 * Remove the bean passed. same as remove(t.<idProoertyGetter>())
58 *
59 * @param t the object to remove.
60 */
61 void remove(T t);
62 }
Here is the generic implementation:
001 /**
002 *
003 */
004 package com.dhptech.dao.hibernate;
005
006 import java.io.Serializable;
007 import java.util.List;
008
009 import org.hibernate.HibernateException;
010 import org.hibernate.Session;
011 import org.hibernate.criterion.Example;
012 import org.hibernate.criterion.MatchMode;
013 import org.springframework.orm.hibernate3.HibernateCallback;
014 import org.springframework.orm.hibernate3.HibernateTemplate;
015
016 import com.dhptech.dao.GenericDao;
017
018 /**
019 * @author danap
020 *
021 */
022 public abstract class GenericDaoHibernate<T,I extends Serializable> implements GenericDao<T,I> {
023 /**
024 * The hibernate template to use.
025 */
026 private HibernateTemplate hibernateTemplate;
027
028 /**
029 * The class of the pojo being persisted.
030 */
031 private Class<? extends T> clazz;
032
033 protected GenericDaoHibernate(Class<? extends T> clazz) {
034 this.clazz = clazz;
035 }
036
037 /**
038 * @return the hibernateTemplate
039 */
040 public HibernateTemplate getHibernateTemplate() {
041 return hibernateTemplate;
042 }
043
044 /**
045 * @param hibernateTemplate the hibernateTemplate to set
046 */
047 public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
048 this.hibernateTemplate = hibernateTemplate;
049 }
050
051 /**
052 * @return the clazz
053 */
054 protected Class<? extends T> getClazz() {
055 return clazz;
056 }
057
058 /**
059 * @param clazz the clazz to set
060 */
061 protected void setClazz(Class<? extends T> clazz) {
062 this.clazz = clazz;
063 }
064
065 /**
066 * @see com.dhptech.dao.GenericDao#get(java.io.Serializable)
067 */
068 @SuppressWarnings("unchecked")
069 public T get(I id) {
070 return (T) getHibernateTemplate().load(clazz, id);
071 }
072
073 /**
074 * @see com.dhptech.dao.GenericDao#get(java.lang.Object)
075 */
076 @SuppressWarnings("unchecked")
077 public List<T> get(final T t) {
078 if (t == null) {
079 return get();
080 } else {
081 // filter on properties set in the customer
082 HibernateCallback callback = new HibernateCallback() {
083 public Object doInHibernate(Session session) throws HibernateException {
084 Example ex = Example.create(t).ignoreCase().enableLike(MatchMode.ANYWHERE);
085 return session.createCriteria(clazz).add(ex).list();
086 }
087 };
088 return (List<T>) getHibernateTemplate().execute(callback);
089 }
090 }
091
092 /**
093 * @see com.dhptech.dao.GenericDao#get()
094 */
095 @SuppressWarnings("unchecked")
096 public List<T> get() {
097 return getHibernateTemplate().loadAll(clazz);
098 }
099
100 /**
101 * @see com.dhptech.dao.GenericDao#remove(java.io.Serializable)
102 */
103 public void remove(I id) {
104 remove(get(id));
105 }
106
107 /**
108 * @see com.dhptech.dao.GenericDao#remove(java.lang.Object)
109 */
110 public void remove(T t) {
111 getHibernateTemplate().delete(t);
112 }
113
114 /**
115 * @see com.dhptech.dao.GenericDao#save(java.lang.Object)
116 */
117 public void save(T t) {
118 getHibernateTemplate().save(t);
119 }
120 }
You will notice that this class is defined abstract and that it takes a Class<? extends T> object as an agument to the constructor. This is necessary because Generics are designed to assure type safeness through compile time checks. The rule is, if you have no "unchecked" warnings then you are type safe. At runtime there is no class specific information available. so you cannot have an expression like "T.class" to get the class of the parameter. Since Hibernate uses the class to lookup the mapping, for operations like the load function, we need to pass this in here. This also forces a caller to define a concrete class that extends this one in order to define the constructor that will pass in the Class object that is required. While one could get away without defining an subclassing interface for this pattern, I think it is a good practice to define both an interface and implemenation that extend these classes.
It would be simple to reimplement this generic implementation for other persistence models. Not sure if it could be done for a JDBC persistence framework but I know it can for Torque or iBatis.
Now all one has to do to use these is to define the specific DAO's interface and extend GenericDao, then create the specific DAO's hibernate implementation by extending the GenericDaoHibernate. Here is an example. Supose you have the following POJO:
01 package com.dhptech.dao.model;
02
03 /**
04 * @author danap
05 *
06 */
07 public class Customer {
08 private Integer id;
09 private Integer version;
10 private String name;
11 private String contactName;
12 private String contactTelNum;
13
14 /**
15 *
16 */
17 public Customer() {
18 // TODO Auto-generated constructor stub
19 }
20
21 /**
22 * @return the id
23 */
24 public Integer getId() {
25 return id;
26 }
27
28 /**
29 * @param id the id to set
30 */
31 public void setId(Integer id) {
32 this.id = id;
33 }
34
35 /**
36 * @return the version
37 */
38 public Integer getVersion() {
39 return version;
40 }
41
42 /**
43 * @param version the version to set
44 */
45 public void setVersion(Integer version) {
46 this.version = version;
47 }
48
49 /**
50 * @return the name
51 */
52 public String getName() {
53 return name;
54 }
55
56 /**
57 * @param name the name to set
58 */
59 public void setName(String name) {
60 this.name = name;
61 }
62
63 /**
64 * @return the contactName
65 */
66 public String getContactName() {
67 return contactName;
68 }
69
70 /**
71 * @param contactName the contactName to set
72 */
73 public void setContactName(String contactName) {
74 this.contactName = contactName;
75 }
76
77 /**
78 * @return the contactTelNum
79 */
80 public String getContactTelNum() {
81 return contactTelNum;
82 }
83
84 /**
85 * @param contactTelNum the contactTelNum to set
86 */
87 public void setContactTelNum(String contactTelNum) {
88 this.contactTelNum = contactTelNum;
89 }
90 }To create a simple DAO for this object you could define the following interface:
01 package com.dhptech.dao;
02
03 import java.util.List;
04
05 import com.dhptech.dao.model.Customer;
06
07 /**
08 * @author danap
09 */
10 public interface CustomerDao extends GenericDao<Customer,Integer> {
11 Customer getCustomerByName(String name);
12 List<Customer> searchCustomersByName(String name);
13 }Now the hibernate implementation:
01 package com.dhptech.dao.hibernate;
02
03 import java.sql.SQLException;
04 import java.util.List;
05
06 import org.hibernate.HibernateException;
07 import org.hibernate.Session;
08 import org.hibernate.criterion.MatchMode;
09 import org.hibernate.criterion.Restrictions;
10 import org.springframework.dao.DataRetrievalFailureException;
11 import org.springframework.orm.hibernate3.HibernateCallback;
12
13 import com.dhptech.dao.CustomerDao;
14 import com.dhptech.dao.model.Customer;
15
16 /**
17 * @author danap
18 *
19 */
20 public class CustomerDaoHibernate extends GenericDaoHibernate<Customer, Integer> implements CustomerDao {
21 /**
22 * The default constructor.
23 */
24 public CustomerDaoHibernate() {
25 super(Customer.class);
26 }
27
28 /**
29 * @see com.dhptech.dao.CustomerDao#getCustomerByName(java.lang.String)
30 */
31 public Customer getCustomerByName(String name) {
32 Customer ex = new Customer();
33 ex.setName(name);
34 List<Customer> matches = get(ex);
35 if ( matches.size() == 0 ) {
36 throw new DataRetrievalFailureException("No customer found with the name, '"+name+"'");
37 } else if ( matches.size() > 1 ) {
38 throw new DataRetrievalFailureException("Too many customers found with the name, '"+name+"'");
39 } else {
40 return matches.get(0);
41 }
42 }
43
44 /**
45 * @see com.dhptech.dao.CustomerDao#searchCustomersByName(java.lang.String)
46 */
47 @SuppressWarnings("unchecked")
48 public List<Customer> searchCustomersByName(final String name) {
49 return (List<Customer>) getHibernateTemplate().execute(new HibernateCallback() {
50 public Object doInHibernate(Session session) throws HibernateException, SQLException {
51 return session
52 .createCriteria(getClazz())
53 .add(Restrictions.like("name", name, MatchMode.ANYWHERE))
54 .list();
55 }
56 });
57 }
58 }
That is it.. Now I can enjoy the persistence layer independence of the DAO pattern without incurring the additional cost of actually coding one for each of my POJOs.
Addendum -- added 10/07/2006
Well it seems I was not the first person to think of this, of course. While I was looking at other implementations, I found that most had followed the basic design I have here. The Class object is passed into the generic base implementation because of the fact that the expression "T.class" is illegal. Well when I was reading hibernate.org's contribution to this pattern ( see "Hibernate's Generic DAO Pattern" ) and I saw some code that I thought was very interesting. It seems that it is possible to find the generic type arguments at runtime and get back a Class object for them. Here is the new constructor for the GenericDaoHibernate class:
01 /**
02 * The class of the pojo being persisted.
03 */
04 private Class<T> clazz;
05
06 protected GenericDaoHibernate() {
07 this.clazz = (Class<T>)
08 ((ParameterizedType)getClass().getGenericSuperclass())
09 .getActualTypeArguments()[0];
10 }
- danapsimer's blog
- 4588 reads


























Thanking You
Dear,
This is a very useful one. Well written. Thanks & need more....
Thanks,
Uditha Madumal
موقع تحميل صور - موقع رفع صور
موقع تحميل صور - موقع رفع صور - e-pro - موقع تحميل الصور
مسجات -رسائل وسائط -مسجات وسائط - وسائط رسائل - مسجات حب -مسج - رسائل حب - رسائل الحب - رسايل حب - مسجات الحب - مسجات عتاب - مسجات غزل - مسجات رومانسية - مسجات عشق - مسجات عراقية - مسجات جوال - مسجات موبايل - مسجات الجوال - مسجات غرام - مسجات حلوة - مسجات شوق - مسجات قهر - مسجات صباحيه - رسائل قصيرة بالفرنسية - رسائل حب بالفرنسية - مسجات
Post new comment