Skip to content

The dao framework

zenbones edited this page Jan 27, 2012 · 8 revisions

The DAO Framework

If you've never used a DAO framework, the DAO stands for Domain Access Object (or Data Access Object, depending upon who you ask). We use a DAO per class setup, meaning that we generate one accessor class for each Entity (a java class that we want to persist into the database). The naming scheme is simple. If I have a Java class Foo that I wish to make persistent, I'll...

  1. Annotate my Java class Foo in order to turn it into an Entity.
  2. Create an interface called FooDao in which I'll define my special 'finder' methods, which are the special ways I'll provide for finding particular instances of Foo, or collections of Foo objects.
  3. Implement those finder methods in an ORM (Object Relational Mapping) specific way. The ORM we use is Hibernate, so my DAO implementation will be FooHibernateDao.
  4. Create a Spring bean that instantiates my DAO implementation. This allows Spring to wire my Entity into Hibernate without having to do anything else.
  5. Write some code that gets my DAO implementation from Spring, and uses my finder methods. Each use of the database should be annotated to tell the database management system how it should handle any sessions or transactions, via the @NonTransaction or @Transactional annotations.

There's a standard location to place your Entity related code in each module...

src/main/java/com/<project>/<module>
                                       |- data
                                              | - Foo.java
                                              | - dao
                                                     |- FooDao.java
                                                     |- hibernate
                                                                 |- FooHibernateDao.java

Let's go through each step now in more detail...

Turning a Java Class into an Entity

To turn your Java class into an Entity, it should extend the following abstract class...

  • org.smallmind.persistence.Durable - Properly handles the ORM id field.

...and have a few basic annotations, as in...

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name = "table name")
public class Foo extends Durable<Long> {

   // Hiberate requires a no arg constructor, which can be private
   private Foo() {
   }

...

}

If you know what you're doing, you may wish to alter the inheritance strategy in certain cases.

Creating a Dao Interface

This is as simple as extending the right class and using generics to tell the framework what Entity it's dealing with, and what the class of the id field will be. As in...

import org.smallmind.persistence.orm.ORMDao;

public interface FooDao extends ORMDao<Long, Foo> {

... 

}

The rest of the interface is filled with your finder methods. Don't worry about the basics like get(), persist(), delete(), list(), scroll(), etc. These are already provided by the base classes you're extending.

Creating a HibernateDao Implementation

