package jef.database.meta; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import jef.common.SimpleException; import jef.common.log.LogUtil; import jef.database.DbUtils; import jef.database.Field; import jef.database.IQueryableEntity; import jef.database.cache.KeyDimension; import jef.database.dialect.ColumnType; import jef.database.dialect.DatabaseDialect; import jef.database.dialect.type.AutoIncrementMapping; import jef.database.dialect.type.ColumnMapping; import jef.database.dialect.type.VersionSupportColumn; import jef.database.meta.def.IndexDef; import jef.database.meta.def.UniqueConstraintDef; import jef.database.query.DbTable; import jef.database.query.JpqlExpression; import jef.database.query.PKQuery; import jef.database.wrapper.clause.BindSql; import jef.tools.ArrayUtils; import jef.tools.Assert; import jef.tools.reflect.ConvertUtils; import jef.tools.reflect.Property; /** * 抽象类用于简化Tablemeta的实现 * * @author jiyi * */ public abstract class AbstractMetadata implements ITableMetadata { /** * schema of the table. (it is always the username in Oracle) */ protected String schema; /** * name of the table. */ protected String tableName; /** * Always operate the table in the named datasource. */ protected String bindDsName; /** * Metadata of the columns autoincrement. */ private AutoIncrementMapping[] increMappings; /** * Metadata of the data/time columns auto-update. */ private VersionSupportColumn[] autoUpdateColumns; /** * the columnOfVersionColumn */ private VersionSupportColumn versionColumn; /** * The fields mapping to columns. */ protected List<ColumnMapping> metaFields; /** * The field of LOB */ protected Field[] lobNames; /** * 记录对应表的所有索引,当建表时使用可自动创建索引 * Revised 2016-8 JPA 2.1规范中增加的@Table的indexes属性和Index注解,因此删除EF原先自己设计的Index注解,改用标准的JPA注解 */ final List<IndexDef> indexes = new ArrayList<IndexDef>(5); /** * 记录对应表所有Unqie约束.当建表时可自动创建约束 */ final List<UniqueConstraintDef> uniques=new ArrayList<UniqueConstraintDef>(5); protected final Map<Field, ColumnMapping> schemaMap = new IdentityHashMap<Field, ColumnMapping>(); protected Map<String, Field> fields = new HashMap<String, Field>(10, 0.6f); protected Map<String, Field> lowerFields = new HashMap<String, Field>(10, 0.6f); // /////////引用索引///////////////// protected final Map<String, AbstractRefField> refFieldsByName = new HashMap<String, AbstractRefField>();// 记录所有关联和引用字段referenceFields protected final Map<Reference, List<AbstractRefField>> refFieldsByRef = new HashMap<Reference, List<AbstractRefField>>();// 记录所有的引用字段,按引用关系 protected boolean cacheable; protected boolean useOuterJoin = true; /** * 列排序器,用这个排序器确保列的输出顺序如下 1、LOB字段总是排在最后。(由于一些数据库驱动问题,这样做能提高操作的成功率,对性能也有少量帮助) * 2、按类定义中的枚举顺序排序,如果是TupleField,则直接按照字段名的ASCII顺序排列。 */ private static final Comparator<ColumnMapping> COLUMN_COMPARATOR = new Comparator<ColumnMapping>() { public int compare(ColumnMapping field1, ColumnMapping field2) { Class<? extends ColumnType> type1 = field1.get().getClass(); Class<? extends ColumnType> type2 = field2.get().getClass(); Boolean b1 = (type1 == ColumnType.Blob.class || type1 == ColumnType.Clob.class); Boolean b2 = (type2 == ColumnType.Blob.class || type2 == ColumnType.Clob.class); int result = b1.compareTo(b2); if (result == 0) { Field f1 = field1.field(); Field f2 = field2.field(); // 在分库分表的情况下,IdentityHashMap.value的内容每一次都不一致,导致建的表的字段顺序也不一致。在select // * 并使用union时会出错 // Advice by Nihf if (f1 instanceof Enum<?> && f2 instanceof Enum<?>) { return Integer.compare(((Enum<?>) f1).ordinal(), ((Enum<?>) f2).ordinal()); } else { return f1.name().compareTo(f2.name()); } } return result; } }; public Field[] getLobFieldNames() { return lobNames; } public String getBindDsName() { return bindDsName; } public ColumnMapping getColumnDef(Field field) { // 2014-10-31 // 在重构用columnMapping代替的设计过程中,会出现两类对象的混用,引起此处作一个判断拦截field(暂时还不需要) // if(field instanceof ColumnMapping) // return (ColumnMapping)field; return schemaMap.get(field); } public void setBindDsName(String bindDsName) { this.bindDsName = MetaHolder.getMappingSite(bindDsName); this.bindProfile = null; } public Collection<ColumnMapping> getColumns() { if (metaFields == null) { Collection<ColumnMapping> map = this.getColumnSchema(); ColumnMapping[] fields = map.toArray(new ColumnMapping[map.size()]); Arrays.sort(fields, COLUMN_COMPARATOR); metaFields = Arrays.asList(fields); } return metaFields; } public String getSchema() { return schema; } /** * 返回表名 * * @param withSchema * true要求带schema * @return */ public String getTableName(boolean withSchema) { if (withSchema && schema != null) return new StringBuilder(schema.length() + tableName.length() + 1).append(schema).append('.').append(tableName).toString(); return tableName; } public String getColumnName(Field fld, DatabaseDialect profile, boolean escape) { ColumnMapping mType = this.schemaMap.get(fld); if (mType != null) { return mType.getColumnName(profile, escape); } // 意外情况 if (fld instanceof JpqlExpression) { throw new UnsupportedOperationException(); } String name = profile.getObjectNameToUse(fld.name()); return escape ? DbUtils.escapeColumn(profile, name) : name; } private DbTable cachedTable; private DatabaseDialect bindProfile; protected KeyDimension pkDim; public DbTable getBaseTable(DatabaseDialect profile) { if (bindProfile != profile) { synchronized (this) { initCache(profile); } } return cachedTable; } private void initCache(DatabaseDialect profile) { bindProfile = profile; cachedTable = new DbTable(bindDsName, profile.getObjectNameToUse(getTableName(true)), false, false); } public KeyDimension getPKDimension(DatabaseDialect profile) { if (pkDim == null) { List<Serializable> pks = new ArrayList<Serializable>(); for (ColumnMapping mapping : this.getPKFields()) { pks.add((Serializable) ConvertUtils.defaultValueForBasicType(mapping.getFieldType())); } PKQuery<?> query = new PKQuery<IQueryableEntity>(this, pks, newInstance()); BindSql sql = query.toPrepareWhereSql(null, profile); KeyDimension dim = KeyDimension.forSingleTable(tableName, sql.getSql(), null, profile); pkDim = dim; } return pkDim; } public ColumnMapping findField(String left) { if (left == null) return null; Field field = lowerFields.get(left.toLowerCase()); if (field != null) { return getColumnDef(field); } return null; } public Field getField(String name) { return fields.get(name); } public Set<String> getAllFieldNames() { return fields.keySet(); } public ColumnType getColumnType(String fieldName) { Field field = fields.get(fieldName); if (field == null) { LogUtil.warn(jef.tools.StringUtils.concat("The field [", fieldName, "] does not find in ", this.getThisType().getName())); return null; } return schemaMap.get(field).get(); } public AutoIncrementMapping getFirstAutoincrementDef() { AutoIncrementMapping[] array = increMappings; if (array != null && array.length > 0) { return array[0]; } else { return null; } } public AutoIncrementMapping[] getAutoincrementDef() { if (increMappings == null) { return new AutoIncrementMapping[0]; } else { return increMappings; } } public VersionSupportColumn[] getAutoUpdateColumnDef() { return autoUpdateColumns; } protected void removeAutoIncAndAutoUpdatingField(Field oldField) { if (increMappings != null) { for (AutoIncrementMapping m : increMappings) { if (m.field() == oldField) { increMappings = (AutoIncrementMapping[]) ArrayUtils.removeElement(increMappings, m); break; } } } if (autoUpdateColumns != null) { for (VersionSupportColumn m : autoUpdateColumns) { if (m.field() == oldField) { autoUpdateColumns = (VersionSupportColumn[]) ArrayUtils.removeElement(autoUpdateColumns, m); break; } } } } protected void updateAutoIncrementAndUpdate(ColumnMapping mType) { if (mType instanceof VersionSupportColumn) { VersionSupportColumn m = (VersionSupportColumn) mType; if (m.isUpdateAlways()) { autoUpdateColumns = ArrayUtils.addElement(autoUpdateColumns, m, VersionSupportColumn.class); } if (m.isVersion()) { if(this.versionColumn!=null){ throw new IllegalArgumentException("There can be only one version column in a entity, but" + this.getName()+" has more."); } this.versionColumn = m; } } if (mType instanceof AutoIncrementMapping) { increMappings = ArrayUtils.addElement(increMappings, (AutoIncrementMapping) mType); } } private void addRefField(AbstractRefField f) { List<AbstractRefField> list = refFieldsByRef.get(f.getReference()); if (list == null) { list = new ArrayList<AbstractRefField>(); refFieldsByRef.put(f.getReference(), list); } list.add(f); refFieldsByName.put(f.getName(), f); } public Map<Reference, List<AbstractRefField>> getRefFieldsByRef() { return refFieldsByRef; } public Map<String, AbstractRefField> getRefFieldsByName() { return refFieldsByName; } public ExtensionConfig getExtension(IQueryableEntity d) { throw new UnsupportedOperationException(); } public ExtensionConfig getExtension(String key) { throw new UnsupportedOperationException(); } protected Collection<ColumnMapping> getColumnSchema() { return this.schemaMap.values(); } protected ReferenceObject innerAdd(Property pp, ITableMetadata target, CascadeConfig config) { Reference r = new Reference(target, config.getRefType(), this); if (config.path != null) { try { r.setHint(config.path); } catch (Exception e) { throw new SimpleException(e); } } ReferenceObject ref = new ReferenceObject(pp, r, config); if (pp.getType() == Object.class) { Class<?> targetContainer = target.getThisType(); if (!config.getRefType().isToOne()) { targetContainer = Collection.class; } ref.setSourceFieldType(targetContainer); } addRefField(ref); return ref; } protected ReferenceField innerAdd(Property pp, ColumnMapping targetFld, CascadeConfig config) { Assert.notNull(targetFld); Reference r = new Reference(targetFld.getMeta(), config.getRefType(), this); if (config.path != null) { r.setHint(config.path); } ReferenceField f = new ReferenceField(pp, r, targetFld, config); if (pp.getType() == Object.class) { Class<?> containerType = targetFld.getFieldType(); if (!config.getRefType().isToOne()) { containerType = Collections.class; } f.setSourceFieldType(containerType); } addRefField(f); return f; } /* * 添加一个引用字段,引用实体表的DO对象 * * @param fieldName 字段名称 * * @param target 实体表对应的类 * * @param path 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ protected ReferenceObject addCascadeField(String fieldName, ITableMetadata target, CascadeConfig config) { Property pp = getContainerAccessor().getProperty(fieldName); if (pp == null) { throw new IllegalArgumentException(fieldName + " is not exist in " + this.getName()); } return innerAdd(pp, target, config); } /* * 添加一个引用字段,引用实体表的某个字段 * * @param fieldName 字段名称 * * @param target 实体表被引用字段 * * @param path 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ protected ReferenceField addCascadeField(String fieldName, Field target, CascadeConfig config) { Assert.notNull(target); Property pp = getContainerAccessor().getProperty(fieldName); if (pp == null) { throw new IllegalArgumentException(fieldName + " is not exist in " + this.getName()); } ColumnMapping targetFld = DbUtils.toColumnMapping(target); return innerAdd(pp, targetFld, config); } public boolean isCacheable() { return cacheable; } public void setCacheable(boolean cacheable) { this.cacheable = cacheable; } public boolean isUseOuterJoin() { return useOuterJoin; } public void setUseOuterJoin(boolean useOuterJoin) { this.useOuterJoin = useOuterJoin; } public List<UniqueConstraintDef> getUniques() { return uniques; } public VersionSupportColumn getVersionColumn() { return versionColumn; } }