以前一直使用Hibernate,习惯了关联实体的懒加载。最近使用Nutz,里面没有提供懒加载的支持,于是乎自己扩展了一下Dao代码使其支持懒加载。这样用户在获取数据时就无需关心从dao抓取关联对象的问题了,调用POPO的getter方法时即可自动加载关联实体。下面就具体方案整理如下:
1. 定义一个名为LinkFieldGetterIntercepter的方法拦截器: 用于调用POJO的getter时自动加载关联对象。
2. 自定义一个EntityHolder: reloadEntity时创建实体代理类,添加第1条中定义的拦截器。并使用实体代理类在mappings中缓存实体。
3. 自定义一个org.nutz.dao.entity.Borning: 便于将ResultSet转换成POJO时,创建的是POJO的代理类实例,而不是POJO原始类实体。
4. 自定义一个EntityMaker: make方法中解析实体时setBorning(...)为第3条中定义的Borning对象。
下面是本人使用JPA作为POJO的annotation,扩展后的相关代码,如果直接使用Nutz annotation实现应该更简单。方法都是一样的。
1. LinkFieldGetterIntercepter:
public class LinkFieldGetterIntercepter extends AbstractMethodInterceptor { private static final Log log = Logs.getLog(LinkFieldGetterIntercepter.class); private Dao dao; private Link link; public LinkFieldGetterIntercepter(Dao dao, Link link) { this.dao = dao; this.link = link; } public Object afterInvoke(Object obj, Object returnObj, Method method, Object... args) { if (returnObj != null) { return returnObj; } String name = link.getOwnField().getName(); dao.fetchLinks(obj, name); returnObj = Mirrors.getValue(obj, name); return returnObj; } }
2. EntityHolder
public class DemsyEntityHolder { private Dao dao;// Dao 对象 private EntityMaker maker; private Map<Class<?>, Entity<?>> mappings;// <AOP代理类,实体> private Map<Class, Class> agentClassMap;// <实体类,AOP代理类> public DemsyEntityHolder(DemsyNutDao dao) { this.dao = dao; this.maker = dao.getEntityMaker(); mappings = new HashMap<Class<?>, Entity<?>>(); agentClassMap = new HashMap<Class, Class>(); } public <T> Entity<T> getEntity(Class<T> classOfT) { // 转换成AOP代理类 if (!Mirrors.isAgent(classOfT)) { classOfT = agentClassMap.get(classOfT); } if (classOfT == null) { return null; } return (Entity<T>) mappings.get(classOfT); } @SuppressWarnings("unchecked") public <T> Entity<T> reloadEntity(Class<T> classOfT, boolean autoCreateTable) { JPAEntity<?> entity = (JPAEntity<?>) maker.make(null, null, classOfT); // 缓存实体 mappings.put(agentClass(entity), entity); return (Entity<T>) entity; } // 创建代理类 private Class agentClass(JPAEntity entity) { Class cls = entity.getType(); if (Mirrors.isAgent(cls)) { return cls; } ClassAgent classAgent = new AsmClassAgent(); List<Link> links = entity.getLinks(null); if (links != null) { for (Link link : links) { String fieldName = link.getOwnField().getName(); fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); classAgent.addInterceptor(MethodMatcherFactory.matcher("get" + fieldName), new LinkFieldGetterIntercepter(dao, link)); } } Class agentClass = classAgent.define(new DefaultClassDefiner(), cls); entity.setAgentMirror(Mirror.me(agentClass)); this.agentClassMap.put(cls, agentClass); return agentClass; } public int count() { return mappings.size(); } }
3 Borning:
class DemsyBorning implements Borning { JPAEntity entity; DemsyBorning(JPAEntity entity) { this.entity = entity; } /** * 创建实体代理实例 * * @return * @throws Exception */ public Object create() throws Exception { //创建代理类的实例,而不是原始类的实例 return entity.getAgentMirror().born(); } public Object born(ResultSet rs, FieldMatcher fm) throws Exception { Object obj = create(); Iterator<EntityField> it = entity.fields().iterator(); while (it.hasNext()) { EntityField ef = it.next(); if (null == fm || fm.match(ef.getField().getName())) ef.fillValue(obj, rs); } return obj; } }
4 EntityMaker:
...... public Entity<?> make(DatabaseMeta db, Connection conn, Class<?> type) { ...... // Borning entity.setBorning(new DemsyBorning(entity)); ......