[weld] GenericDao,Spring(2.5) VS Weld

wuhaixing 2010-04-17
对于还不知道什么是GenericDao的同学,推荐阅读
http://community.jboss.org/wiki/GenericDataAccessObjects
在开始weld文档中关于weld的部分之前,希望有个例子能给大家看,所以花了些时间研究,也在seamframework论坛请教了些问题,得到了他们热心的帮助。感兴趣的可以到
http://www.seamframework.org/Community/GenericDAO看看。
由于我对spring和weld都不是非常熟悉,所以实现方式可能不是非常恰当,欢迎讨论。
wuhaixing 2010-04-17
spring+hibernat的代码构成

FinderDispatcherGenericDaoImpl,这是最关键的核心类,其中有interceptor的实现和绑定:

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();
    }

    protected GenericDao<T, PK> createGenericHibernateDao() {
        GenericHibernateDaoImpl<T, PK> genericDao = new GenericHibernateDaoImpl<T, PK>(entityClass);
        genericDao.setSessionFactory(getSessionFactory());
        genericDao.setHibernateTemplate(getHibernateTemplate());
        genericDao.setUsernameResolver(usernameResolver);
        return genericDao;
    }

    private MethodInterceptor createGenericDaoInterceptor() {
        return new MethodInterceptor() {
            public Object invoke(MethodInvocation invocation) throws Throwable {
                /*
                     * If the session will be opened at this place, the same method
                     * closes the session and transaction
                     */
                boolean isSessionAvailable = isSessionAvailable();
                if (!isSessionAvailable) {
                    openSession();
                }
                Object result = evaluateMethodInvocation(invocation);
                if (!isSessionAvailable) {
                    closeSession();
                }
                return result;

            }

            private Object evaluateMethodInvocation(MethodInvocation invocation) throws Throwable {
                Object result = null;
                Method method = invocation.getMethod();
                if (method.isAnnotationPresent(Query.class)) {
                    result = executeQuery(invocation);
                } else if (method.isAnnotationPresent(BulkUpdate.class)) {
                    executeBulkUpdate(invocation);
                } else {
                    result = delegateToServiceMethod(invocation);
                }
                return result;
            }

            private Object delegateToServiceMethod(MethodInvocation invocation) throws Throwable {
                Method serviceMethod = FinderDispatcherGenericDaoImpl.this.servicesImpl != null ? FinderDispatcherGenericDaoImpl.this.servicesImpl.getClass().getMethod(invocation.getMethod().getName(), invocation.getMethod().getParameterTypes()) : null;
                if (serviceMethod != null) {
                    return serviceMethod.invoke(FinderDispatcherGenericDaoImpl.this.servicesImpl, invocation.getArguments());
                } else {
                    // should be only save, update, delete from the generic
                    // dao
                    return invocation.proceed();
                }
            }

            private void executeBulkUpdate(MethodInvocation invocation) {
                openTransaction();
                Method method = invocation.getMethod();
                BulkUpdate bulkUpdate = method.getAnnotation(BulkUpdate.class);
                FinderExecutor target = (FinderExecutor) invocation.getThis();
                target.executeUpdate(bulkUpdate.value(), invocation.getArguments());
            }

            private boolean isSessionAvailable() {
              。。。。
            }

            private void openTransaction() {
               。。。。
            }

            private Object executeQuery(MethodInvocation invocation) {
                Method method = invocation.getMethod();
                Query query = method.getAnnotation(Query.class);
                FinderExecutor target = (FinderExecutor) invocation.getThis();
                if (query.limitClause()) {
                    Object orginal[] = invocation.getArguments();
                    int len = orginal.length - 2;
                    Object copy[] = new Object[len];
                    for (int i = 0; i < len; i++) {
                        copy[i] = orginal[i];
                    }
                    return target.executeFinder(query.value(), copy, method, (Integer) orginal[len], (Integer) orginal[len + 1]);
                } else {
                    return target.executeFinder(query.value(), invocation.getArguments(), method, null, null);
                }
            }

            private void closeSession() { 
              。。。。。。
            }

            private void commitTransaction(SessionHolder sessionHolder) { 
               。。。。。。。
            }

            private void openSession() {
                 。。。。。
            }
        };
    }

    @SuppressWarnings(value = "unchecked")
    public Class getObjectType() {
        return daoInterface;
    }

    public boolean isSingleton() {
        return true;
    }

    public Object getServicesImpl() {
        return servicesImpl;
    }

    public void setServicesImpl(Object servicesImpl) {
        this.servicesImpl = servicesImpl;
    }

    public Class<T> getEntityClass() {
        return entityClass;
    }

    @Required
    public void setEntityClass(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public Class<?> getDaoInterface() {
        return daoInterface;
    }

    @Required
    public void setDaoInterface(Class<?> daoInterface) {
        this.daoInterface = daoInterface;
    }

    @Required
    public void setUsernameResolver(UsernameResolver usernameResolver) {
        this.usernameResolver = usernameResolver;
    }
}


