1 2 Previous Next 25 Replies Latest reply on Nov 18, 2011 2:44 PM by ratking Go to original post
      • 15. Re: Generic DAO
        andygibson.contact.andygibson.net

        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

          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

            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

              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

                Should I place every dao in application scope with @Produces?

                • 20. Re: Generic DAO
                  wuhaixing.wuhaixing.gmail.com

                  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

                    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

                      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/directory


                      I, 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

                        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

                          I implememented this using a different approach:


                          http://seamframework.org/Community/InjectionOfAProxiedInterface

                          • 25. Re: Generic DAO
                            ratking

                            @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

                            1 2 Previous Next