-
15. Re: Generic DAO
andygibson.contact.andygibson.net Apr 16, 2010 5:41 PM (in response to musketyr)Nicklas : Yeah, you're right, interceptors do work from calls from other code, however when I tried it yesterday it wasn't working so something must be funky with it somewhere. I've a few issues I need to post regarding CDI.
Wu : I take your point about creating lots of queries and setting parameters. However, I don't know that doing something with an annotation is the answer. Why not just create a bean that can be injected and executes the query for you :
class QueryExecutor { List<Object> execute(String ejbql,... Object) { createQuery(ejbql) SetParams...; } } class MyDao { @Inject QueryExecutor executor; List<User> findByEmail(String email) { return executor.execute("select u from User u where u.email = ?",email); } }
It's about the same amount of code without the complexity of doing it in an annotation on an interface and having to code that up. Plus if you decide to change the implementation of a query you aren't screwed because you bound it so tightly to this dao proxy implementation. Also, it's a bit ironic implementing to an interface, but tying the actual implementation (the ejbql) to the interface. I would at least move the @Query annotations to an implementation.
Have you used Seam and the Seam EntityQuery?
Self promoting for a second, I just released an open source querying API (Spigot) that might help with this somewhat. I just coded up a quick solution for this using it so you can do something like :
public JpaUserDao implements UserDao { @Query("select c from Customer c where c.manager.id = #{user.id} and c.lastName = :1") public List<Customer> findUsersCustomersByLastName(String lastName) { return null; } }
The interceptor picks up the annotation and executes the query returning the results. It also resolves the el expressions and binds the parameters for you.
You could also annotate it with Hibernate or plain SQL Query interceptor and just enable the interceptor on deployment.I'm only asking because if there is any point in having this kind of mechanism, I'll enhance it a bit and add it to the library. While this is OK for limited cases, there's no pagination and no ability to transform the query based on the parameters.
Cheers,
Andy
-
16. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Apr 17, 2010 3:42 AM (in response to musketyr)Andy,I just want to refactor a spring implemention to weld and see what's happened.I agree to tying the actual implementation to the interface is ironic,but I don't like there is a 'return null' implemention,and generally only one implemention is needed.
I think the first solution is better,thanks for your kindness.
I love seam and adopted it in a few projects,that's why I start to learn weld.
-
17. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Apr 17, 2010 4:31 AM (in response to musketyr)I just create a workable implementation,here it is:
GenericDaoImpl do the work:
public class GenericDaoImpl<T, PK extends Serializable> implements GenericDao<T, PK> { private EntityManager em; private Class<T> type; public GenericDaoImpl(EntityManager em,Class<T> type) { this.em = em; this.type = type; } @SuppressWarnings(value = "unchecked") public T findById(PK id) { return (T) em.find(type, id); } @SuppressWarnings("unchecked") public T save(T entity) { return (T)em.merge(entity); } @Override public void refresh(T entity) { em.refresh(entity); } public void delete(T entity) { em.remove(entity); } public Object executeFinder(String query, Object[] queryArgs, Method method, Integer firstResults, Integer maxResults) { String querySpecification = replaceGenericTypeName(query); Query q = em.createQuery(querySpecification); setParameter(queryArgs, q); setResultLimitations(firstResults, maxResults, q); if (Collection.class.isAssignableFrom(method.getReturnType())) { return q.getResultList(); } else { return q.getSingleResult(); } } private void setResultLimitations(Integer firstResults, Integer maxResults, Query q) { if (firstResults != null) { q.setFirstResult(firstResults); } if (maxResults != null) { q.setMaxResults(maxResults); } } public void executeUpdate(String query, Object[] queryArgs) { String querySpecification = replaceGenericTypeName(query); Query q = em.createQuery(querySpecification); setParameter(queryArgs, q); q.executeUpdate(); } private String replaceGenericTypeName(String query) { if (query.contains("$TYPE")) { return query.replace("$TYPE", type.getSimpleName()); } return query; } private void setParameter(Object[] queryArgs, Query q) { if (queryArgs != null) { for (int i = 0; i < queryArgs.length; i++) { q.setParameter(i, queryArgs[i]); } } } public Class<T> getType() { return type; } public void setType(Class<T> type) { this.type = type; } }
Dao annotation and Interceptor creat the Dao proxy implements Dao interface:
@InterceptorBinding @Retention(RUNTIME) @Target({METHOD, TYPE}) public @interface Dao { }
@Interceptor @Dao public class DaoInterceptor { @PersistenceContext EntityManager em; // place injection point here because proxy class is new ed manual private @Inject Logger logger; @AroundInvoke public Object createDao(InvocationContext ctx) throws Exception { Class<?> returnType = ctx.getMethod().getReturnType(); logger.info("create dao {}",returnType.getName()); if (!returnType.isInterface()) { throw new UnsupportedOperationException("Only results of methods " + "which produce implementations of an interface can be replaced!"); } Object result = ctx.proceed(); result = Proxy.newProxyInstance( returnType.getClassLoader(), new Class[] { returnType }, new DaoInvocationHandler(new GenericDaoImpl(em,returnType))); return result; } }
InvocationHandler get the annonation and delegate the work to DaoImpl:
public class DaoInvocationHandler implements InvocationHandler { private final Object delegate; public DaoInvocationHandler(Object delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { QuerySpecifiction querySpecification = getQueryAnnotation(method); if(querySpecification != null) { if (querySpecification.limitClause()) { int len = args.length - 2; Object copy[] = new Object[len]; for (int i = 0; i < len; i++) { copy[i] = args[i]; } return ((GenericDao)delegate).executeFinder(querySpecification.value(), copy, method, (Integer) args[len], (Integer) args[len + 1]); } else { return ((GenericDao)delegate).executeFinder(querySpecification.value(), args, method, null, null); } } return method.invoke(delegate, args); } private QuerySpecifiction getQueryAnnotation(Method m) { for (Annotation a : m.getAnnotations()) { if (a instanceof QuerySpecifiction) { return (QuerySpecifiction) a; } } for (Annotation a : m.getDeclaringClass().getAnnotations()) { if (a instanceof QuerySpecifiction) { return (QuerySpecifiction) a; } } return null; } }
Since I'm not familiar with weld,so I'd like to you give me some suggestion to improve it!Thanks!
-
18. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Apr 17, 2010 4:39 AM (in response to musketyr)usage
interface specificy Dao
public interface WidgetDao extends GenericDao<Widget,Long>{ @QuerySpecifiction("select w from Widget w order by w.name") public List<Widget> getWidgets(); }
DaoFactory produces dao(I don't like this part,maybe some extension can place this in the xml without useless method body?):
public class WidgetDaoFactory { @Produces @Dao public WidgetDao getWidgetDao() { return null; } }
client use Dao:
@Inject WidgetDao widgetDao; return widgetDao.getWidgets();
-
19. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Apr 19, 2010 5:29 AM (in response to musketyr)Should I place every dao in application scope with @Produces?
-
20. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Apr 19, 2010 3:38 PM (in response to musketyr)refactor DaoFactory
public class DaoFactory { @PersistenceContext EntityManager em; private @Inject Logger logger; @Produces @Dao(Object.class) public Object createDao(InjectionPoint injectionPoint) throws Exception { Class<?> returnType = injectionPoint.getAnnotated().getAnnotation(Dao.class).value(); logger.info("create dao {}",returnType.getName()); if (!returnType.isInterface()) { throw new UnsupportedOperationException("Only results of methods " + "which produce implementations of an interface can be replaced!"); } GenericDao genericDao = new GenericDaoImpl(em,returnType); Object result = Proxy.newProxyInstance( returnType.getClassLoader(), new Class[] { returnType }, new DaoInvocationHandler(genericDao)); return result; } }
Dao qualifers
@Qualifier @Retention(RUNTIME) @Target({METHOD, FIELD, PARAMETER, TYPE}) public @interface Dao { @Nonbinding Class<?> value(); }
There is no interceptor needed!Usage is:
@Inject @Dao(WidgetDao.class) Object widgetDao; return ((WidgetDao)widgetDao).getWidgets();
-
21. Re: Generic DAO
wuhaixing.wuhaixing.gmail.com Jun 1, 2010 11:19 PM (in response to musketyr)refactor DAOFactory:
public class DaoFactory { @PersistenceContext EntityManager em; private @Inject Logger logger; @Produces @Dao public Object createDao(InjectionPoint injectionPoint) throws Exception { Class<?> entityType = injectionPoint.getAnnotated().getAnnotation(Dao.class).value(); if(!(injectionPoint.getMember() instanceof Field)) { throw new UnsupportedOperationException("Only Field injection of an interface can be proxied!"); } Class<?> daoType = ((Field)injectionPoint.getMember()).getType(); if(!daoType.isInterface()) { throw new UnsupportedOperationException("Only Field injection of an interface can be proxied!"); } logger.info("create dao {}",daoType); GenericDao genericDao = new GenericDaoImpl(em,entityType); Object result = Proxy.newProxyInstance( entityType.getClassLoader(), new Class[] {daoType}, new DaoInvocationHandler(genericDao)); return result; } }
extension to override injected object type,I stolen it from seam-faces module:
@ApplicationScoped public class DaoTypeOverrideExtension implements Extension { private final Map<Class<?>, AnnotatedType<?>> typeOverrides = new HashMap<Class<?>, AnnotatedType<?>>(); public <T> void processAnnotatedType(@Observes final ProcessAnnotatedType<T> event) { AnnotatedTypeBuilder<T> builder = AnnotatedTypeBuilder.newInstance(event.getAnnotatedType()); builder.readAnnotationsFromUnderlyingType(); boolean modifiedType = false; for (AnnotatedField<?> f : event.getAnnotatedType().getFields()) { if (f.isAnnotationPresent(Dao.class)) { builder.overrideFieldType(f.getJavaMember(), Object.class); modifiedType = true; } } if (modifiedType) { AnnotatedType<T> replacement = builder.create(); typeOverrides.put(replacement.getJavaClass(), replacement); event.setAnnotatedType(replacement); } } public boolean hasOverriddenType(final Class<?> clazz) { return typeOverrides.containsKey(clazz); } public AnnotatedType<?> getOverriddenType(final Class<?> clazz) { return typeOverrides.get(clazz); } }
now usage is :
@Inject @Dao(Widget.class) WidgetDao widgetDao;
-
22. Re: Generic DAO
cjalmeida Jun 3, 2010 3:23 AM (in response to musketyr)This is indeed a nice extension.
Maybe Seam folks should create an
extension directory
where the community could place their portable extensions. Something like http://vaadin.com/directoryI, for instance, developed a wrapper around Quartz to batch run reports that I believe would be useful for other developers.
-
23. Re: Generic DAO
nickarls Jun 3, 2010 3:55 AM (in response to musketyr)I agree (and I have called for some
Weld Shop
-type directory here on sfwk.org before).Let's hope Dan or Lincoln picks up on this as we don't want announcements to get lost on the forums.
-
24. Re: Generic DAO
agori Jun 8, 2010 5:06 AM (in response to musketyr)I implememented this using a different approach:
http://seamframework.org/Community/InjectionOfAProxiedInterface
-
25. Re: Generic DAO
ratking Nov 18, 2011 2:44 PM (in response to musketyr)@wu haixing
Thank for your codes.你好,非常感谢你共享你的例子程序,这段代码2010年我曾在 http://www.iteye.com/topic/646483 也看到过,并且在GlassFish里运行了一下,确实跑通了(需要自己补一个services文件来激活那个DaoTypeOverrideExtension),因此对你很崇拜。
后来,我又找到Andy Gibson的博客网站,看到他写的DataValve,介绍在这里: http://www.andygibson.net/blog/projects/datavalve/ ,源码在这里:https://github.com/andygibson/datavalve
阅读和试用DataValve之后,感觉Andy Gibson的水平更高,视野更好(看来米国的IT实力比国内弓虽很多呀),比你的GenericDao更实用。
尽管如此,对你的敬仰依然没变。我英文不行,English的论坛能看懂,但要是英文交流就很怵。虽然从Seam 3刚出现就开始跟进,但进步不大,试用 Seam 3.1.0.beta + EJB 3.1 + PrimeFaces 3.0.M4 + GlassFish 3.1.2.bete ,遇到不少事务异常、懒加载异常,几经尝试也不知该如何处理。在国内找不到能讨论的地方,ITEYE的Seam圈子也没什么实质内容。
翻墙读了 http://debasishg.blogspot.com/2006/06/stateful-thinking-with-jboss-seam.html 以及 http://blog.frankel.ch/why-cdi-wont-replace-spring 尽管是旧文章,里面批评Seam的话我觉得好像也在理呀~
与Generic DAO相近的话题,是对Seam 2的EntityHome和EntityQuery如何向Seam 3迁移的探讨:
http://seamframework.org/Community/SeamApplicationFrameworkEntityHomeQueryHome国内使用过Seam的人不多,我只知道两个,一个是wu haixing,另一个是hantsy bai。
关于Seam 3的事务异常、懒加载异常相关问题如何解决,非常想向您请教与探讨。
ratking (at) ynet.com
2011-11-19