package org.nutz.dao.impl.entity; import java.sql.ResultSet; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.nutz.dao.DaoException; import org.nutz.dao.FieldMatcher; import org.nutz.dao.entity.Entity; import org.nutz.dao.entity.EntityIndex; import org.nutz.dao.entity.LinkField; import org.nutz.dao.entity.LinkVisitor; import org.nutz.dao.entity.MappingField; import org.nutz.dao.entity.PkType; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Pojo; import org.nutz.lang.Lang; import org.nutz.lang.Mirror; import org.nutz.lang.born.BornContext; import org.nutz.lang.born.Borning; import org.nutz.lang.born.Borns; import org.nutz.lang.util.Context; /** * 记录一个实体 * * @author zozoh(zozohtnt@gmail.com) */ public class NutEntity<T> implements Entity<T> { private static final Object[] EMTRY_ARG = new Object[]{}; /** * 按照 Java 字段名索引映射字段 */ private Map<String, MappingField> byJava; /** * 按照数据库字段名索引映射字段 */ private Map<String, MappingField> byDB; /** * 按照增加顺序,记录所有映射字段 */ private List<MappingField> fields; /** * 按照顺序存储实体所有的索引 */ private List<EntityIndex> indexes; /** * 实体索引按照名称方式的散列 */ private Map<String, EntityIndex> indexMap; /** * 按顺序记录复合主键 */ private List<MappingField> theComposites; /** * 预执行宏列表 */ private List<Pojo> beforeInsertMacroes; /** * 后执行字段宏列表 */ private List<Pojo> afterInsertMacroes; /** * 所有一对一映射字段 */ protected LinkFieldSet ones; /** * 所有一对多映射字段 */ protected LinkFieldSet manys; /** * 所有多对多映射字段 */ protected LinkFieldSet manymanys; /** * 数字型主键 */ private MappingField theId; /** * 字符型主键 */ private MappingField theName; /** * 实体 Java 类型 */ protected Class<T> type; /** * 实体 Java 类型:通过 Mirror 增强 */ private Mirror<T> mirror; /** * 根据 ResultSet 创建实体的方法 */ protected Borning<T> bornByRS; /** * 根据默认构造函数或者工厂方法创建实体的方法 */ protected Borning<T> bornByDefault; /** * 实体表名 */ private EntityName tableName; /** * 实体表注释 */ private String tableComment; /** * 字段注释 */ private Map<String, String> columnComments; /** * 是否有表注释 */ private boolean hasTableComment; /** * 是否有字段注释 */ private boolean hasColumnComment; /** * 实体视图名 */ private EntityName viewName; /** * 实体补充描述 */ private Map<String, Object> metas; /** * 实体的主键类型 */ private PkType pkType; public NutEntity(Class<T> type) { this.type = type; this.mirror = Mirror.me(type); this.byJava = new HashMap<String, MappingField>(); this.byDB = new HashMap<String, MappingField>(); this.indexMap = new HashMap<String, EntityIndex>(); this.fields = new ArrayList<MappingField>(5); this.indexes = new ArrayList<EntityIndex>(3); this.theComposites = new ArrayList<MappingField>(3); this.metas = new HashMap<String, Object>(); this.columnComments = new HashMap<String, String>(); this.pkType = PkType.UNKNOWN; // 字段宏 beforeInsertMacroes = new ArrayList<Pojo>(3); afterInsertMacroes = new ArrayList<Pojo>(3); // 获得默认的构造方法 try { bornByDefault = mirror.getBorningByArgTypes(); } catch (Exception e) {} // 检查对象的创建方法 BornContext<T> bc = Borns.evalByArgTypes(type, ResultSet.class); if (null != bc) this.bornByRS = bc.getBorning(); else if (null == bornByDefault) throw new DaoException("Need non-arg constructor : " + type); // 映射 this.ones = new LinkFieldSet(); this.manys = new LinkFieldSet(); this.manymanys = new LinkFieldSet(); } public T getObject(ResultSet rs, FieldMatcher matcher) { // 构造时创建对象 if (null != bornByRS) return bornByRS.born(rs); // 通过反射每个字段逐次设置对象 T re = bornByDefault.born(EMTRY_ARG); if (null == matcher) for (MappingField fld : fields) fld.injectValue(re, rs); else for (MappingField fld : fields) if (matcher.match(fld.getName())) fld.injectValue(re, rs); // 返回构造的对象 return re; } public T getObject(Record rec) { T obj = bornByDefault.born(EMTRY_ARG); for (MappingField fld : fields) fld.injectValue(obj, rec); return obj; } /** * 当所有字段增加完成,这个方法必须被调用,用来搜索复合主键 * * @param names * 复合主键的 Java 字段名数组 */ public void checkCompositeFields(String[] names) { if (!Lang.isEmptyArray(names) && names.length > 1) { for (String name : names) { if (byJava.containsKey(name) && byJava.get(name).isCompositePk()) theComposites.add(byJava.get(name)); else throw Lang.makeThrow("Fail to find comosite field '%s' in class '%s'!", name, type.getName()); } this.pkType = PkType.COMPOSITE; } else if (null != this.theId) { this.pkType = PkType.ID; } else if (null != this.theName) { this.pkType = PkType.NAME; } } /** * 增加映射字段 * * @param field * 数据库实体字段 */ public void addMappingField(MappingField field) { if (field.isId()) theId = field; else if (field.isName()) theName = field; byJava.put(field.getName(), field); byDB.put(field.getColumnName(), field); fields.add(field); columnComments.put(field.getName(), field.getColumnComment()); } /** * * @param lnk */ public void addLinkField(LinkField lnk) { switch (lnk.getLinkType()) { case ONE: ones.add(lnk); break; case MANY: manys.add(lnk); break; case MANYMANY: manymanys.add(lnk); break; default: throw Lang.makeThrow("It is a miracle in Link field: '%s'(%s)", lnk.getName(), lnk.getEntity().getType().getName()); } } /** * 增加实体索引 * * @param index * 实体索引 */ public void addIndex(EntityIndex index) { indexes.add(index); indexMap.put(index.getName(), index); } public Context wrapAsContext(Object obj) { return new EntityObjectContext(this, obj); } public List<LinkField> visitOne(Object obj, String regex, LinkVisitor visitor) { return ones.visit(obj, regex, visitor); } public List<LinkField> visitMany(Object obj, String regex, LinkVisitor visitor) { return manys.visit(obj, regex, visitor); } public List<LinkField> visitManyMany(Object obj, String regex, LinkVisitor visitor) { return manymanys.visit(obj, regex, visitor); } public void setTableName(String namep) { this.tableName = EntityName.create(namep); } public void setTableComment(String tComment) { this.tableComment = tComment; } public void setHasTableComment(boolean hasTableComment) { this.hasTableComment = hasTableComment; } public void setHasColumnComment(boolean hasColumnComment) { this.hasColumnComment = hasColumnComment; } public void setBeforeInsertMacroes(List<Pojo> beforeInsertMacroes) { this.beforeInsertMacroes = beforeInsertMacroes; } public void setAfterInsertMacroes(List<Pojo> afterInsertMacroes) { this.afterInsertMacroes = afterInsertMacroes; } public void setViewName(String namep) { this.viewName = EntityName.create(namep); } public MappingField getField(String name) { return byJava.get(name); } public MappingField getColumn(String name) { return byDB.get(name); } public List<MappingField> getMappingFields() { return fields; } public List<LinkField> getLinkFields(String regex) { List<LinkField> reOnes = ones.getList(regex); List<LinkField> reManys = manys.getList(regex); List<LinkField> reManymanys = manymanys.getList(regex); List<LinkField> re = new ArrayList<LinkField>(reOnes.size() + reManys.size() + reManymanys.size()); re.addAll(reOnes); re.addAll(reManys); re.addAll(reManymanys); return re; } public List<MappingField> getCompositePKFields() { return this.theComposites; } public MappingField getNameField() { return this.theName; } public MappingField getIdField() { return this.theId; } public List<MappingField> getPks() { if (null != theId) return Lang.list(theId); if (null != theName) return Lang.list(theName); return theComposites; } public Class<T> getType() { return this.type; } public Mirror<T> getMirror() { return this.mirror; } public List<EntityIndex> getIndexes() { return this.indexes; } public EntityIndex getIndex(String name) { return this.indexMap.get(name); } public String getTableName() { return this.tableName.value(); } public String getViewName() { return this.viewName.value(); } public boolean addBeforeInsertMacro(Pojo pojo) { if (null != pojo) { beforeInsertMacroes.add(pojo); return true; } return false; } public boolean addAfterInsertMacro(Pojo pojo) { if (null != pojo) { afterInsertMacroes.add(pojo); return true; } return false; } public List<Pojo> cloneBeforeInsertMacroes() { List<Pojo> re = new ArrayList<Pojo>(beforeInsertMacroes.size()); for (Pojo pojo : beforeInsertMacroes) re.add(pojo.duplicate()); return re; } public List<Pojo> cloneAfterInsertMacroes() { List<Pojo> re = new ArrayList<Pojo>(afterInsertMacroes.size()); for (Pojo pojo : afterInsertMacroes) re.add(pojo.duplicate()); return re; } public PkType getPkType() { return pkType; } public Object getMeta(String key) { return metas.get(key); } public boolean hasMeta(String key) { return metas.containsKey(key); } public Map<String, Object> getMetas() { return metas; } public String toString() { return String.format("Entity<%s:%s>", getType().getName(), getTableName()); } public boolean hasTableComment() { return hasTableComment; } public String getTableComment() { return tableComment; } public boolean hasColumnComment() { return hasColumnComment; } public String getColumnComent(String columnName) { return columnComments.get(columnName); } }