Java tutorial
/* * Copyright 2007-2016 the original author or authors. * * 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 net.ymate.platform.persistence.base; import net.ymate.platform.core.lang.PairObject; import net.ymate.platform.core.util.ClassUtils; import net.ymate.platform.persistence.annotation.*; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * ??? * * @author (suninformation@163.com) on 2014216 ?2:20:48 * @version 1.0 */ public final class EntityMeta { private static final Map<Class<? extends IEntity>, EntityMeta> __entityMetas; static { __entityMetas = new ConcurrentHashMap<Class<? extends IEntity>, EntityMeta>(); } /** * ?? */ private String __entityName; /** * ?() */ private ShardingRule __shardingRuleAnno; /** * */ private Class<?> __primaryKeyClass; /** * ??? */ private List<String> __primaryKeys; /** * ??? */ private List<String> __autoincrementProps; /** * ???? */ private List<String> __readonlyProps; /** * -> */ private Map<String, PropertyMeta> __properties; /** * -> */ private Map<String, PropertyMeta> __fields; /** * ??-> */ private Map<String, IndexMeta> __indexes; /** * ??? */ private boolean __multiplePrimaryKey; /** * ? */ private String __comment; /** * @param targetClass * @return ??? */ public static EntityMeta createAndGet(Class<? extends IEntity> targetClass) { EntityMeta _returnMeta = __entityMetas.get(targetClass); if (_returnMeta == null) { // clazz?@Entity if (ClassUtils.isAnnotationOf(targetClass, Entity.class)) { _returnMeta = new EntityMeta( StringUtils.defaultIfBlank(targetClass.getAnnotation(Entity.class).value(), fieldNameToPropertyName(targetClass.getSimpleName(), 0)), targetClass.getAnnotation(ShardingRule.class)); // clazz?@Comment if (ClassUtils.isAnnotationOf(targetClass, Comment.class)) { _returnMeta.__comment = targetClass.getAnnotation(Comment.class).value(); } // ? __doParseProperties(targetClass, _returnMeta); // ? __doParsePrimaryKeys(targetClass, _returnMeta); // ? __doParseIndexes(targetClass, _returnMeta); // ? __entityMetas.put(targetClass, _returnMeta); } } return _returnMeta; } public static Set<Class<? extends IEntity>> getEntityClasses() { return Collections.unmodifiableSet(__entityMetas.keySet()); } /** * ????JavaBean?<br> * ??"user_name"?"UserName"<br> * * @param propertyName ?? * @return ?JavaBean? */ public static String propertyNameToFieldName(String propertyName) { if (StringUtils.contains(propertyName, '_')) { String[] _words = StringUtils.split(propertyName, '_'); if (_words != null) { if (_words.length > 1) { StringBuilder _returnBuilder = new StringBuilder(); for (String _word : _words) { _returnBuilder.append(StringUtils.capitalize(_word.toLowerCase())); } return _returnBuilder.toString(); } return StringUtils.capitalize(_words[0].toLowerCase()); } } return propertyName; } /** * JavaBean????<br> * ??"userName"?"user_name"<br> * * @param fieldName ?? * @param capitalize ???(?0-?1-?1-) * @return ? */ public static String fieldNameToPropertyName(String fieldName, int capitalize) { if (StringUtils.isNotBlank(fieldName) && !StringUtils.contains(fieldName, '_')) { String _currStr = fieldName.substring(0, 1); _currStr = capitalize <= 0 ? _currStr.toLowerCase() : _currStr.toUpperCase(); StringBuilder _returnBuilder = new StringBuilder(_currStr); for (int _idx = 1; _idx < fieldName.length(); _idx++) { _currStr = fieldName.substring(_idx, _idx + 1); if (_currStr.equals(_currStr.toUpperCase()) && !Character.isDigit(_currStr.charAt(0))) { _returnBuilder.append("_"); _currStr = capitalize > 0 ? _currStr.toUpperCase() : _currStr.toLowerCase(); } else { _currStr = capitalize > 1 ? _currStr.toUpperCase() : _currStr.toLowerCase(); } _returnBuilder.append(_currStr); } return _returnBuilder.toString(); } return fieldName; } /** * ?@Property * * @param targetClass * @param targetMeta ? */ private static void __doParseProperties(Class<? extends IEntity> targetClass, EntityMeta targetMeta) { for (Field _field : ClassUtils.getFields(targetClass, true)) { if (ClassUtils.isAnnotationOf(_field, Property.class)) { PropertyMeta _meta = __doGetPropertyMeta(_field.getAnnotation(Property.class), _field, targetMeta); if (_meta != null) { targetMeta.__properties.put(_meta.getName(), _meta); targetMeta.__fields.put(_meta.getField().getName(), _meta); } } } } private static PropertyMeta __doGetPropertyMeta(Property property, Field field, EntityMeta targetMeta) { PropertyMeta _meta = null; // ??Field String _propName = StringUtils.defaultIfBlank(property.name(), fieldNameToPropertyName(field.getName(), 0)); if (!targetMeta.containsProperty(_propName)) { field.setAccessible(true); _meta = new PropertyMeta(_propName, field, property.autoincrement(), property.sequenceName(), property.nullable(), property.unsigned(), property.length(), property.decimals(), property.type()); if (ClassUtils.isAnnotationOf(field, Default.class)) { _meta.setDefaultValue(field.getAnnotation(Default.class).value()); } if (ClassUtils.isAnnotationOf(field, Comment.class)) { _meta.setComment(field.getAnnotation(Comment.class).value()); } if (ClassUtils.isAnnotationOf(field, Readonly.class)) { _meta.setReadonly(true); targetMeta.__readonlyProps.add(_meta.getName()); } if (_meta.isAutoincrement()) { targetMeta.__autoincrementProps.add(_meta.getName()); } } return _meta; } /** * ?@Id * * @param targetClass * @param targetMeta ? */ private static void __doParsePrimaryKeys(Class<? extends IEntity> targetClass, EntityMeta targetMeta) { PairObject<Field, Id> _id = ClassUtils.getFieldAnnotationFirst(targetClass, Id.class); if (_id == null) { throw new IllegalArgumentException("Primary key annotation '@Id' not found."); } // targetMeta.__primaryKeyClass = _id.getKey().getType(); // ??? if (ClassUtils.isAnnotationOf(_id.getKey().getType(), PK.class)) { if (ClassUtils.isInterfaceOf(_id.getKey().getType(), IEntityPK.class)) { targetMeta.__multiplePrimaryKey = true; // for (Field _field : ClassUtils.getFields(_id.getKey().getType(), true)) { if (ClassUtils.isAnnotationOf(_field, Property.class)) { PropertyMeta _meta = __doGetPropertyMeta(_field.getAnnotation(Property.class), _field, targetMeta); if (_meta != null) { targetMeta.__properties.put(_meta.getName(), _meta); targetMeta.__fields.put(_meta.getField().getName(), _meta); // targetMeta.__primaryKeys.add(_meta.getName()); } } } } else { throw new IllegalArgumentException("PrimaryKey must implement interface IEntityPK"); } } else { targetMeta.__primaryKeys.add(targetMeta.__fields.get(_id.getKey().getName()).getName()); } } /** * ?@Indexes@Index * * @param targetClass * @param targetMeta ? */ private static void __doParseIndexes(Class<? extends IEntity> targetClass, EntityMeta targetMeta) { List<Index> _indexes = new ArrayList<Index>(); if (ClassUtils.isAnnotationOf(targetClass, Indexes.class)) { _indexes.addAll(Arrays.asList(targetClass.getAnnotation(Indexes.class).value())); } if (ClassUtils.isAnnotationOf(targetClass, Index.class)) { _indexes.add(targetClass.getAnnotation(Index.class)); } for (Index _index : _indexes) { // ???? if (StringUtils.isNotBlank(_index.name()) && ArrayUtils.isNotEmpty(_index.fields())) { // ?????? if (!targetMeta.containsIndex(_index.name())) { // ??? for (String _field : _index.fields()) { if (!targetMeta.containsProperty(_field)) { throw new IllegalArgumentException("Invalid index field '" + _field + "'"); } } targetMeta.__indexes.put(_index.name(), new IndexMeta(_index.name(), _index.unique(), Arrays.asList(_index.fields()))); } } } } /** * ? * * @param name ?? * @param shardingRule ?() */ private EntityMeta(String name, ShardingRule shardingRule) { this.__primaryKeys = new ArrayList<String>(); this.__autoincrementProps = new ArrayList<String>(); this.__readonlyProps = new ArrayList<String>(); this.__properties = new HashMap<String, PropertyMeta>(); this.__fields = new HashMap<String, PropertyMeta>(); this.__indexes = new HashMap<String, IndexMeta>(); // this.__entityName = name; } /** * @param indexName ?? * @return ??? */ public boolean containsIndex(String indexName) { return this.__indexes.containsKey(indexName); } /** * @param propertyName ?? * @return ??? */ public boolean containsProperty(String propertyName) { return this.__properties.containsKey(propertyName); } /** * @param fieldName ?? * @return ??? */ public boolean containsField(String fieldName) { return this.__fields.containsKey(fieldName); } /** * @return ? */ public boolean hasAutoincrement() { return !this.__autoincrementProps.isEmpty(); } /** * @param propertyName ?? * @return ? */ public boolean isAutoincrement(String propertyName) { return this.__autoincrementProps.contains(propertyName); } /** * @param propertyName ?? * @return ? */ public boolean isPrimaryKey(String propertyName) { return this.__primaryKeys.contains(propertyName); } /** * @return ??? */ public boolean isMultiplePrimaryKey() { return this.__multiplePrimaryKey; } /** * @param propertyName ?? * @return ?? */ public boolean isReadonly(String propertyName) { return this.__readonlyProps.contains(propertyName); } /** * @return ??? */ public List<String> getAutoincrementKeys() { return Collections.unmodifiableList(this.__autoincrementProps); } /** * @return ?? */ public String getEntityName() { return this.__entityName; } /** * @return ?() */ public ShardingRule getShardingRule() { return __shardingRuleAnno; } /** * @return */ public Class<?> getPrimaryKeyClass() { return this.__primaryKeyClass; } /** * @return ??? */ public List<String> getPrimaryKeys() { return Collections.unmodifiableList(this.__primaryKeys); } /** * @return ??? */ public Collection<PropertyMeta> getProperties() { return Collections.unmodifiableCollection(this.__properties.values()); } public Collection<String> getPropertyNames() { return Collections.unmodifiableCollection(this.__properties.keySet()); } public PropertyMeta getPropertyByName(String propertyName) { return this.__properties.get(propertyName); } public PropertyMeta getPropertyByField(String fieldName) { return this.__fields.get(fieldName); } /** * @return ??? */ public Collection<IndexMeta> getIndexes() { return Collections.unmodifiableCollection(this.__indexes.values()); } /** * @return ? */ public String getComment() { return this.__comment; } @Override public String toString() { return "EntityMeta{" + "entityName='" + __entityName + '\'' + ", primaryKeyClass=" + __primaryKeyClass + ", primaryKeys=" + __primaryKeys + ", autoincrementProps=" + __autoincrementProps + ", readonlyProps=" + __readonlyProps + ", properties=" + __properties + ", fields=" + __fields + ", indexes=" + __indexes + ", multiplePrimaryKey=" + __multiplePrimaryKey + ", comment='" + __comment + '\'' + '}'; } /** * ?? * * @author (suninformation@163.com) on 15/4/20 ?10:47 * @version 1.0 */ public static class PropertyMeta { // ?? private String name; // ???Field private Field field; // ? private boolean autoincrement; // ??? private String sequenceName; // ? private boolean nullable; // ?? private boolean unsigned; // ?0?? private int length; // ??0? private int decimals; // ? private Type.FIELD type; // private String defaultValue; // private String comment; // ?? private boolean readonly; public PropertyMeta(String name, Field field) { this.name = name; this.field = field; } public PropertyMeta(String name, Field field, boolean autoincrement, String sequenceName, boolean nullable, boolean unsigned, int length, int decimals, Type.FIELD type) { this.name = name; this.field = field; this.autoincrement = autoincrement; this.sequenceName = sequenceName; this.nullable = nullable; this.unsigned = unsigned; this.length = length; this.decimals = decimals; this.type = type; } public PropertyMeta(String name, Field field, boolean autoincrement, String sequenceName, boolean nullable, boolean unsigned, int length, int decimals, Type.FIELD type, String defaultValue, String comment, boolean readonly) { this.name = name; this.field = field; this.autoincrement = autoincrement; this.sequenceName = sequenceName; this.nullable = nullable; this.unsigned = unsigned; this.length = length; this.decimals = decimals; this.type = type; this.defaultValue = defaultValue; this.comment = comment; this.readonly = readonly; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Field getField() { return field; } public void setField(Field field) { this.field = field; } public boolean isAutoincrement() { return autoincrement; } public void setAutoincrement(boolean autoincrement) { this.autoincrement = autoincrement; } public String getSequenceName() { return sequenceName; } public void setSequenceName(String sequenceName) { this.sequenceName = sequenceName; } public boolean isNullable() { return nullable; } public void setNullable(boolean nullable) { this.nullable = nullable; } public boolean isUnsigned() { return unsigned; } public void setUnsigned(boolean unsigned) { this.unsigned = unsigned; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public int getDecimals() { return decimals; } public void setDecimals(int decimals) { this.decimals = decimals; } public Type.FIELD getType() { return type; } public void setType(Type.FIELD type) { this.type = type; } public String getDefaultValue() { return defaultValue; } public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public boolean isReadonly() { return readonly; } public void setReadonly(boolean readonly) { this.readonly = readonly; } @Override public String toString() { return "PropertyMeta{" + "name='" + name + '\'' + ", field=" + field + ", autoincrement=" + autoincrement + ", sequenceName='" + sequenceName + '\'' + ", nullable=" + nullable + ", unsigned=" + unsigned + ", length=" + length + ", decimals=" + decimals + ", type=" + type + ", defaultValue='" + defaultValue + '\'' + ", comment='" + comment + '\'' + ", readonly=" + readonly + '}'; } } /** * ?? * * @author (suninformation@163.com) on 15/4/20 ?18:25 * @version 1.0 */ public static class IndexMeta { // ?? private String name; // ? private boolean unique; // ??? private List<String> fields; public IndexMeta(String name, boolean unique, List<String> fields) { this.name = name; this.unique = unique; this.fields = fields; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isUnique() { return unique; } public void setUnique(boolean unique) { this.unique = unique; } public List<String> getFields() { return fields; } public void setFields(List<String> fields) { this.fields = fields; } @Override public String toString() { return "IndexMeta{" + "name='" + name + '\'' + ", unique=" + unique + ", fields=" + fields + '}'; } } }