Announcement Announcement Module
Collapse
No announcement yet.
Elegant design for a multi database servers DAO bean configuration Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Elegant design for a multi database servers DAO bean configuration

    Hello,

    I have a Spring based DAO with an xml file that instantiates all the DAO like:
    Code:
    	<bean id="addressDao"
    		class="com.thalasoft.learnintouch.core.dao.hibernate.AddressHibernateDao">
    		<property name="sessionFactory" ref="sessionFactory" />
    	</bean>
    The file spring-hibernate-dao.xml contains more than a hundred of such bean definitions, one for each table of the schema.

    This has worked fine when the DAO layer was targeting only one database server, namely, the MySql database server.

    But the DAO layer must now also target the Oracle database server.

    As it happens, this database server has an SQL syntax that is not exactly compatible with the other one, even if using Hibernate.

    Therefore some Oracle database server specific DAO beans are required to instantiate some DAO with the Oracle SQL syntax.

    There is an Oracle custom DAO:
    Code:
    	<bean id="navbarLanguageCustomDao"
    		class="com.thalasoft.learnintouch.core.dao.oracle.hibernate.NavbarLanguageCustomHibernateDao">
    		<property name="sessionFactory" ref="sessionFactory" />
    	</bean>
    At first, I thought about injecting into the original DAO, the Oracle custom DAO when needed, with an autowired injected custom DAO like:
    Code:
    	@Autowired(required=false)
    	private NavbarLanguageCustomDao navbarLanguageCustomDao;
    
    	public void setNavbarLanguageCustomDao(NavbarLanguageCustomDao navbarLanguageCustomDao) {
    		this.navbarLanguageCustomDao = navbarLanguageCustomDao;
    	}
    
    	public List<NavbarLanguage> findWithNavbar(Navbar navbar) {
    		if (navbarLanguageCustomDao != null) {
    			return navbarLanguageCustomDao.findWithNavbar(navbar);
    		} else {
    			Criteria criteria = getSession().createCriteria(getPersistentClass());
    			criteria.add(Restrictions.eq("navbar", navbar)).addOrder(Order.asc("languageCode"));
    			return criteria.list();
    		}
    	}
    But this becomes problematic if a third database server is later targeted, as it may not implement the same set of DAO methods, resulting in the call to a non implemented method.

    The inheritance mechanism does not have this potential problem. It would be simple to have the custom DAO inherit from the original one.

    But the DAO beans being defined need to have one constant id whatever the database server

    Because the DAO beans are injected in an integration test suite, I cannot have beans with varying ids. For example I cannot have for the MySql database server a bean with the id navbarLanguageDao and for the Oracle database server a bean with the id navbarLanguageOracleDao.

    This means loading one xml bean configuration file for each database server. This is easly done with a Maven profile.

    But then, each of these files would have lots of copy pasted bean definitions, for all those beans that happen to be compatible DAOs and don't require any custom DAO.

    I wonder if there would be away to avoid this copy paste of so many DAO bean definitions.

    I know about bean inheritance, but this does not seem to offer a reusable id attribute, or does it ?

    What would be nice would be to have one bean definition overwriting another one, with the two beans having the same id, and only the child one being seen. Is that feasible ?
    Last edited by stephaneeybert; Nov 29th, 2012, 11:51 AM.

  • #2
    I'm having trouble inheriting from the DAO base class.

    I have the following custom DAO class:
    Code:
    @Repository
    @Transactional
    public class UserCustomHibernateDao extends UserHibernateDao {
    
    	@Override
    	public Page<User> findWithCreationDateTime(LocalDateTime fromDateTime, LocalDateTime toDateTime, final int pageNumber, final int pageSize) {
    		Criteria criteria = getSession().createCriteria(getPersistentClass());
    		criteria.add(Restrictions.sqlRestriction("formatdatetime(creation_datetime, 'yyyy/MM/dd') between formatdatetime('" + fromDateTime + "', 'yyyy/MM/dd') and formatdatetime('" + toDateTime + "', 'yyyy/MM/dd')"));
    		criteria.addOrder(Order.asc("firstname")).addOrder(Order.asc("lastname")).addOrder(Order.asc("email"));
    		Page<User> page = getPage(pageNumber, pageSize, criteria);
    		return page;
    	}
    
    }
    But running the integration test gives me the following exception:
    Caused by: org.springframework.beans.BeanInstantiationExcepti on: Could not instantiate bean class [com.thalasoft.learnintouch.core.dao.h2.hibernate.M ailCustomHibernateDao]: Constructor threw exception; nested exception is java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at org.springframework.beans.BeanUtils.instantiateCla ss(BeanUtils.java:162)
    at org.springframework.beans.factory.support.SimpleIn stantiationStrategy.instantiate(SimpleInstantiatio nStrategy.java:87)
    at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.instantiateBean(Abstrac tAutowireCapableBeanFactory.java:990)
    ... 45 more
    Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at com.thalasoft.learnintouch.core.dao.hibernate.Gene ricHibernateDao.<init>(GenericHibernateDao.java:41 )
    at com.thalasoft.learnintouch.core.dao.hibernate.Mail HibernateDao.<init>(MailHibernateDao.java:26)
    at com.thalasoft.learnintouch.core.dao.h2.hibernate.M ailCustomHibernateDao.<init>(MailCustomHibernateDa o.java:12)
    at sun.reflect.NativeConstructorAccessorImpl.newInsta nce0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInsta nce(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newI nstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Construc tor.java:513)
    at org.springframework.beans.BeanUtils.instantiateCla ss(BeanUtils.java:147)
    ... 47 more
    Here is the DAO base class:
    Code:
    @Repository
    @Transactional
    public class UserHibernateDao extends GenericHibernateDao<User, Serializable> implements UserDao {
    
    	@Override
    	public Page<User> findWithCreationDateTime(LocalDateTime fromDateTime, LocalDateTime toDateTime, final int pageNumber, final int pageSize) {
    		Criteria criteria = getSession().createCriteria(getPersistentClass());
    		criteria.add(Restrictions.sqlRestriction("DATE(creation_datetime) between DATE('" + fromDateTime + "') and DATE('" + toDateTime + "')"));
    		criteria.addOrder(Order.asc("firstname")).addOrder(Order.asc("lastname")).addOrder(Order.asc("email"));
    		Page<User> page = getPage(pageNumber, pageSize, criteria);
    		return page;
    	}
    And the generic DAO base class:
    Code:
    @Repository
    @Transactional
    public abstract class GenericHibernateDao<T, ID extends Serializable> implements GenericDao<T, ID> {
    
    	private static Logger logger = LoggerFactory.getLogger(GenericHibernateDao.class);
    
    	private Class<T> persistentClass;
    	private SessionFactory sessionFactory;
    
    	@SuppressWarnings("unchecked")
    	public GenericHibernateDao() {
    		this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    	}

    Comment


    • #3
      I could solve this by using TypeResolver class of Jonathan Halterman at https://github.com/jhalterman/typetools

      Here is how my code looks now:

      Class<?>[] typeArguments = TypeResolver.resolveArguments(getClass(), GenericDao.class);
      this.persistentClass = (Class<T>) typeArguments[0];

      Comment

      Working...
      X