net.ymate.platform.persistence.base.EntityMeta.java Source code

Java tutorial

Introduction

Here is the source code for net.ymate.platform.persistence.base.EntityMeta.java

Source

/*
 * 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 + '}';
        }
    }
}