相关的beans配置如下:
<bean id="baseGenericDao"          class="org.devproof.portal.core.module.common.dao.FinderDispatcherGenericDaoImpl"
          parent="hibernateDaoSupport" abstract="true">
        <property name="usernameResolver" ref="usernameResolver"/
</bean>


wuhaixing 2010-04-17
对应的weld实现分成了几个部分:

首先是interceptor:

@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Dao {

}


@Interceptor
@Dao 
public class DaoInterceptor {
	@PersistenceContext
    EntityManager em;
	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;
	}
}


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;
	}
}
wuhaixing 2010-04-17
至于Dao的定义及调用,差别不多,spring中是:

public interface WidgetDao extends GenericDao<Widget,Long>{
	@QuerySpecifiction("select w from Widget w order by w.name")
	public List<Widget> getWidgets();
}


加上xml配置:
<bean id="widgetDao" parent="baseGenericDao">
        <property name="daoInterface" value="WidgetDao"/>
        <property name="entityClass"   value="WidgetEntity"/></bean>



调用为对Dao的get和set方法。
wuhaixing 2010-04-17
weld中interface一样

由一个produces方法加上interceptor来创建Dao:

@Produces @Dao
public WidgetDao getWidgetDao() {
		return null; 
}


调用为注入的field,不需要get及set方法:
@Inject WidgetDao widgetDao



wuhaixing 2010-04-17
至于负责执行具体任务的DaoImpl,几乎是完全一致的。只是interceptor的实现及绑定方式,相比较而言weld更清晰。
lonvea 2010-04-17
小兄弟,大哥给你秀一下我的weld实现。。

1.BaseDAO:所有DAO依赖注入的修饰类型

@Target( { TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
@Qualifier
public @interface BaseDAO {

}

2.ibatis中sqlmap修饰

@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD})
@Qualifier
public @interface SqlMap {

}

3. sqlmap生产方法(依赖注出)

public class SqlMapClientFactory {

private static SqlMapClient sqlMapClient;

.........

@Produces
@SqlMap
public static SqlMapClient getInstance(){
return sqlMapClient;
}

}


4.UserDAO:用户信息操作类

@BaseDAO
public class UserDAOImpl implements UserDAO {

@Inject
@SqlMap
private SqlMapClient sqlMapClient;

@Override
public void insert(UserDO userDO) throws DAOException {
try {
sqlMapClient.insert("UserDAO.insert", userDO);
} catch (Exception e) {
throw new DAOException(e);
}
}

....
}


超级简单。。。

不过本人最近对JSF和JPA有强烈抵触情绪。NND,JSF感觉有点补靠谱了。改成velocity+servlet自己封装View层。

由于JPA无法对sql进行动态支持,分表操作等,改成ibatis。。



wuhaixing 2010-04-17
呵呵,简单直观的方式,但代码应该还是会多一些。另外没看懂你的Dao实现中insert参数的作用。
至于抵触情绪,大可不必,他们能发展到现在的样子,还是有可取之处的。
ibatis有他的优势,毕竟他是瞄着orm的缺点做的。
rrsy23 2010-04-19
还是做成NO DAO吧


直接一个service里面提供持久化服务就OK
wuhaixing 2010-04-19
rrsy23 写道
还是做成NO DAO吧


直接一个service里面提供持久化服务就OK


其实这个实现并不是严格意义上的DAO,只是为了简化无处不在的@PersistenceContext 和 createQuery。从理论上来说,用接口声明query语句是有问题的,但这种方式的确会减少你敲键盘的次数。
Global site tag (gtag.js) - Google Analytics