For starters I knew I wanted the standard generic DAO pattern which in my case I will be referring to as JPADataServices for clarity in this project which uses several data store types and accessor patterns.
The generic DAO pattern looks like this.
/** * Generic entity methods * */ public interface JPADataService<K, T> { /** * Returns the entity matching the given id * @param id the id * @return the entity */ public T get(K id); /** * Returns all entities * @return List of entities */ public List<T> getAll(); ... }
/** * basic implementation for all entities * */ public abstract class JPADataServiceImpl<K, T> implements JPADataService<K, T> { protected Class<T> entityKey; protected Class<T> entityClass; protected EntityManager em; protected static Logger LOGGER = LoggerFactory.getLogger(JPADataServiceImpl.class); protected JPADataServiceImpl(TypeLiteral<K> key,TypeLiteral<T> entity) { entityKey = (Class<K>) key.getRawType(); entityClass = (Class<T>) entity.getRawType(); // Injected via guice @Provides, see below @Inject public void setEm(EntityManager entityManager){ this.em = entityManager; } /** * {@inheritDoc} */ public T get(K id) { T result = em.find(entityClass, id); return result; } ... }
The EntityManager injected above comes from a guice @Provides binding.
@Provides EntityManager provideEntityManager(){ try { logger.info("creating entity manager 'auction-test'"); return Persistence.createEntityManagerFactory("auction-test").createEntityManager(); }catch(Throwable ex){ logger.error("Cannot create EntityManagerFactory."); throw new ExceptionInInitializerError(ex); } }
Continuing with the generic pattern for concrete DAO's where you need more functionality then basic CRUD.
/** * More interesting methods needed */ public interface JPAUserDataService extends JPADataService<Long, User> { public User getUserByEmail(String email); public Collection<User> getUserByEmployer(Employer employer); }
/** * Implementation class * */ public class JPAUserDataServiceImpl extends JPADataServiceImpl<Long, User> implements JPAUserDataService{ public JPAUserDataServiceImpl(){ super(new TypeLiteral<Long>(){}, new TypeLiteral<User>(){}); } @Override public User getUserByEmail(String email) { return null; //todo } @Override public Collection<User> getUserByEmployer(Employer employer) { return null; //todo } }
Now that the implementation handles assigning all the generics you can fall back to the basic binding examples provided by guice.
/** * guice binding */ @Override protected void configure() { bind(JPAUserDataService.class) .to(JPAUserDataServiceImpl.class) .in(Scopes.SINGLETON); ... }
Seems to be working well for me! You don't get the most out of DI this way as you end up with a one to one relationship of interface to implementation instead of the one to many. That being the case, this really only serves to simplify clients usage especially across multi-module project with as I noted, more then one data store SQL & NoSQL. I am thinking about another approach, a way to bind the more generic JPADataService to all the entities that don't need more then CRUD without having to extend for each type, AND while still allowing for more functional interfaces when needed (as shown).
No comments:
Post a Comment