Mittwoch, 5. Oktober 2011

Generic DAO and JPA

Generic DAO is a design pattern to reduce and prevent code replacing on basic CRUD operations. Hibernate-Generic-DAO (http://code.google.com/p/hibernate-generic-dao/) provides exactly this pattern solution for Hibernate or JPA.

UML Overview of this example:

BaseDAO is an extension of GenericDAO and it must be extended by each DAO class of the project.

JPAGenericDAO is a generic, abstract class to be extended by each concrete DAO class.

Sample code:
  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. public abstract class JPAGenericDAO<T, ID extends Serializable> extends GenericDAOImpl<T, ID> implements
  7.         BaseDAO<T, ID> {
  8.     private static final Logger logger = LoggerFactory.getLogger(JPAGenericDAO.class);
  9.    
  10.     @PersistenceContext(unitName="scheduling")
  11.     private EntityManager entityManager;
  12.    
  13.     public JPAGenericDAO(){
  14.         super();
  15.     }
  16.    
  17.     @PostConstruct
  18.     public void initialized(){
  19.         logger.info( "initializing generic dao, setting entity manager: {}", entityManager);
  20.         this.setEntityManager(entityManager);
  21.         JPASearchProcessor searchProcessor = new JPASearchProcessor(new JPAAnnotationMetadataUtil());
  22.         this.setSearchProcessor(searchProcessor);
  23.     }
  24.     @Override
  25.     public T findByExampleUnique(T example) {
  26.         Search s = this.createSearch(example);
  27.         return searchUnique(s);
  28.     }
  29.     @Override
  30.     public Collection<T> findByExample(T example) {
  31.         Search s = this.createSearch(example);
  32.         return search(s);
  33.     }
  34.    
  35.     protected Search createSearch(T example) {
  36.         Search s = new Search(example.getClass());
  37.         s.addFilter(this._getFilterFromExample(example));
  38.         return s;
  39.     }
  40. }

UserDAO extends the BaseDAO :

  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. public interface UserDAO extends BaseDAO<User, Long> {
  7. }


and a sample reference implementation of DAOs :

  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. @Singleton
  7. public class JPAUserDAO extends JPAGenericDAO<User, Long> implements UserDAO {
  8.     public JPAUserDAO(){
  9.         super();
  10.     }
  11. }


We keep basic CRUD operations in our abstract class but we have also seperate interfaces to provide 'Model' specific method and keep them seperate from generic DAO solutions.

Dienstag, 4. Oktober 2011

Testing with Arquillian and Embedded Glassfish 3.1 - Part 2

We are configured our project to create and run tests with Arquillian. Here i will explain how to create a simple test using Arquillian and JPA.

We have created a data source with JNDI-name "jdbc/sample" in part 1, we will use it for our JPA test, i have used an API to make generic DAOs and hibernate-entity-manager as an entitity manager, needed dependencies therefore :

  1. <dependency>
  2.     <groupId>org.hibernate</groupId>
  3.     <artifactId>hibernate-entitymanager</artifactId>
  4.     <version>3.6.7.Final</version>
  5.     <exclusions>
  6.         <exclusion>
  7.             <artifactId>slf4j-api</artifactId>
  8.             <groupId>org.slf4j</groupId>
  9.         </exclusion>
  10.     </exclusions>
  11. </dependency>
  12. <dependency>
  13.     <groupId>org.hibernate</groupId>
  14.     <artifactId>hibernate-core</artifactId>
  15.     <version>3.6.7.Final</version>
  16.     <exclusions>
  17.         <exclusion>
  18.             <artifactId>slf4j-api</artifactId>
  19.             <groupId>org.slf4j</groupId>
  20.         </exclusion>
  21.     </exclusions>
  22. </dependency>
  23. <!-- generic DAO dependencies -->
  24. <!-- This first dependency includes all the JPA implementations for the DAOs -->
  25. <dependency>
  26.     <groupId>com.googlecode.genericdao</groupId>
  27.     <artifactId>dao</artifactId>
  28.     <version>1.0.0</version>
  29. </dependency>
  30. <!-- This second one includes the Hibernate Entity Manager plugin for the framework -->
  31. <dependency>
  32.     <groupId>com.googlecode.genericdao</groupId>
  33.     <artifactId>search-jpa-hibernate</artifactId>
  34.     <version>1.0.0</version>
  35. </dependency>
  36. <!-- end of generic DAO dependencies -->

I've created a BaseDAO interface and then the a specific interface to provide CRUD operations for users.

  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. public interface BaseDAO<T, ID extends Serializable> extends GenericDAO<T, ID> {
  7.     public Collection<T> findByExample(T example);
  8.    
  9.     public T findByExampleUnique(T example);
  10. }

  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. public interface UserDAO extends BaseDAO<User, Long> {
  7. }

And an abstract class for JPA implementations :

  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. public abstract class JPAGenericDAO<T, ID extends Serializable> extends GenericDAOImpl<T, ID> implements
  7.         BaseDAO<T, ID> {
  8.     private static final Logger logger = LoggerFactory.getLogger(JPAGenericDAO.class);
  9.    
  10.     @PersistenceContext(unitName="sample")
  11.     private EntityManager entityManager;
  12.    
  13.     public JPAGenericDAO(){
  14.         super();
  15.     }
  16.    
  17.     @PostConstruct
  18.     public void initialized(){
  19.          this.setEntityManager(entityManager);
  20.         JPASearchProcessor searchProcessor = new JPASearchProcessor(new JPAAnnotationMetadataUtil());
  21.         this.setSearchProcessor(searchProcessor);
  22.     }
  23.     @Override
  24.     public T findByExampleUnique(T example) {
  25.         Search s = this.createSearch(example);
  26.         return searchUnique(s);
  27.     }
  28.     @Override
  29.     public Collection<T> findByExample(T example) {
  30.         Search s = this.createSearch(example);
  31.         return search(s);
  32.     }
  33.    
  34.     protected Search createSearch(T example) {
  35.         Search s = new Search(example.getClass());
  36.         s.addFilter(this._getFilterFromExample(example));
  37.         return s;
  38.     }
  39. }

As you remember we are created a persistence-unit named "sample" while injecting the entity-manager at this point, we are used this unit for our DAOs :
  1. @PersistenceContext(unitName="sample")
  2. private EntityManager entityManager
And a reference implementation for our JPA DAO :


  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. @Singleton
  7. public class JPAUserDAO extends JPAGenericDAO<User, Long> implements UserDAO {
  8.     public JPAUserDAO(){
  9.         super();
  10.     }
  11. }

We are now ready to implement an Arquillian Test for our JPA class.The most important section to create a static method annotated with @Deployment to build a ShrinkWrap, here is a sample method:

  1. /**
  2. * Since Arquillian actually creates JAR files under the covers with
  3. * ShrinkWrap, the @Deployment is your way of controlling what is included
  4. * in that Archive. Note, each class utilized in your test case - whether
  5. * directly or indirectly - must be added to the deployment archive.
  6. */
  7. @Deployment
  8. public static Archive<?> createTestArchive() {
  9.     return ShrinkWrap
  10.         .create(JavaArchive.class, "test.jar")
  11.         .addAsResource("arquillian.xml")
  12.         .addPackages(true, UserDAO.class.getPackage())
  13.         .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
  14.         .addAsManifestResource("META-INF/persistence.xml",
  15.         ArchivePaths.create("persistence.xml"));
  16. }

We must add our arquillian configuration too, otherwise ShrinkWrap reads the default configuration and ignores our configuration.

I've created an abstract class which is extended by all test-classes :


  1. /**
  2.  *
  3.  * @author Hasan Oezdemir
  4.  * @since 02.10.2011
  5.  *
  6.  */
  7. public class AbstractJPATest {
  8.     /**
  9.      * Since Arquillian actually creates JAR files under the covers with
  10.      * ShrinkWrap, the @Deployment is your way of controlling what is included
  11.      * in that Archive. Note, each class utilized in your test case - whether
  12.      * directly or indirectly - must be added to the deployment archive.
  13.      */
  14.     @Deployment
  15.     public static Archive<?> createTestArchive() {
  16.         return ShrinkWrap
  17.                 .create(JavaArchive.class, "test.jar")
  18.                 .addAsResource("arquillian.xml")
  19.                 .addAsResource("log4j.xml")
  20.                 .addPackages(true, UserDAO.class.getPackage())
  21.                 .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
  22.                 .addAsManifestResource("META-INF/persistence.xml",
  23.                         ArchivePaths.create("persistence.xml"));
  24.     }
  25.     @Inject
  26.     protected UserTransaction userTransaction;
  27.     public AbstractJPATest() {
  28.         super();
  29.     }
  30.     /**
  31.      * @throws java.lang.Exception
  32.      */
  33.     @Before
  34.     public void setUp() throws Exception {
  35.         this.userTransaction.begin();
  36.     }
  37.     /**
  38.      * @throws java.lang.Exception
  39.      */
  40.     @After
  41.     public void tearDown() throws Exception {
  42.         try {
  43.             this.userTransaction.rollback();
  44.         } catch (Exception e) {
  45.             throw new Exception(e);
  46.         }
  47.     }
  48.     /**
  49.      * @return a test user.
  50.      */
  51.     protected User createUser() {
  52.         User user = new User();
  53.         user.setEmail("hasan@hotmail.com");
  54.         user.setName("hasan");
  55.         user.setPassword("12345");
  56.         return user;
  57.     }
  58. }

Transaction management may be better but this is enought for now :)

We must run our tests with Arquillian.class therefore we should add @RunWith(Arquillian.class) for each our tests-classes, here is a sample :


  1. /**
  2.  * @author Hasan Oezdemir
  3.  * @since 01.10.2011
  4.  *
  5.  */
  6. @RunWith(Arquillian.class)
  7. public class JPAUserDAOTest extends AbstractJPATest {
  8.    
  9.     @Inject
  10.     private JPAUserDAO jpaUserDAO;
  11.     /**
  12.      * Test method for
  13.      * {@link com.citecompany.dao.jpa.JPAGenericDAO#findByExampleUnique(java.lang.Object)}
  14.      * .
  15.      */
  16.     @Test
  17.     public void testFindByExampleUnique() {
  18.         User user = this.createUser();
  19.         this.jpaUserDAO.save(user);
  20.         User found = this.jpaUserDAO.findByExampleUnique(user);
  21.         assertNotNull("Expected user " + user + " is not found in data source.", found);
  22.         assertEquals(user.getName(), found.getName());
  23.     }
  24. }

So our test class is ready to start, you start it either with maven or in eclipse.