1 2 Previous Next 25 Replies Latest reply on Nov 18, 2011 2:44 PM by ratking

    Generic DAO

    musketyr
      Hi,
      I just wonder if is possible implement generic crud DAO like one in hibernate book and bind them by interface and type parameter (similar to @Any Instance<MyClass>)

      Something like:

      public class SomeClass {

        @Curent DAO<MyEntity> myEntityDAO;

      }

      public interface DAO<T> {
        // CRUD methods
      }

      public class GenericDAO<T> implements DAO<T> {
        // implementation close to generic dao in JPA with Hibernate book
      }

      public class DAOProvider {
       
        @Produces public DAO<T> provideDAO {
          // possible tests for choosing more special implementation
          ...
          return new GenericDAO<T>();
        }
      }

      I tried to play with it for a while, but it doesn't work. What is the right way to define binding by interface and type parameter just like Instance<MyClass>?

        • 1. Re: Generic DAO
          pmuir

          JSR-299 only identifies generic types as identical if they have the same type parameter, here you don't have an implementation of DAO<MyEntity>.

          • 2. Re: Generic DAO
            musketyr

            It's quite shame :( I think this looks like a nice idiom. It would be great to have build in support for such a binding (maybe with specialization - use concreate implementation where possible), but I don't even know if is it possible to obtain all needed info in runtime (or what info exactly is needed)

            • 3. Re: Generic DAO
              gavin.king

              you're right. The latest spec considers Dao<T> injectable to Dao<MyEntity>, it's just not implemented in the RI yet.

              • 4. Re: Generic DAO
                musketyr

                That's great! I'm looking forward so much on final release of jsr299 and all other java ee 6 stuff. And Seam 3 too :) Thanks for great work you've all done!

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

                  Can I write a generic DAO with interceptor?
                  I have a few classes and interface just as follow:
                  public interface FinderExecutor {
                      /**
                       * Execute generic query
                       */
                      Object executeFinder(String query, Object[] queryArgs, Method method, Integer firstResults, Integer maxResults);

                      /**
                       * Execute generic bulk update
                       */
                      void executeUpdate(String query, Object[] queryArgs);
                  }



                  public interface GenericDao<T, PK extends Serializable> extends FinderExecutor {
                      /**
                       * Get an entity by id
                       *
                       * @param id
                       * @return entity
                       */
                      T findById(PK id);

                      // /**
                      // * Find all entites
                      // *
                      // * @return list of entities
                      // */
                      // List<T> findAll();

                      /**
                       * Stores an entity
                       *
                       * @param entity entity to save
                       */
                      T save(T entity);

                      /**
                       * Deletes an entity
                       *
                       * @param entity entity to delete
                       */
                      void delete(T entity);

                      /**
                       * refresh an entity
                       *
                       * @param entity entity to refresh
                       */
                      void refresh(T entity);

                      /**
                       * Returns the represented generic type
                        */
                       Class<T> getType();
                  }



                  @InterceptorBinding
                  @Retention(RUNTIME)
                  @Target({METHOD, TYPE})
                  public @interface Query {
                      /**
                       * Contains the HQL Statement
                       */
                      @Nonbinding String value() default "";

                      /**
                       * if true, the last two parameters of the annotated method have to be
                       * integers which are first result and maximum result
                       */
                      @Nonbinding boolean limitClause() default false;
                  }



                  @Query @Interceptor
                  public class QueryInterceptor {
                       @PersistenceContext
                       EntityManager entityManager;
                       
                       @AroundInvoke
                       public Object executeQuery(InvocationContext ctx) throws Exception {
                            Object result = entityManager.createQuery( .........);
                            
                            return result;
                       }
                  }


                  public interface UserDao extends GenericDao<UserEntity, Integer> {
                      @Query("select u from UserEntity u join fetch u.role r join fetch r.rights where u.username like ?")
                      UserEntity findUserByUsername(String username);

                      @Query("select u from UserEntity u where u.sessionId = ?")
                      UserEntity findUserBySessionId(String sessionId);

                      @Query("select distinct u from UserEntity u where u.email like ?")
                      List<UserEntity> findUserByEmail(String email);

                      @Query("select count(u) from UserEntity u where u.username like ?")
                      long existsUsername(String username);

                      @Query("select count(u) from UserEntity u where u.role = ?")
                      Long countUserForRole(RoleEntity role);

                      @Query(value = "select distinct(u) from UserEntity u join u.role.rights as r where r.right = ?")
                      List<UserEntity> findUserWithRight(String right);
                  }


                  Can I just inject the UserDao in the action class and get the result by interceptor?

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

                    sorry for the code format,but I cann't edit it any more

                    • 7. Re: Generic DAO
                      pmuir

                      Post again with better formatting, no-one can read that ;-)

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

                        I want to execute query in the interceptor and specify the ql in the method with annotation,just as follow:


                        @Query @Interceptor
                        public class QueryInterceptor {
                             
                             @PersistenceContext 
                             EntityManager em;
                        
                             @AroundInvoke 
                             public Object executeQuery(InvocationContext ctx) throws Exception {
                                  Object result = null;
                                  Query queryAnnotation = getQueryAnnotation(ctx.getMethod());
                                  String queryValue = queryAnnotation.value();
                                  if (queryAnnotation.limitClause()) {
                                    Object orginal[] = ctx.getParameters();
                                    for (Object obj:orginal) {
                                        System.out.println(obj);
                                        
                                    }
                                 } else {
                                      System.out.println(queryValue);
                                      result = em.createQuery(queryValue)
                                                .getResultList();
                                 }
                                  
                                  return result;
                             }
                             
                             private Query getQueryAnnotation(Method m) {
                                  for (Annotation a : m.getAnnotations()) {
                                       if (a instanceof Query) {
                                            return (Query) a;
                                       }
                                  }
                                  for (Annotation a : m.getDeclaringClass().getAnnotations()) {
                                       if (a instanceof Query) {
                                            return (Query) a;
                                       }
                                  }
                        
                                  throw new RuntimeException("@Query not found on method " + m.getName()
                                            + " or its class " + m.getClass().getName());
                             }
                             
                        }





                        and



                        @InterceptorBinding
                        @Retention(RUNTIME)
                        @Target({METHOD, TYPE})
                        public @interface Query {
                            /**
                             * Contains the HQL Statement
                             */
                            @Nonbinding String value() default "";
                        
                            /**
                             * if true, the last two parameters of the annotated method have to be
                             * integers which are first result and maximum result
                             */
                            @Nonbinding boolean limitClause() default false;
                        }



                        just use interceptor as :


                        @Produces
                           @Named
                           @RequestScoped
                           @SuppressWarnings("unchecked")
                           @Query("select w from Widget w order by w.name")
                           List<Widget> getWidgets();




                        How can I get method arguments with interceptor?

                        • 9. Re: Generic DAO
                          pmuir

                          Do:


                          String ql = ctx.getMethod().getAnnotation(Query.class).value();

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

                            In an application use JPA,there's a lot of entityManager.createQuery(.....)。I had spy the devproof's GenericDao implementations,he use spring's ProxyFactory and interceptor introduce a simpler approach:



                            public class FinderDispatcherGenericDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements FactoryBean, Serializable {
                            
                                private static final long serialVersionUID = -3752572093862325307L;
                            
                                private Object servicesImpl;
                                private Class<T> entityClass;
                                private Class<?> daoInterface;
                                private UsernameResolver usernameResolver;
                            
                                public Object getObject() throws Exception {
                                    ProxyFactory result = new ProxyFactory();
                                    GenericDao<T, PK> genericDao = createGenericHibernateDao();
                                    result.setTarget(genericDao);
                                    result.setInterfaces(new Class[]{daoInterface});
                                    result.addAdvice(createGenericDaoInterceptor());
                                    return result.getProxy();
                                }
                            }
                            




                            What you need is just A interface specify the query and method arguments、result,no repeate createQuery any more:





                            @CacheQuery(region = UserConstants.QUERY_CACHE_REGION)
                            public interface UserDao extends GenericDao<UserEntity, Integer> {
                                @Query("select u from UserEntity u join fetch u.role r join fetch r.rights where u.username like ?")
                                UserEntity findUserByUsername(String username);
                            
                                @Query("select u from UserEntity u where u.sessionId = ?")
                                UserEntity findUserBySessionId(String sessionId);
                            
                                @Query("select distinct u from UserEntity u where u.email like ?")
                                List<UserEntity> findUserByEmail(String email);
                            
                                @Query("select count(u) from UserEntity u where u.username like ?")
                                long existsUsername(String username);
                            
                                @Query("select count(u) from UserEntity u where u.role = ?")
                                Long countUserForRole(RoleEntity role);
                            
                                @Query(value = "select distinct(u) from UserEntity u join u.role.rights as r where r.right = ?")
                                List<UserEntity> findUserWithRight(String right);
                            }
                            




                            bean declaretion in the configuration:



                            <bean id="userDao" parent="baseGenericDao">
                                    <property name="daoInterface"
                                              value="org.devproof.portal.core.module.user.dao.UserDao"/>
                                    <property name="entityClass"
                                              value="org.devproof.portal.core.module.user.entity.UserEntity"/>
                            </bean>
                            



                            Can this is implented in weld?
                            Spring version Source can be viewed in http://code.google.com/p/devproof/source/browse/svn/trunk/portal-core/src/main/java/org/devproof/portal/core/module/common/dao

                            • 11. Re: Generic DAO
                              pmuir

                              Yes, you could implement this in Weld. For example you could use an interceptor or you could use lifecycle events to read the annotations and create beans.

                              • 12. Re: Generic DAO
                                andygibson.contact.andygibson.net

                                Is there much need for this? Where would you call it from while passing it the parameters and such that the interceptors will still fire?


                                I don't think that calling one injected bean from another bean fires the interceptors on the injected bean method i.e.


                                class SomeBean {
                                
                                  @SomeInterceptor
                                  public Object someMethod(String param) {
                                    ...
                                  }
                                }
                                
                                class SomeOtherBean {
                                
                                  @Inject
                                  private SomeBean someBean;
                                
                                  public void someCode() {
                                    ...
                                    ...
                                    someBean.someMethod(aValue);
                                    ...
                                  }
                                
                                }
                                



                                I don't believe that in this case, if you call SomeOtherBean.someCode, when it calls someMethod on the injected SomeBean instance, the Interceptor won't fire, nor would parameter injection or any other bean enhancements?


                                So, you could call it like #{someBean.someMethod} from JSF, or as an event handler or something like that, which is fine, but I don't see query methods being called like this, and there's no real way to stick parameters on there.


                                Also, when you bind a JSF page to some query results, you don't pass in method parameters when getting the values, and you can only inject parameters on a @produces method.


                                I'm just wondering if there is a real use case for it, because the only way I see it being useful is returning data from @Produces method calls that don't have parameters (or only have injected parameters).


                                Cheers,


                                Andy

                                • 13. Re: Generic DAO
                                  nickarls

                                  Without looking too much at the code I think they might fire for normal scoped beans since I think the interceptor stack is built into the proxy. Ish.

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

                                    There is what I want to implement:
                                    A interface just for describe the query should be executed:


                                    public interface UserDao extends GenericDao<UserEntity, Integer> {
                                        @Query("select distinct u from UserEntity u where u.email like ?")
                                        List<UserEntity> findUserByEmail(String email);
                                    }
                                    



                                    A interceptor populate the EntityManager Query from the annotation and method arguments,or just proxy it to a generic dao implementation:




                                    @QuerySpecifiction @Interceptor
                                    public class QueryInterceptor {
                                         private @Inject Logger logger;
                                         @Inject GenericDao target;
                                         @AroundInvoke 
                                         public Object executeQuery(InvocationContext ctx) throws Exception {
                                              Method method = ctx.getMethod();
                                              QuerySpecifiction querySpecification = getQueryAnnotation(method);
                                              if (querySpecification.limitClause()) {
                                                        Object orginal[] = ctx.getParameters();
                                                         int len = orginal.length - 2;
                                                        Object copy[] = new Object[len];
                                                        for (int i = 0; i < len; i++) {
                                                           copy[i] = orginal[i];
                                                        }
                                                        return target.executeFinder(querySpecification.value(), copy, method, (Integer) orginal[len], (Integer) orginal[len + 1]);
                                                   } else {
                                                       return target.executeFinder(querySpecification.value(), ctx.getParameters(), method, null, null);
                                                  }
                                    
                                         }
                                    .........     
                                    }



                                    A @Produces method create the dao's instance:




                                    @Produces @Dao
                                    public UserDao getUserDao() {
                                              return null; 
                                    }



                                    A interceptor create the dao's proxy instant:




                                    @Interceptor
                                    @Dao 
                                    public class DaoInterceptor {
                                         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(result));  
                                            
                                              return result;
                                    
                                         }
                                    
                                         
                                    }




                                    Then in the client,I could find the user by email like this:




                                    @Inject UserDao userDao;
                                    
                                    return userDao. findUserByEmail("some@mail.com");




                                    Because lots of method just create the query and set the parameters with method arguments,so I think should write those code once and reuse it for different entity.However,I cann't figure out how to achieve this with weld yet.

                                    1 2 Previous Next