Jpa自动建表浅谈,造一个轮子

在Spring项目中,偷懒的话可能需要用到jpa去自动建表,一张遵循阿里巴巴Java开发规范的表,应该具备(主键ID,创建时间,修改时间)这三个通用属性.

也就是说,所有的表都应该有如下sql:

create table xxx if not exists (
    id int not null auto_increment,
    created_at timestamp default current_timetamp,
    updated_at timestamp default current_timetamp on update current_timestamp,
    .....
)

那么,如何让我们用jpa建的entity表都有如下的开头模板呢?

EntityBase.java

import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;


@Getter
@Setter
@MappedSuperclass
public class EntityBase {
    @Id
    @GeneratedValue
    private Long id;

    @CreationTimestamp
    @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    private LocalDateTime createdAt;

    @UpdateTimestamp
    @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    private LocalDateTime updatedAt;

}

所有的entity只需要继承该类即可,无须重复造轮子.

import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;


@Getter
@Setter
@MappedSuperclass
public class TableEntity {
    @Id
    @GeneratedValue
    private Long id;

    @CreationTimestamp
    @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    private LocalDateTime createdAt;

    @UpdateTimestamp
    @Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
    private LocalDateTime updatedAt;

}

另一个问题,jpa建表的时候字段顺序如何与代码顺序相同?

jpa建表的字段顺序是按照字母序排列的,其原因是因为源码在保存字段顺序的时候使用了TreeMap(org.hibernate.cfg.PropertyContainer第89行).所以只需要覆盖此类,使用LinkedHashMap即可,不清楚map区别的可以看:

Java中各种Map的区别

所以保证字段顺序的步骤,

在包里新建一个类 org.hibernate.cfg.PropertyContainer

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */

// $Id$

package org.hibernate.cfg;

import java.util.*;
import javax.persistence.Access;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;

import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;

import org.jboss.logging.Logger;

/**
 * A helper class to keep the {@code XProperty}s of a class ordered by access type.
 *
 * @author Hardy Ferentschik
 */
