package jef.database.meta; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import jef.accelerator.bean.BeanAccessor; import jef.accelerator.bean.FastBeanWrapperImpl; import jef.common.Entry; import jef.database.DbUtils; import jef.database.Field; import jef.database.IQueryableEntity; import jef.database.PojoWrapper; import jef.database.VarObject; import jef.database.annotation.JoinType; import jef.database.annotation.PartitionFunction; import jef.database.annotation.PartitionKey; import jef.database.annotation.PartitionTable; import jef.database.dialect.ColumnType; import jef.database.dialect.type.ColumnMapping; import jef.database.dialect.type.ColumnMappings; import jef.database.meta.def.IndexDef; import jef.database.meta.extension.EfPropertiesExtensionProvider; import jef.database.meta.extension.ExtensionAccessor; import jef.tools.ArrayUtils; import jef.tools.Assert; import jef.tools.StringUtils; import jef.tools.reflect.BeanAccessorMapImpl; import jef.tools.reflect.Property; import com.google.common.collect.Multimap; public class DynamicMetadata extends AbstractMetadata { private Class<? extends IQueryableEntity> type = VarObject.class; protected Map<String, Field> lowerColumnToFieldName = new HashMap<String, Field>(10, 0.6f); private List<ColumnMapping> pkFields = new ArrayList<ColumnMapping>(); private final Set<TupleModificationListener> listeners = new HashSet<TupleModificationListener>(); private BeanAccessor containerAccessor = BeanAccessorMapImpl.INSTANCE; protected final List<ColumnMapping> orderdColumns = new ArrayList<ColumnMapping>(); /** * 创建当前元数据的对象实例。 由于2.0版开始,TupleMetadata的数据容器类型不再仅有VarObject一种, * 因此newInstance返回的不是VarObject类型。 2.0之前的代码需要改为使用{@link #newVar()}。 * * @since 2.0 */ public IQueryableEntity newInstance() { if (type == VarObject.class) { return new VarObject(this); } return (IQueryableEntity) containerAccessor.newInstance(); } @Override public String toString() { return tableName; } public String getName() { return getTableName(false); } public String getSimpleName() { return getTableName(false); } public Set<String> getAllColumnNames() { return lowerColumnToFieldName.keySet(); } /* * 特殊处理,由于半动态表在实例化过程中,通过新建的TupleField代替了原来在元模型中定义的field,Field不再是单例对象,因此只能通过名称去匹配 * 。 */ public ColumnMapping getColumnDef(Field field) { return schemaMap.get(getField(field.name())); } /** * 构造,半动态模型 * * @param parent * @param extension */ public DynamicMetadata(AbstractMetadata parent, ExtensionConfig extension) { // System.err.println("初始化动态实体模板:"+parent.getName()+" - "+extension.getName()); this.type = parent.getThisType().asSubclass(IQueryableEntity.class); BeanAccessor raw = FastBeanWrapperImpl.getAccessorFor(type); this.containerAccessor = new ExtensionAccessor(raw, extension.getName(), EfPropertiesExtensionProvider.getInstance()); this.tableName = extension.getName(); this.schema = parent.getSchema(); setBindDsName(parent.getBindDsName()); for (ColumnMapping m : parent.getColumnSchema()) { this.updateColumn(m.fieldName(), m.rawColumnName(), m.get(), m.isPk(), false); } this.refFieldsByName.putAll(parent.getRefFieldsByName()); this.refFieldsByRef.putAll(parent.getRefFieldsByRef()); this.indexes.addAll(parent.getIndexDefinition()); } public Class<?> getThisType() { return type; } public Class<? extends IQueryableEntity> getContainerType() { return type; } /** * 快速获得动态模型定义的field对象 * * @param fieldname * @return */ public Field f(String fieldname) { Field field = fields.get(fieldname); if (field == null) throw new IllegalArgumentException("There is no field '" + fieldname + "' in table " + this.tableName); return field; } public List<Field> getPKField() { if (pkFields == null) return Collections.emptyList(); return new AbstractList<Field>() { @Override public Field get(int index) { return pkFields.get(index).field(); } @Override public int size() { return pkFields.size(); } }; } /** * 定义一个列。 * <p> * 这个方法适用于java字段名和数据库列名相同的场景。如果java字段名和列名不一致,请使用 * {@link #addColumn(String, String, ColumnType, boolean)} * * @param columnName * 列名(字段名) * @param type * 数据类型 * * */ public void addColumn(String columnName, ColumnType type) { boolean pk = (type instanceof ColumnType.AutoIncrement) || (type instanceof ColumnType.GUID); updateColumn(columnName, columnName, type, pk, false); } /** * 定义一个列 * * @param fieldName * 字段名 * @param columnName * 列名 * @param type * 数据类型 * @param isPk * 是否主键 */ public void addColumn(String fieldName, String columnName, ColumnType type, boolean isPk) { updateColumn(fieldName, columnName, type, isPk, false); } /** * 更新或添加一个列 * * @param fieldName * 字段名 * @param columnName * 列名 * @param type * 数据类型 * @param isPk * 是否主键 * @return 返回 true 更新列 , false 新增列 */ public boolean updateColumn(String fieldName, String columnName, ColumnType type, boolean isPk) { return updateColumn(fieldName, columnName, type, isPk, true); } protected boolean internalUpdateColumn(Field field, String columnName, ColumnType type, boolean isPk, boolean replace) { if (isPk) { type.setNullable(false); } Field oldField = fields.get(field.name()); if (oldField != null) { if (!replace) { throw new IllegalArgumentException("The field " + field + " already exist."); } // 替换的场合,先清除旧的缓存记录 internalRemoveField(oldField); } else { replace = false;// 新建的场合 oldField = field; } ColumnMapping mType = ColumnMappings.getMapping(oldField, this, columnName, type, isPk); updateAutoIncrementAndUpdate(mType); String fieldName = field.name(); schemaMap.put(oldField, mType); orderdColumns.add(mType); fields.put(fieldName, oldField); lowerFields.put(fieldName.toLowerCase(), oldField); lowerColumnToFieldName.put(columnName.toLowerCase(), oldField); if (isPk) pkFields.add(mType); if (mType.isLob()) { lobNames = jef.tools.ArrayUtils.addElement(lobNames, oldField, jef.database.Field.class); } super.metaFields = null;// 清缓存 super.pkDim = null; for (TupleModificationListener listener : listeners) { listener.onUpdate(this, field); } return replace; } private void internalRemoveField(Field field) { // fields ColumnMapping mType = schemaMap.remove(field); orderdColumns.remove(mType); if (mType != null) { // columnToField lowerColumnToFieldName.remove(mType.lowerColumnName()); pkFields.remove(mType); lowerFields.remove(field.name().toLowerCase()); } // increMappings removeAutoIncAndAutoUpdatingField(field); if (lobNames != null) { lobNames = (Field[]) ArrayUtils.removeElement(lobNames, field); if (lobNames.length == 0) lobNames = null; } super.metaFields = null;// 清缓存 super.pkDim = null; } protected boolean updateColumn(String fieldName, String columnName, ColumnType type, boolean isPk, boolean replace) { fieldName = StringUtils.trimToNull(fieldName); Assert.notNull(fieldName); Field field = fields.get(fieldName); if (field == null) { field = new TupleField(this, fieldName); } return internalUpdateColumn(field, columnName, type, isPk, replace); } /** * 删除指定的列 * * @param fieldName * 当列名\字段名 不同时,这个方法按照字段名删除 * @return false如果没找到此列 */ public boolean removeColumnByFieldName(String fieldName) { Field field = fields.remove(fieldName); if (field == null) return false; internalRemoveField(field); for (TupleModificationListener listener : listeners) { listener.onDelete(this, field); } return true; } public Field getFieldByLowerColumn(String fieldname) { return lowerColumnToFieldName.get(fieldname); } /* * 添加多对多引用字段 */ public void addCascadeManyToMany(String fieldName, Field targetField, JoinKey... path) { CascadeConfig config = new CascadeConfig(null, (ManyToMany) null); if (path.length > 0) { config.path = new JoinPath(JoinType.INNER, path); } ColumnMapping targetFld = DbUtils.toColumnMapping(targetField); Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, targetFld, config); } /* * 添加多对多引用字段 */ public void addCascadeManyToMany(String fieldName, ITableMetadata targetClass, JoinKey... path) { CascadeConfig config = new CascadeConfig(null, (ManyToMany) null); if (path.length > 0) { config.path = new JoinPath(JoinType.INNER, path); } Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, targetClass, config); } /** * 添加一个1对1引用字段,引用实体表的DO对象 * * @param fieldName * 字段名称 * * @param target * 实体表对应的类 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeOneToOne(String fieldName, ITableMetadata target, JoinPath path) { CascadeConfig config = new CascadeConfig(null, (OneToOne) null); config.path = path; Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, target, config); } /** * 添加一个1对1引用字段,引用实体表的某个字段 * * @param fieldName * 字段名称 * * @param target * 实体表被引用字段 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeOneToOne(String fieldName, Field target, JoinPath path) { CascadeConfig config = new CascadeConfig(null, (OneToOne) null); config.path = path; ColumnMapping targetFld = DbUtils.toColumnMapping(target); Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, targetFld, config); } /** * 添加一个1对多引用字段,引用实体表的DO对象 * * @param fieldName * 字段名称 * * @param target * 实体表的对应DO对象 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeOneToMany(String fieldName, ITableMetadata target, JoinKey... path) { CascadeConfig config = new CascadeConfig(null, (OneToMany) null); if (path.length > 0) { config.path = new JoinPath(JoinType.INNER, path); } Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, target, config); } /** * 添加一个1对多引用字段,引用实体表的某个字段 * * @param fieldName * 字段名称 * * @param target * 实体表被引用字段 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeOneToMany(String fieldName, Field target, JoinKey... path) { CascadeConfig config = new CascadeConfig(null, (OneToMany) null); if (path.length > 0) { config.path = new JoinPath(JoinType.INNER, path); } ColumnMapping targetFld = DbUtils.toColumnMapping(target); Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, targetFld, config); } /** * 添加一个多对一引用字段,引用实体表的DO对象 * * @param fieldName * 字段名称 * * @param target * 实体表被引用字段 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeManyToOne(String fieldName, ITableMetadata target, JoinPath path) { CascadeConfig config = new CascadeConfig(null, (ManyToOne) null); config.path = path; Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, target, config); } /** * 添加一个多对一引用字段,引用实体表的一个字段 * * @param fieldName * 字段名称 * * @param target * 实体表被引用字段 * * @param path * 用于连接到实体表的连接提示(如果在全局中注册了关系,则此处可以省略) */ public void addCascadeManyToOne(String fieldName, Field target, JoinPath path) { CascadeConfig config = new CascadeConfig(null, (ManyToOne) null); config.path = path; ColumnMapping targetFld = DbUtils.toColumnMapping(target); Property pp = containerAccessor.getProperty(fieldName); innerAdd(pp, targetFld, config); } /** * 设置索引 * * @param fields * 这些字段将被加入到一个复合索引中 * @param comment * 索引修饰,如"unique","bitmap"。无修饰传入null或""均可。 */ public void addIndex(String[] fields, String comment) { IndexDef def=new IndexDef("",fields); def.setDefinition(comment); indexes.add(def); } /** * 设置索引 * * @param fields * 索引字段名 * @param comment * 索引修饰,如"unique","bitmap"。无修饰传入null或""均可。 */ public void addIndex(String fieldName, String comment) { addIndex(new String[] { fieldName }, comment); } public boolean isAssignableFrom(ITableMetadata type) { return type == this; } public List<IndexDef> getIndexDefinition() { return indexes; } public PartitionTable getPartition() { return null; } @SuppressWarnings("rawtypes") public Entry<PartitionKey, PartitionFunction>[] getEffectPartitionKeys() { return null; } @SuppressWarnings("rawtypes") public Multimap<String, PartitionFunction> getMinUnitFuncForEachPartitionKey() { return null; } public PojoWrapper transfer(Object p, boolean isQuery) { throw new UnsupportedOperationException(); } public EntityType getType() { return EntityType.TUPLE; } public boolean containsMeta(ITableMetadata meta) { return meta == this; } @Override public List<ColumnMapping> getPKFields() { if (pkFields == null) return Collections.emptyList(); return pkFields; } public void addListener(TupleModificationListener adapter) { this.listeners.add(adapter); } @Override public BeanAccessor getContainerAccessor() { return containerAccessor; } /** * 给子类用的构造 * * @param tableName */ protected DynamicMetadata(String tableName) { tableName = tableName.trim(); String schema = null; int index = tableName.indexOf('.'); if (index > -1) { schema = tableName.substring(0, index); tableName = tableName.substring(index + 1); } this.schema = schema; this.tableName = tableName; } /** * 给子类用的构造 * * @param schema * @param tableName */ protected DynamicMetadata(String schema, String tableName) { if (StringUtils.isBlank(tableName)) { throw new IllegalArgumentException("Invalid table name " + tableName); } this.tableName = tableName.trim(); this.schema = StringUtils.trimToNull(schema); } @Override public TupleMetadata getExtendsTable() { return null; } @Override public Collection<ColumnMapping> getExtendedColumns() { return getColumnSchema(); } @Override public ColumnMapping getExtendedColumnDef(String field) { return schemaMap.get(getField(field)); } @SuppressWarnings("unchecked") @Override public Map<String,String> getColumnComments() { return Collections.EMPTY_MAP; } }