duminică, 17 aprilie 2011

JPA with Google Guice

Intro

This post will demonstrate how to configure Google Guice with JPA.

Development environment
  • Eclipse Helios for JEE programmers
  • Maven plugin- http://m2eclipse.sonatype.org/sites/m2e
Setup project

Create a new Maven project and select the archetype with artifact jpa-maven-archetype.
Now with all this done let's move on to have some fun.

Edit pom.xml file
  • Remove junit dependency and add the latest junit release, in my case 4.8.2 and set the scope to "test"
  • Remove hibernate-entitymanager and geronimo-spec-jta and add hibernate-entitymanager version 3.4.0.GA and set the type to jar
  • Add hibernate-core version 3.3.2.GA and set the type to jar
  • Add guice version 3.0
  • Add guice-persist version 3.0
Because all the settings are done we can proceed to next step.

JPA layer

In JPA layer will be create 2 simple entities: User and Role with a many to many relation between them.
Both entities will inherit the AbstractEntity class. We create AbstractEntity class because maybe not all entities will use the same type for the ID and we want to create generic DAO classes.

AbstractEntity class:

public abstract class AbstractEntity
<P>
 implements Serializable {
 public abstract P getId();
}

User class:
@Entity
@Table(name = "demo_user")
@NamedQueries({@NamedQuery(name="User.GetAll", query = "from User")})
public class User extends AbstractEntity
<Long>
 {

 @Id
 @SequenceGenerator(name = "USER_ID_GENERATOR", sequenceName = "SEQ_USER")
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_ID_GENERATOR")
 private Long id;
 
 private String password;
 
 private Boolean status;

 private String username;
 
 @ManyToMany(fetch = FetchType.LAZY)
 @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { @JoinColumn(name = "role_id") })
 private List<Role> roles;
 /**
  * 
  */
 public User() {
  
 }


 @Override
 public Long getId() {
  return id;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public String getPassword() {
  return password;
 }

 public void setStatus(Boolean status) {
  this.status = status;
 }

 public Boolean getStatus() {
  return status;
 }

 public void setUsername(String username) {
  this.username = username;
 }

 public String getUsername() {
  return username;
 }

 public void setRoles(List<Role> roles) {
  this.roles = roles;
 }

 public List<Role> getRoles() {
  return roles;
 }

}


Create an interface called GenericDAO:
public interface GenericDAO<E extends AbstractEntity, P>
{
 /**
  * Persist the indicated entity to database
  * @param entity
  * @return the primary key
  */
 P insert(E entity);
 
 /**
  * Retrieve an object using indicated ID
  * @param id
  * @return
  */
 E find(P id);

 /**
  * Update indicated entity to database
  * @param entity
  */
 void update(E entity);
 
 /**
  * Delete indicated entity from database
  * @param entity
  */
 void delete(E entity);
 
 /**
  * Return the entity class
  * @return
  */
 Class<E> getEntityClass();
 
 /**
  * Get the entity manager
  * @return
  */
 EntityManager getEntityManager();
 
 /**
  * 
  * @return
  */
 List<E> findAll();
}

Implement the interface in GenericDAOImpl class:

public abstract class GenericDAOImpl<E extends AbstractEntity, P> implements
  GenericDAO<E, P> {
 @Inject
 private EntityManager entityManager;
 private Class<E> entityClass;

 @SuppressWarnings("unchecked")
 public P insert(E entity) {
  entityManager.persist(entity);
  return (P)entity.getId();
 }

 public E find(P id) {
  return entityManager.find(getEntityClass(), id);
 }

 public void update(E entity) {
  entityManager.merge(entity);
 }

 public void delete(E entity) {
  entityManager.remove(entity);
 }

 public EntityManager getEntityManager() {
  return entityManager;
 }

 @SuppressWarnings("unchecked")
 public Class<E> getEntityClass() {
  if (entityClass == null) {
   Type type = getClass().getGenericSuperclass();
   if (type instanceof ParameterizedType) {
    ParameterizedType paramType = (ParameterizedType) type;

    entityClass = (Class<E>) paramType.getActualTypeArguments()[0];

   } else {
    throw new IllegalArgumentException(
      "Could not guess entity class by reflection");
   }
  }
  return entityClass;
 }
 
 @SuppressWarnings("unchecked")
 @Override
 public List<E> findAll() {
  return this.entityManager.createNamedQuery(getEntityClass().getSimpleName()+".GetAll").getResultList();
 }
    
}