class PropertyContainer {
//
//    static {
//        System.setProperty("jboss.i18n.generate-proxies", "true");
//    }

    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, PropertyContainer.class.getName());

    /**
     * The class for which this container is created.
     */
    private final XClass xClass;
    private final XClass entityAtStake;

    /**
     * Holds the AccessType indicated for use at the class/container-level for cases where persistent attribute
     * did not specify.
     */
    private final AccessType classLevelAccessType;

    private final List<XProperty> persistentAttributes;

    PropertyContainer(XClass clazz, XClass entityAtStake, AccessType defaultClassLevelAccessType) {
        this.xClass = clazz;
        this.entityAtStake = entityAtStake;

        if ( defaultClassLevelAccessType == AccessType.DEFAULT ) {
            // this is effectively what the old code did when AccessType.DEFAULT was passed in
            // to getProperties(AccessType) from AnnotationBinder and InheritanceState
            defaultClassLevelAccessType = AccessType.PROPERTY;
        }

        AccessType localClassLevelAccessType = determineLocalClassDefinedAccessStrategy();
        assert localClassLevelAccessType != null;

        this.classLevelAccessType = localClassLevelAccessType != AccessType.DEFAULT
                ? localClassLevelAccessType
                : defaultClassLevelAccessType;
        assert classLevelAccessType == AccessType.FIELD || classLevelAccessType == AccessType.PROPERTY;


        final List<XProperty> fields = xClass.getDeclaredProperties( AccessType.FIELD.getType() );
        final List<XProperty> getters = xClass.getDeclaredProperties( AccessType.PROPERTY.getType() );

        preFilter( fields, getters );

        final Map<String,XProperty> persistentAttributesFromGetters = new HashMap<String, XProperty>();

        final LinkedHashMap<String, XProperty> localAttributeMap = new LinkedHashMap<>();
        collectPersistentAttributesUsingLocalAccessType(
                xClass,
                localAttributeMap,
                persistentAttributesFromGetters,
                fields,
                getters
        );
        collectPersistentAttributesUsingClassLevelAccessType(
                xClass,
                classLevelAccessType,
                localAttributeMap,
                persistentAttributesFromGetters,
                fields,
                getters
        );
        this.persistentAttributes = verifyAndInitializePersistentAttributes( xClass, localAttributeMap );
    }

    private void preFilter(List<XProperty> fields, List<XProperty> getters) {
        Iterator<XProperty> propertyIterator = fields.iterator();
        while ( propertyIterator.hasNext() ) {
            final XProperty property = propertyIterator.next();
            if ( mustBeSkipped( property ) ) {
                propertyIterator.remove();
            }
        }

        propertyIterator = getters.iterator();
        while ( propertyIterator.hasNext() ) {
            final XProperty property = propertyIterator.next();
            if ( mustBeSkipped( property ) ) {
                propertyIterator.remove();
            }
        }
    }

    private static void collectPersistentAttributesUsingLocalAccessType(
            XClass xClass,
            LinkedHashMap<String, XProperty> persistentAttributeMap,
            Map<String,XProperty> persistentAttributesFromGetters,
            List<XProperty> fields,
            List<XProperty> getters) {

        // Check fields...
        Iterator<XProperty> propertyIterator = fields.iterator();
        while ( propertyIterator.hasNext() ) {
            final XProperty xProperty = propertyIterator.next();
            final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );
            if ( localAccessAnnotation == null
                    || localAccessAnnotation.value() != javax.persistence.AccessType.FIELD ) {
                continue;
            }

            propertyIterator.remove();
            persistentAttributeMap.put( xProperty.getName(), xProperty );
        }

        // Check getters...
        propertyIterator = getters.iterator();
        while ( propertyIterator.hasNext() ) {
            final XProperty xProperty = propertyIterator.next();
            final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );
            if ( localAccessAnnotation == null
                    || localAccessAnnotation.value() != javax.persistence.AccessType.PROPERTY ) {
                continue;
            }

            propertyIterator.remove();

            final String name = xProperty.getName();

            // HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()
            final XProperty previous = persistentAttributesFromGetters.get( name );
            if ( previous != null ) {
                throw new org.hibernate.boot.MappingException(
                        LOG.ambiguousPropertyMethods(
                                xClass.getName(),
                                HCANNHelper.annotatedElementSignature( previous ),
                                HCANNHelper.annotatedElementSignature( xProperty )
                        ),
                        new Origin( SourceType.ANNOTATION, xClass.getName() )
                );
            }

            persistentAttributeMap.put( name, xProperty );
            persistentAttributesFromGetters.put( name, xProperty );
        }
    }

    private static void collectPersistentAttributesUsingClassLevelAccessType(
            XClass xClass,
            AccessType classLevelAccessType,
            LinkedHashMap<String, XProperty> persistentAttributeMap,
            Map<String,XProperty> persistentAttributesFromGetters,
            List<XProperty> fields,
            List<XProperty> getters) {
        if ( classLevelAccessType == AccessType.FIELD ) {
            for ( XProperty field : fields ) {
                if ( persistentAttributeMap.containsKey( field.getName() ) ) {
                    continue;
                }

                persistentAttributeMap.put( field.getName(), field );
            }
        }
        else {
            for ( XProperty getter : getters ) {
                final String name = getter.getName();

                // HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()
                final XProperty previous = persistentAttributesFromGetters.get( name );
                if ( previous != null ) {
                    throw new org.hibernate.boot.MappingException(
                            LOG.ambiguousPropertyMethods(
                                    xClass.getName(),
                                    HCANNHelper.annotatedElementSignature( previous ),
                                    HCANNHelper.annotatedElementSignature( getter )
                            ),
                            new Origin( SourceType.ANNOTATION, xClass.getName() )
                    );
                }

                if ( persistentAttributeMap.containsKey( name ) ) {
                    continue;
                }

                persistentAttributeMap.put( getter.getName(), getter );
                persistentAttributesFromGetters.put( name, getter );
            }
        }
    }

    public XClass getEntityAtStake() {
        return entityAtStake;
    }

    public XClass getDeclaringClass() {
        return xClass;
    }

    public AccessType getClassLevelAccessType() {
        return classLevelAccessType;
    }

    /**
     * @deprecated Use the {@link #propertyIterator()} method instead.
     */
    @Deprecated
    public Collection<XProperty> getProperties() {
        return Collections.unmodifiableCollection( this.persistentAttributes );
    }

    public Iterable<XProperty> propertyIterator() {
        return persistentAttributes;
    }

    private static List<XProperty> verifyAndInitializePersistentAttributes(XClass xClass, Map<String, XProperty> localAttributeMap) {
        ArrayList<XProperty> output = new ArrayList( localAttributeMap.size() );
        for ( XProperty xProperty : localAttributeMap.values() ) {
            if ( !xProperty.isTypeResolved() && !discoverTypeWithoutReflection( xProperty ) ) {
                String msg = "Property " + StringHelper.qualify( xClass.getName(), xProperty.getName() ) +
                        " has an unbound type and no explicit target entity. Resolve this Generic usage issue" +
                        " or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type";
                throw new AnnotationException( msg );
            }
            output.add( xProperty );
        }
        return CollectionHelper.toSmallList( output );
    }
