package com.aggrepoint.dao;
import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ConversionServiceFactoryBean;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.support.DaoSupport;
/**
* 不能注入SessionFactory,不能在checkDaoConfig时获取SessionFactory,
* 因为会造成嵌套加载其他扫描Dao对象的情况,对象数量多时会引起堆栈溢出。
*
* 需要支持多个数据源时,可以明确指定entityManager或sessionFactory参数,指向负责
*
* @author Jiangming Yang (yangjm@gmail.com)
*/
public class DaoFactoryBean<T, K> extends DaoSupport implements FactoryBean<T>,
ApplicationContextAware {
private static final Log logger = LogFactory.getLog(DaoFactoryBean.class);
private static ConversionService conversionService;
private ApplicationContext ctx;
private EntityManager entityManager;
private SessionFactory sessionFactory;
private Class<T> daoInterface;
private T proxy;
private Class<K> domainClz;
private List<IFunc> funcs;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
public void setEntityManager(EntityManager manager) {
entityManager = manager;
}
public void setSessionFactory(SessionFactory factory) {
sessionFactory = factory;
}
@SuppressWarnings("unchecked")
Class<K> findDomainClass(Class<?> intf) {
for (Type t : intf.getGenericInterfaces()) {
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
if (pt.getRawType().equals(DaoService.class)) {
return (Class<K>) pt.getActualTypeArguments()[0];
}
}
}
for (Class<?> t : intf.getInterfaces()) {
Class<K> k = findDomainClass(t);
if (k != null)
return k;
}
return null;
}
public void setDaoInterface(Class<T> daoInterface) {
this.daoInterface = daoInterface;
domainClz = findDomainClass(daoInterface);
if (domainClz == null) {
// { exception caught by Spring framework, so have to print it out
// here
String err = "Unable to match extract domain class from dao interface '"
+ daoInterface + "'.";
logger.error(err);
// }
throw new IllegalArgumentException(err);
}
}
public void setFuncs(List<IFunc> funcs) {
this.funcs = funcs;
}
/**
* get ConversionService. If a service is defined in context with name
* daoConversionService then use it, otherwise create a default one
*
* @return
*/
private ConversionService getConversionService() {
if (conversionService != null)
return conversionService;
try {
conversionService = (ConversionService) ctx
.getBean("daoConversionService");
if (conversionService != null)
return conversionService;
} catch (Exception e) {
}
ConversionServiceFactoryBean factory = new ConversionServiceFactoryBean();
factory.afterPropertiesSet();
conversionService = factory.getObject();
return conversionService;
}
private EntityManager getEntityManager() {
if (entityManager == null)
try {
entityManager = ctx.getBean(EntityManagerFactory.class)
.createEntityManager();
} catch (NoSuchBeanDefinitionException e) {
}
return entityManager;
}
private SessionFactory getSessionFactory() {
if (sessionFactory == null)
try {
sessionFactory = ctx.getBean(SessionFactory.class);
} catch (NoSuchBeanDefinitionException e) {
}
return sessionFactory;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T getObject() throws Exception {
if (proxy == null) {
try {
CacheManager cm = null;
try {
cm = ctx.getBean("daoCache", CacheManager.class);
} catch (Exception e) {
}
if (cm == null) {
try {
cm = ctx.getBean(CacheManager.class);
} catch (Exception e) {
}
}
proxy = (T) Proxy.newProxyInstance(daoInterface
.getClassLoader(), new Class[] { daoInterface },
new DaoInvocationHandler<K>(getEntityManager(),
getSessionFactory(), cm,
getConversionService(), daoInterface,
domainClz, funcs));
} catch (Throwable t) {
logger.error("Error while creating proxy for dao interface '"
+ this.daoInterface + "'.", t);
throw new IllegalArgumentException(t);
}
}
return proxy;
}
/**
* {@inheritDoc}
*/
public Class<T> getObjectType() {
return this.daoInterface;
}
/**
* {@inheritDoc}
*/
public boolean isSingleton() {
return true;
}
@Override
protected void checkDaoConfig() throws IllegalArgumentException {
notNull(daoInterface, "Property 'daoInterface' is required");
isTrue(daoInterface.isInterface(),
"Property 'daoInterface' must be interface");
}
}