Obs: I haven't used @Transactional annotation, because I prefer to use this on specific methods where I am making multiple database operations.

Extend GenericDAOImpl in UserDAO and RoleDAO classes.

Create the Guice persistence module

Create a class called JPAInitializer in order to initialize the persistence service when is necessary:

@Singleton
public class JPAInitializer
{

 @Inject
 public JPAInitializer(final PersistService service)
 {
  service.start();
 }
}

Create PersistenceModule class and configure the bindings inside of configure method:

public class PersistenceModule extends AbstractModule {

 /* (non-Javadoc)
  * @see com.google.inject.AbstractModule#configure()
  */
 @Override
 protected void configure() {
  install(new JpaPersistModule("demo-jpa-test"));
  bind(JPAInitializer.class).asEagerSingleton();
  bind(new TypeLiteral<GenericDAO<User, Long>>() {
  }).to(new TypeLiteral<UserDAO>() {
  });
  bind(new TypeLiteral<GenericDAO<Role, Long>>() {
  }).to(new TypeLiteral<RoleDAO>() {
  });

 }

}

Test the DAO's

Because testing is very important below can be found UserDAOTest that will demonstrate how to test this implementation:
public class UserDAOTest {
 
 @Inject
 GenericDAO<User, Long> dao;

 @Before
 public void setUp() throws Exception
 {
  Injector injector = Guice.createInjector(new PersistenceModule());
  injector.injectMembers(this);
  dao.getEntityManager().getTransaction().begin();
 }

 /**
  * @throws java.lang.Exception
  */
 @After
 public void tearDown() throws Exception
 {
  dao.getEntityManager().getTransaction().rollback();
 }
 
 @Test
 public void testInsert() {
  User user = new User();
  user.setUsername("user1");
  user.setPassword("password");
  user.setStatus(Boolean.TRUE);
  dao.insert(user);
  assertNotNull(user.getId());
 }
 
 @Test
 public void testFindsetUsername("user1");
  user.setPassword("password");
  user.setStatus(Boolean.TRUE);
  Long id = dao.insert(user);
  assertNotNull(dao.find(id));
 }

 @Test
 public void testUpdate() {
  User user = new User();
  user.setUsername("user1");
  user.setPassword("password");
  user.setStatus(Boolean.TRUE);
  Long id = dao.insert(user);
  user.setUsername("user2");
  User user2 = dao.find(id);
  assertEquals("user2", user2.getUsername());
 }


 @Test
 public void testDelete() {
  User user = new User();
  user.setUsername("user1");
  user.setPassword("password");
  user.setStatus(Boolean.TRUE);
  Long id = dao.insert(user);
  dao.delete(user);
  assertNull(dao.find(id));
 }


 @Test
 public void testGetEntityManager() {
  assertNotNull(dao.getEntityManager());
 }


 @Test
 public void testGetEntityClass() {
  assertEquals(User.class, dao.getEntityClass());
 }


 @Test
 public void testFindAll() {
  User user = new User();
  user.setUsername("user1");
  user.setPassword("password");
  user.setStatus(Boolean.TRUE);
  dao.insert(user);
 
  User user2 = new User();
  user2.setUsername("user2");
  user2.setPassword("password");
  user2.setStatus(Boolean.TRUE);
  dao.insert(user2);
  
  assertEquals(2, dao.findAll().size());
 }

}

Summary

More documentation and tutorials about Google Guice you can find at this url http://code.google.com/p/google-guice/ and about JPA at this URL http://www.oracle.com/technetwork/articles/javaee/jpa-137156.html.
That's all!

Un comentariu: