Java tutorial
/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jef.database.meta; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.sql.SQLException; import java.util.ArrayList; 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 javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Table; import com.alibaba.fastjson.util.IOUtils; import com.github.geequery.orm.annotation.Comment; import com.github.javaparser.JavaParser; import com.github.javaparser.ParseException; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.BodyDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.ModifierSet; import com.github.javaparser.ast.body.TypeDeclaration; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import jef.accelerator.bean.BeanAccessor; import jef.accelerator.bean.FastBeanWrapperImpl; import jef.common.Entry; import jef.common.log.LogUtil; import jef.database.DbCfg; import jef.database.DbUtils; import jef.database.DebugUtil; import jef.database.Field; import jef.database.IQueryableEntity; import jef.database.OperateTarget; import jef.database.PojoWrapper; import jef.database.annotation.BindDataSource; import jef.database.annotation.EasyEntity; import jef.database.annotation.PartitionFunction; import jef.database.annotation.PartitionKey; import jef.database.annotation.PartitionTable; import jef.database.dialect.type.ColumnMapping; import jef.database.meta.AnnotationProvider.FieldAnnotationProvider; import jef.database.meta.def.IndexDef; import jef.database.meta.def.UniqueConstraintDef; import jef.database.routing.function.AbstractDateFunction; import jef.database.routing.function.HashMod1024MappingFunction; import jef.database.routing.function.MapFunction; import jef.database.routing.function.ModulusFunction; import jef.database.routing.function.RawFunc; import jef.tools.ArrayUtils; import jef.tools.JefConfiguration; import jef.tools.StringUtils; import jef.tools.reflect.BeanAccessorMapImpl; import jef.tools.reflect.BeanUtils; import jef.tools.reflect.FieldEx; @SuppressWarnings("rawtypes") public final class TableMetadata extends AbstractMetadata { /** * ?SchemaDO */ private Class<?> thisType; private BeanAccessor pojoAccessor; private Class<? extends IQueryableEntity> containerType; BeanAccessor containerAccessor; // ///////////////??////////////////////// private PartitionTable partition;// // ?? private Multimap<String, PartitionFunction> partitionFuncs; private Entry<PartitionKey, PartitionFunction>[] effectPartitionKeys; private List<ColumnMapping> pkFields = new ArrayList<>(2);// private final Map<Field, String> fieldToColumn = new IdentityHashMap<Field, String>();// ??Field??? private final Map<String, String> lowerColumnToFieldName = new HashMap<String, String>();// ??Column??Field???Column(key?) /** * * * @param clz * @param annos */ TableMetadata(Class<? extends IQueryableEntity> clz, AnnotationProvider annos) { this.containerType = clz; this.containerAccessor = FastBeanWrapperImpl.getAccessorFor(clz); this.thisType = clz; initByAnno(clz, annos); } /** * POJO * * @param varClz * @param clz * @param annos */ TableMetadata(Class<PojoWrapper> varClz, Class<?> clz, AnnotationProvider annos) { this.containerType = varClz; this.containerAccessor = new BeanAccessorMapImpl(clz); this.thisType = clz; this.pojoAccessor = FastBeanWrapperImpl.getAccessorFor(clz); initByAnno(clz, annos); } protected void initByAnno(Class<?> thisType, AnnotationProvider annos) { // schema? Table table = annos.getAnnotation(javax.persistence.Table.class); if (table != null) { if (table.schema().length() > 0) { schema = MetaHolder.getMappingSchema(table.schema());// ?? } if (table.name().length() > 0) { tableName = table.name(); } for (javax.persistence.Index index : table.indexes()) { this.indexes.add(IndexDef.valueOf(index)); } for (javax.persistence.UniqueConstraint unique : table.uniqueConstraints()) { this.uniques.add(new UniqueConstraintDef(unique)); } } if (tableName == null) { // ???? boolean needTranslate = JefConfiguration.getBoolean(DbCfg.TABLE_NAME_TRANSLATE, false); if (needTranslate) { tableName = DbUtils.upperToUnderline(thisType.getSimpleName()); } else { tableName = thisType.getSimpleName(); } } BindDataSource bindDs = annos.getAnnotation(BindDataSource.class); if (bindDs != null) { this.bindDsName = MetaHolder.getMappingSite(StringUtils.trimToNull(bindDs.value())); } Cacheable cache = annos.getAnnotation(Cacheable.class); this.cacheable = cache != null && cache.value(); EasyEntity entity = annos.getAnnotation(EasyEntity.class); if (entity != null) { this.useOuterJoin = entity.useOuterJoin(); } } /** * ? */ public PartitionTable getPartition() { return partition; } /** * ??? * * @param t */ synchronized void setPartition(PartitionTable t) { //???setPartitionpkField? if (this.pkFields instanceof ArrayList) { this.pkFields = Collections.unmodifiableList(this.pkFields); } if (t == null) return; effectPartitionKeys = withFunction(t.key()); if (effectPartitionKeys.length == 0) { effectPartitionKeys = null; return; } this.partition = t; // ???? Multimap<String, PartitionFunction> fieldKeyFn = ArrayListMultimap.create(); for (Entry<PartitionKey, PartitionFunction> entry : getEffectPartitionKeys()) { PartitionKey key = entry.getKey(); String field = key.field(); if (entry.getValue() instanceof AbstractDateFunction) { Collection<PartitionFunction> olds = fieldKeyFn.get(field); if (olds != null) { for (PartitionFunction<?> old : olds) { if (old instanceof AbstractDateFunction) { int oldLevel = ((AbstractDateFunction) old).getTimeLevel(); int level = ((AbstractDateFunction) entry.getValue()).getTimeLevel();// ???? if (level < oldLevel) { fieldKeyFn.remove(field, old); break;// ?? } else { continue;// ? } } } } } fieldKeyFn.put(field, entry.getValue()); } partitionFuncs = fieldKeyFn; } public Class<?> getThisType() { return thisType; } public Class<? extends IQueryableEntity> getContainerType() { return containerType; } public List<IndexDef> getIndexDefinition() { return indexes; } public List<ColumnMapping> getPKFields() { return pkFields; } /** * Java Field * * @param field * @param column */ public void putJavaField(Field field, ColumnMapping type, String columnName, boolean isPk) { fields.put(field.name(), field); lowerFields.put(field.name().toLowerCase(), field); fieldToColumn.put(field, columnName); String lastFieldName = lowerColumnToFieldName.put(columnName.toLowerCase(), field.name()); if (lastFieldName != null && !field.name().equals(lastFieldName)) { throw new IllegalArgumentException( String.format("The field [%s] and [%s] in [%s] have a duplicate column name [%s].", lastFieldName, field.name(), containerType.getName(), columnName)); } if (isPk) { type.setPk(true); this.pkFields.add(type); Collections.sort(pkFields, PK_COMPARE); } schemaMap.put(field, type); // super.updateAutoIncrementAndUpdate(type); if (type.isLob()) { lobNames = ArrayUtils.addElement(lobNames, field, jef.database.Field.class); } } private static final Comparator<ColumnMapping> PK_COMPARE = new Comparator<ColumnMapping>() { public int compare(ColumnMapping o1, ColumnMapping o2) { int i1 = -1; int i2 = -1; if (o1.field() instanceof Enum) { i1 = ((Enum<?>) o1.field()).ordinal(); } if (o1.field() instanceof Enum) { i2 = ((Enum<?>) o2.field()).ordinal(); } return Integer.compare(i1, i2); } }; /* * (non-Javadoc) * * @see java.lang.Object#toString() */ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Entity: [").append(containerType.getName()).append("]\n"); for (ColumnMapping m : schemaMap.values()) { String fname = m.fieldName(); sb.append(" ").append(fname); StringUtils.repeat(sb, ' ', 10 - fname.length()); sb.append('\t').append(m.get()); sb.append("\n"); } sb.setLength(sb.length() - 1); return sb.toString(); } /** * ?? * ??Annotation?/partition-conf.properties? * ??? {@link #partitPolicy} * * @return */ public Entry<PartitionKey, PartitionFunction>[] getEffectPartitionKeys() { return effectPartitionKeys; } private Entry<PartitionKey, PartitionFunction>[] withFunction(PartitionKey[] key) { @SuppressWarnings("unchecked") Entry<PartitionKey, PartitionFunction>[] result = new Entry[key.length]; for (int i = 0; i < key.length; i++) { result[i] = new Entry<PartitionKey, PartitionFunction>(key[i], createFunc(key[i])); } return result; } private static PartitionFunction<?> createFunc(PartitionKey value) { if (value.functionClass() != PartitionFunction.class) { try { String[] params = value.functionConstructorParams(); if (params.length == 0) { return value.functionClass().newInstance(); } else { Class[] clz = new Class[params.length]; for (int i = 0; i < params.length; i++) { clz[i] = String.class; } Constructor cc = value.functionClass().getConstructor(clz); cc.setAccessible(true); return (PartitionFunction<?>) cc.newInstance((Object[]) params); } } catch (Exception e) { throw new IllegalArgumentException(e); } } switch (value.function()) { case DAY: return AbstractDateFunction.DAY; case HH24: return AbstractDateFunction.HH24; case MODULUS: if (value.functionConstructorParams().length == 0 || StringUtils.isEmpty(value.functionConstructorParams()[0])) { return ModulusFunction.getDefault(); } else { return new ModulusFunction(value.functionConstructorParams()[0]); } case HASH_MOD1024_RANGE: if (value.functionConstructorParams().length == 0 || StringUtils.isEmpty(value.functionConstructorParams()[0])) { return new HashMod1024MappingFunction(); } else { int num = 0; if (value.functionConstructorParams().length > 1) { num = StringUtils.toInt(value.functionConstructorParams()[1], 0); } return new HashMod1024MappingFunction(value.functionConstructorParams()[0], num); } case MONTH: return AbstractDateFunction.MONTH; case YEAR: return AbstractDateFunction.YEAR; case YEAR_LAST2: return AbstractDateFunction.YEAR_LAST2; case YEAR_MONTH: return AbstractDateFunction.YEAR_MONTH; case YEAR_MONTH_DAY: return AbstractDateFunction.YEAR_MONTH_DAY; case WEEKDAY: return AbstractDateFunction.WEEKDAY; case RAW: return new RawFunc(value.defaultWhenFieldIsNull(), value.length()); case MAPPING: if (value.functionConstructorParams().length == 0) { throw new IllegalArgumentException( "You must config the 'functionConstructorParams' while using funcuon Map"); } int num = 0; if (value.functionConstructorParams().length > 1) { num = StringUtils.toInt(value.functionConstructorParams()[1], 0); } return new MapFunction(value.functionConstructorParams()[0], num); default: throw new IllegalArgumentException("Unknown KeyFunction:" + value.function()); } } public Multimap<String, PartitionFunction> getMinUnitFuncForEachPartitionKey() { return partitionFuncs; } void setTableName(String tableName) { this.tableName = tableName; } void setSchema(String schema) { this.schema = schema; } /** * ??metaData?????? * * @param db * @throws SQLException */ public synchronized void removeNotExistColumns(OperateTarget db) throws SQLException { Set<String> set = DebugUtil.getColumnsInLowercase(db, getTableName(true)); List<Field> removeColumn = new ArrayList<Field>(); for (Field field : fieldToColumn.keySet()) { String column = fieldToColumn.get(field).toLowerCase(); if (!set.contains(column)) { removeColumn.add(field); } } for (Field field : removeColumn) { schemaMap.remove(field); // FIXME, others are not removed LogUtil.show("The field [" + field.name() + "] was remove since column not exist in db."); } if (removeColumn.size() > 0) metaFields = null; } public IQueryableEntity newInstance() { if (pojoAccessor != null) { return new PojoWrapper(pojoAccessor.newInstance(), pojoAccessor, this, false); } else { return (IQueryableEntity) containerAccessor.newInstance(); } } public String getName() { return thisType.getName(); } public String getSimpleName() { return thisType.getSimpleName(); } public Field getFieldByLowerColumn(String columnLowercase) { return fields.get(lowerColumnToFieldName.get(columnLowercase)); } public PojoWrapper transfer(Object p, boolean isQuery) { if (p == null) return null; if (p instanceof IQueryableEntity) { throw new IllegalArgumentException(); } if (p.getClass() == this.thisType) { return new PojoWrapper(p, pojoAccessor, this, isQuery); } else { throw new IllegalArgumentException(p.getClass() + " != " + this.thisType); } } public EntityType getType() { return this.containerType == PojoWrapper.class ? EntityType.POJO : EntityType.NATIVE; } private List<Class> parents; public void addParent(Class<?> processingClz) { if (parents == null) { parents = new ArrayList<Class>(3); } parents.add(processingClz); } public boolean containsMeta(ITableMetadata type) { if (type == this) return true; if (parents == null) return false; for (Class clz : parents) { if (type.getThisType() == clz) { return true; } } return false; } @Override public BeanAccessor getContainerAccessor() { return containerAccessor; } TupleMetadata extendMeta; TupleMetadata extendContainer; @Override public TupleMetadata getExtendsTable() { return extendContainer; } @Override public Collection<ColumnMapping> getExtendedColumns() { return extendMeta == null ? Collections.<ColumnMapping>emptyList() : extendMeta.getColumnSchema(); } @Override public ColumnMapping getExtendedColumnDef(String field) { return extendMeta.getColumnDef(extendMeta.getField(field)); } @Override public Map<String, String> getColumnComments() { // ?????? Map<String, String> result = getFromSource(); // ??? { Comment comment = thisType.getAnnotation(Comment.class); if (comment != null) { result.put("#TABLE", comment.value()); } } for (ColumnMapping column : this.getColumns()) { FieldEx field = BeanUtils.getField(thisType, column.fieldName()); if (field == null) { continue; } Comment comment = field.getAnnotation(Comment.class); if (comment != null) { result.put(column.fieldName(), comment.value()); } } return result; } /** * ???? * * @return */ private Map<String, String> getFromSource() { Map<String, String> result = new HashMap<String, String>(); Class<?> type = thisType; URL url = this.getClass().getResource("/" + type.getName().replace('.', '/') + ".java"); if (url == null) { url = getFixedPathSource(type); } if (url == null) return result; try { InputStream in = url.openStream(); try { CompilationUnit unit = JavaParser.parse(in, "UTF-8"); if (unit.getTypes().isEmpty()) return result; TypeDeclaration typed = unit.getTypes().get(0); if (typed instanceof ClassOrInterfaceDeclaration) { ClassOrInterfaceDeclaration clz = (ClassOrInterfaceDeclaration) typed; String table = getContent(clz.getComment()); if (table != null) result.put("#TABLE", table); for (BodyDeclaration body : typed.getMembers()) { if (body instanceof FieldDeclaration) { FieldDeclaration field = (FieldDeclaration) body; if (ModifierSet.isStatic(field.getModifiers())) { continue; } if (field.getVariables().size() > 1) { continue; } String name = field.getVariables().get(0).getId().getName(); String javaDoc = getContent(field.getComment()); if (javaDoc != null) result.put(name, javaDoc); } } } } finally { IOUtils.close(in); } } catch (ParseException e) { LogUtil.exception(e); } catch (IOException e) { LogUtil.exception(e); } return result; } private String getContent(com.github.javaparser.ast.comments.Comment comment) { if (comment == null) return null; String s = comment.getContent(); return s.replaceAll("\\s*\\*", "").trim(); } /** * ?Maven???? * * @param type * @return */ private URL getFixedPathSource(Class type) { String clzPath = "/" + type.getName().replace('.', '/') + ".class"; URL url = this.getClass().getResource(clzPath); if (url == null) return null; String path = url.getPath(); path = path.substring(0, path.length() - clzPath.length()); File source = null; if (path.endsWith("/target/test-classes")) { source = new File(path.substring(0, path.length() - 20), "src/test/java"); } else if (path.endsWith("/target/classes")) { source = new File(path.substring(0, path.length() - 15), "src/main/java"); } if (source == null) return null; File java = new File(source, type.getName().replace('.', '/') + ".java"); if (java.exists()) try { return java.toURI().toURL(); } catch (MalformedURLException e) { LogUtil.exception(e); return null; } return null; } // ??Column?? public void addColumnHelper(FieldAnnotationProvider field) { Column column = field.getAnnotation(Column.class); if (column != null) { String name = column.name(); if (StringUtils.isEmpty(name)) { name = field.getName(); } lowerColumnToFieldName.put(name.toLowerCase(), field.getName()); } } }