The code below is a typical DAO implementation 'stub'. If you copy it and change the class of the id field if necessary (we've assumed a Long id below), and 'Foo' to whatever your Entity class is, this should serve almost all your needs. You'll need to get the data source name from the Spring wiring you create (see below), or the wiring that's already been created for the database schema you're using. Yes, the HibernateProxySession taken by the constructor already has this name, but providing it in the DAO implementation class, via that @DataSource annotation, lets us do a bunch of Spring setup for you, during wiring, based on meta-data alone. The meat of the class will be ORM specific implementations of finder methods for your DAO interface.

import org.smallmind.persistence.orm.DataSource;
import org.smallmind.persistence.orm.hibernate.HibernateDao;
import org.smallmind.persistence.orm.hibernate.HibernateProxySession;

@DataSource("data source name wired into spring")
public class FooHibernateDao extends HibernateDao<Long, Foo> implements FooDao {

   public FooHibernateDao(HibernateProxySession proxySession) {

      super(proxySession);
   }
   ...
}

Wiring Up Spring

If you're starting a new database, then you'll need to create properties and Spring wiring for your new data source, which we'll cover under Data Sources in this book. If this has already been done, then just look for the appropriate Spring file to add your DAO implementation to. It'll be in the usual place, in the module's Spring files, named <data source name>-orm.xml. It should look something like this...

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <import resource="classpath:com/<project>/<data source name>/<data source name>-hibernate.xml"/>

    <!-- Dao Classes -->
    <bean id="fooDao" class="com.<project>.<module>.data.dao.hibernate.FooHibernateDao" init-method="register">
        <constructor-arg index="0" ref="<data source name>ProxySession"/>
    </bean>

</beans>

This wiring imports the wiring file for the data source itself, instantiates the DAO implementation you created and registers it with the framework code, passing in an instance of the session factory provided by the data source wiring file. Just use the code snippet for the fooDao bean as a template, changing the 'id' and 'class' as necessary for your Entities.

Writing Database Code

As mentioned, we use a DAO per class framework. Each DAO implementation manages a single Entity class. In which DAO does a particular finder method belong? What Entity class does it return? If the finder returns Entities of type Foo, then that method goes in FooDao. It doesn't matter what parameters the finder method takes, only its return type. It's that simple. Where do I look for the code which returns instances of Foo? In the implementation of FooDao. Easy.

Of course, persist, update, and delete operations on an Entity should be executed upon the correct DAO type for that Entity as well. Sure, the underlying code, if you get a few layers down, doesn't care what the particular class of an Entity happens to be, but the DAO for an instance of an Entity is where the override code will be that handles any extra operations for that Entity type, and you'll miss all that if you use the wrong DAO.

So, how do you go about creating and using these DAO methods?

Creating an Entity

A newly created Entity is not associated with the ORM framework. In order for that instance to be inserted into the database it must be associated with the underlying ORM system. An instance of a persistent Entity, taken from the database, which has been severed from the database session in which it was retrieved, must be re-associated with a session before changes to that object will be persisted into the database. In both cases the method to use is simply persist(). Persist returns an copy of the object which is now associated with the current database session, and any changes to that object will be pushed into the database upon commit of the current transaction. It's important to note that persist() returns a copy of the object. Changes to the original object will be ignored. Therefore, the proper way to use persist is like this...

import org.smallmind.persistence.orm.aop.Transactional;

@Transactional
public void something () {

   Foo foo = new Foo();
   foo = fooDao.persist(foo);
}

For the love you bear your fellows, always replace the instance of the Entity you're operating upon with the persistent copy, as in the code above. It's perfectly safe, by the way, to operate this way on an already persistent instance, because you'll just get the same instance back, and no harm will be done.

Note the @Transactional annotation. That means that this method will be wrapped in a transaction. The termination and commit of that transaction will be handled for you. It's fine to operate on multiple databases within the same @Transactional annotation. It's fine to call other methods which are also annotated for transactions. The transaction will close when the outermost annotated method returns. If an exception is thrown, any kind of exception, the transaction will be rolled back. This is not the usual Spring behavior, because this is not the usual Spring annotation. This version is from the open source SmallMind project (as are a few other classes we're leveraging).

Updating Entities

Once an entity has been associated with a database session, either because it was retrieved from the database within the current session, or because it was persisted within the current session, then any changes to that object will be reflected in the database upon transaction commit. of course, you must have a transaction for this to work, so you need to be within an @Transactional annotation.

However, if you're planning on integrating caching by annotation (see Caching By Annotation) then you can never by sure whether an entity has been returned via the ORM, or from the cache, and so it's safest to go through the bother of persisting any entity to which changes have been made, somewhere within an @Transactional boundary.

import org.smallmind.persistence.orm.aop.Transactional;

@Transactional
public void something () {

   Foo foo = fooDao.get(1234);
   foo.setBar(new Bar("Hello"));
   foo = fooDao.persist(foo);
}

Deleting Entities

The easiest way to delete an Entity is to be within a transaction and then to call the delete() method of that Entity's DAO.

import org.smallmind.persistence.orm.aop.Transactional;

@Transactional
public void something () {

   fooDao.delete(foo);
}

Finding Entities

For Hibernate DAO implementations, there are 3 basic ways of getting objects from the database, and 3 different query types you can use. They're all supported by the DAO framework. The 3 retrieval methods are...

  1. find - Returns a single instance of the managed type
  2. list - Returns a List of the managed type, which means that all instances matching the query will be hauled into memory, shoved into a List, and handed back.
  3. scroll - Returns an Iterable of the managed type, which means that only a few instances will be instantiated at one time (you can control the number), but a cursor will be open on the database while the iteration is in operation.

The 3 query types supported are...

  1. Criteria - Build a query programatically (this is fairly cool and useful stuff)
  2. HQL - Like SQL only object oriented
  3. SQL - Just what it sounds like, but should be avoided because it bypasses the ORM mappings which insulate the programmer from database names and database irregularities

All of this is supported by the Hibernate implementation of the DAO framework with methods which combine a retrieval methodology with a query type, as in findByCriteria, listByQuery, or scrollBySQLQuery, or any of the other combinations like scrollByCriteria. Each of these methods takes a related implementation of an abstract class, called CriteriaDetails, QueryDetails, or SQLQueryDetails. The implementations of these abstract classes will construct and hand back the correct query type, and their return values will be the correct result of those queries. The way you do this is via an implementation of an anonymous inner class in your DAO implementation, as in...

import org.smallmind.persistence.orm.hibernate.QueryDetails;

...

public Iterable<Foo> scrollFooForBar (final Bar bar) {

   return scrollByQuery(new QueryDetails() {

      @Override
      public String getQueryString() {

         return "from Foo foo where foo.bar = :bar";
      }

      @Override
      public Query completeQuery(Query query) {

         return query.setParameter("bar", bar).setFetchSize(100);
      }
   });
}

Just put in an anonymous implementation of either CriteriaDetails, QueryDteails, or SQLQueryDetails, to a matching DAO method, and the requirements of that implementation will pretty much force you to do the right thing. The code calling this finder method might look like this...

import org.smallmind.persistence.orm.aop.NonTransactional;

@NonTransactional
public void something () {

   for (Foo foo : fooDao.scrollFooForBar(bar)) {
      System.out.println(foo);
   }
}

Once again, note the @NonTransactional annotation. In this case, no changes are being made to the database (no writes, only reads), so we don't need a transaction, and it's a good optimization not to get one if we don't need it. In this case, @NonTransactional is there to track the session, without a transaction, and make sure the session is closed appropriately (returning the database connection) when no longer needed. You don't put @NonTransactional nor @Transactional annotations on methods in the DAO implementation itself. Code using the DAOs should be correctly annotated, because it's that code that knows what its session and transaction usage is going to be.

Getting Entities

If you're looking for a particular instance of an Entity, and you know what that Entity's id is, you can use the get() method on the appropriate DAO implementation to pull it directly from the database without a query, as in...

import org.smallmind.persistence.orm.aop.NonTransactional;

@NonTransactional
public void something () {

   Foo foo = fooDao.get(537221L);
}

Clone this wiki locally