//
//	private void considerExplicitFieldAndPropertyAccess() {
//		for ( XProperty property : fieldAccessMap.values() ) {
//			Access access = property.getAnnotation( Access.class );
//			if ( access == null ) {
//				continue;
//			}
//
//			// see "2.3.2 Explicit Access Type" of JPA 2 spec
//			// the access type for this property is explicitly set to AccessType.FIELD, hence we have to
//			// use field access for this property even if the default access type for the class is AccessType.PROPERTY
//			AccessType accessType = AccessType.getAccessStrategy( access.value() );
//            if (accessType == AccessType.FIELD) {
//				propertyAccessMap.put(property.getName(), property);
//			}
//            else {
//				LOG.debug( "Placing @Access(AccessType.FIELD) on a field does not have any effect." );
//			}
//		}
//
//		for ( XProperty property : propertyAccessMap.values() ) {
//			Access access = property.getAnnotation( Access.class );
//			if ( access == null ) {
//				continue;
//			}
//
//			AccessType accessType = AccessType.getAccessStrategy( access.value() );
//
//			// see "2.3.2 Explicit Access Type" of JPA 2 spec
//			// the access type for this property is explicitly set to AccessType.PROPERTY, hence we have to
//			// return use method access even if the default class access type is AccessType.FIELD
//            if (accessType == AccessType.PROPERTY) {
//				fieldAccessMap.put(property.getName(), property);
//			}
//            else {
//				LOG.debug( "Placing @Access(AccessType.PROPERTY) on a field does not have any effect." );
//			}
//		}
//	}

//	/**
//	 * Retrieves all properties from the {@code xClass} with the specified access type. This method does not take
//	 * any jpa access rules/annotations into account yet.
//	 *
//	 * @param access The access type - {@code AccessType.FIELD}  or {@code AccessType.Property}
//	 *
//	 * @return A maps of the properties with the given access type keyed against their property name
//	 */
//	private TreeMap<String, XProperty> initProperties(AccessType access) {
//		if ( !( AccessType.PROPERTY.equals( access ) || AccessType.FIELD.equals( access ) ) ) {
//			throw new IllegalArgumentException( "Access type has to be AccessType.FIELD or AccessType.Property" );
//		}
//
//		//order so that property are used in the same order when binding native query
//		TreeMap<String, XProperty> propertiesMap = new TreeMap<String, XProperty>();
//		List<XProperty> properties = xClass.getDeclaredProperties( access.getType() );
//		for ( XProperty property : properties ) {
//			if ( mustBeSkipped( property ) ) {
//				continue;
//			}
//			// HHH-10242 detect registration of the same property twice eg boolean isId() + UUID getId()
//			XProperty oldProperty = propertiesMap.get( property.getName() );
//			if ( oldProperty != null ) {
//				throw new org.hibernate.boot.MappingException(
//						LOG.ambiguousPropertyMethods(
//								xClass.getName(),
//								HCANNHelper.annotatedElementSignature( oldProperty ),
//								HCANNHelper.annotatedElementSignature( property )
//						),
//						new Origin( SourceType.ANNOTATION, xClass.getName() )
//				);
//			}
//
//			propertiesMap.put( property.getName(), property );
//		}
//		return propertiesMap;
//	}

    private AccessType determineLocalClassDefinedAccessStrategy() {
        AccessType classDefinedAccessType;

        AccessType hibernateDefinedAccessType = AccessType.DEFAULT;
        AccessType jpaDefinedAccessType = AccessType.DEFAULT;

        org.hibernate.annotations.AccessType accessType = xClass.getAnnotation( org.hibernate.annotations.AccessType.class );
        if ( accessType != null ) {
            hibernateDefinedAccessType = AccessType.getAccessStrategy( accessType.value() );
        }

        Access access = xClass.getAnnotation( Access.class );
        if ( access != null ) {
            jpaDefinedAccessType = AccessType.getAccessStrategy( access.value() );
        }

        if ( hibernateDefinedAccessType != AccessType.DEFAULT
                && jpaDefinedAccessType != AccessType.DEFAULT
                && hibernateDefinedAccessType != jpaDefinedAccessType ) {
            throw new MappingException(
                    "@AccessType and @Access specified with contradicting values. Use of @Access only is recommended. "
            );
        }

        if ( hibernateDefinedAccessType != AccessType.DEFAULT ) {
            classDefinedAccessType = hibernateDefinedAccessType;
        }
        else {
            classDefinedAccessType = jpaDefinedAccessType;
        }
        return classDefinedAccessType;
    }

    private static boolean discoverTypeWithoutReflection(XProperty p) {
        if ( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class )
                .targetEntity()
                .equals( void.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class )
                .targetEntity()
                .equals( void.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class )
                .targetEntity()
                .equals( void.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class )
                .targetEntity()
                .equals( void.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( ManyToAny.class ) ) {
            if ( !p.isCollection() && !p.isArray() ) {
                throw new AnnotationException( "@ManyToAny used on a non collection non array property: " + p.getName() );
            }
            return true;
        }
        else if ( p.isAnnotationPresent( Type.class ) ) {
            return true;
        }
        else if ( p.isAnnotationPresent( Target.class ) ) {
            return true;
        }
        return false;
    }

    private static boolean mustBeSkipped(XProperty property) {
        //TODO make those hardcoded tests more portable (through the bytecode provider?)
        return property.isAnnotationPresent( Transient.class )
                || "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() )
                || "org.hibernate.bytecode.internal.javassist.FieldHandler".equals( property.getType().getName() );
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值