SpringBoot3.3.0集成Jpa使用Hibernate生成表字段乱序问题解决(兼容含有父类的实体)

SpringBoot2.7.4中解决了JPA使用HIbernate生成表字段乱序问题后,升级到SpringBoot3.3.0后,原来Hibernate中的相关类有移动,因此重新集成。

一、版本信息

  • SpringBoot 3.3.0(截止2024-06-18最新SpringBoot仍为3.3.0)
  • JDK 21
  • Maven 3.9.6
  • 自动依赖(hibernate-core 6.5.2.Final)等(实际使用6.4.9)
  • IDEA2024.1 社区版

二、pom.xml坐标

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.0</version><!-- 2.7.4↑-->
    <relativePath/>
</parent>

<dependencies>

    ...
    <!-- jpa -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- 以下依赖在SpringBoot3.3.0 -->
    <!-- 1)由于JPA自定义按照顺序生成,因此在需在此进行显式的进行依赖引用,否则会造成PropertyContainer或InheritanceState引用其他类无效 -->
    <!-- 2)SpringBoot3.3.0原生适配Hibernate为6.5.2,这里使用6.4.9,高版本会造成对已存在表字段的无故修改,暂不知原因为何,因此暂采用低版本 -->
    <!-- hibernate-core:https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core -->
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>6.4.9.Final</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate.common/hibernate-commons-annotations -->
    <dependency>
        <groupId>org.hibernate.common</groupId>
        <artifactId>hibernate-commons-annotations</artifactId>
        <version>6.0.6.Final</version>
    </dependency>
    ...

</dependencies>

三、本地代码目录示例

注意:此两个文件与SpringBoot2.7.4中使用Hibernate的5.6.11所属位置不同!

新增两个文件
  • 新增两个文件,org目录与cn目录对齐;
  • 文件1:PropertyContainer,修改其中属性集合从TreeMap更改为ListedHashMap;
  • 文件2:InheritanceState,兼容当实体继承有父类时,自定义子父类中的表字段顺序(需根据父类属性进行调整)。

四、yml配置文件中关于jpa配置

spring:
  #配置自动建表:update:没有表新建,有表更新操作,控制台显示建表语句
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    # spring.jpa.open-in-view 属性被默认启用,需要手动配置该属性,去掉这个警告
    # open-in-view 是指延时加载的一些属性数据,可以在页面展现的时候,保持session不关闭,从而保证能在页面进行延时加载
    open-in-view: false

五、文件具体内容

1. PropertyContainer.java(实现按照实体类顺序生成表字段)

1) 源码位置

SpringBoot2.7.4集成Hibernate时位置信息
hibernate-core-5.6.11(SpringBoot2.7.4集成)
SpringBoot3.3.0集成Hibernate时位置信息
hibernate-core-6.4.9(SpringBoot3.3.0集成)

org.hibernate.boot.model.internal.InheritanceState类的位置信息与上述基本一致,下同。

2) 源码如下

package org.hibernate.boot.model.internal;

import jakarta.persistence.*;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.*;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.MappingException;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.jboss.logging.Logger;

import java.util.*;

/**
 * SpringBoot Jpa按照顺序生成数据库表字段
 * 依赖包见:hibernate-core-6.4.9.Final.jar
 * 源码参见:org.hibernate.boot.model.internal.PropertyContainer
 * 修改:将全部TreeMap修改为LinkedHashMap即可。
 * 说明:由于Entity存在继承BaseModel基类,数据库表中先以基类后以子类进行排序
 * PS:此类为其源码/或使用反编译的class代码也可。
 * <p>
 * 注意:如出现引用出现飘红情况,请在pom中显式的对Hibernate进行依赖,示例
 * <pre>
 *  <!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core -->
 *  <dependency>
 *      <groupId>org.hibernate.orm</groupId>
 *      <artifactId>hibernate-core</artifactId>
 *      <version>6.4.9.Final</version>
 *  </dependency>
 *  <!-- https://mvnrepository.com/artifact/org.hibernate.common/hibernate-commons-annotations -->
 *  <dependency>
 *      <groupId>org.hibernate.common</groupId>
 *      <artifactId>hibernate-commons-annotations</artifactId>
 *      <version>6.0.6.Final</version>
 *  </dependency>
 * </pre>
 *
 * @author qyd
 * @since 2024-06-14
 */
public class PropertyContainer {

    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;

    public 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
                || classLevelAccessType == AccessType.RECORD;


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

        preFilter(fields, getters, recordComponents);

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

        // TODO 将localAttributeMap直接以LinkedHashMap进行创建
        final Map<String, XProperty> localAttributeMap = new LinkedHashMap<>();
        // // If the record class has only record components which match up with fields and no additional getters,
        // // we can retain the property order, to match up with the record component order
        // if (!recordComponents.isEmpty() && recordComponents.size() == fields.size() && getters.isEmpty()) {
        //     localAttributeMap = new LinkedHashMap<>();
        // }
        // //otherwise we sort them in alphabetical order, since this is at least deterministic
        // else {
        //     localAttributeMap = new TreeMap<>();
        // }
        collectPersistentAttributesUsingLocalAccessType(
                xClass,
                localAttributeMap,
                persistentAttributesFromGetters,
                persistentAttributesFromComponents,
                fields,
                getters,
                recordComponents
        );
        collectPersistentAttributesUsingClassLevelAccessType(
                xClass,
                classLevelAccessType,
                localAttributeMap,
                persistentAttributesFromGetters,
                persistentAttributesFromComponents,
                fields,
                getters,
                recordComponents
        );
        this.persistentAttributes = verifyAndInitializePersistentAttributes(xClass, localAttributeMap);
    }

    private void preFilter(List<XProperty> fields, List<XProperty> getters, List<XProperty> recordComponents) {
        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();
            }
        }

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

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

        // 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() != jakarta.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() != jakarta.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);
        }

        // Check record components...
        propertyIterator = recordComponents.iterator();
        while (propertyIterator.hasNext()) {
            final XProperty xProperty = propertyIterator.next();
            final Access localAccessAnnotation = xProperty.getAnnotation(Access.class);
            if (localAccessAnnotation == null) {
                continue;
            }

            propertyIterator.remove();
            final String name = xProperty.getName();
            persistentAttributeMap.put(name, xProperty);
            persistentAttributesFromComponents.put(name, xProperty);
        }
    }

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

                persistentAttributeMap.put(name, 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 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);
            }
            // When a user uses the `property` access strategy for the entity owning an embeddable,
            // we also have to add the attributes for record components,
            // because record classes usually don't have getters, but just the record component accessors
            for (XProperty recordComponent : recordComponents) {
                final String name = recordComponent.getName();
                if (persistentAttributeMap.containsKey(name)) {
                    continue;
                }

                persistentAttributeMap.put(name, recordComponent);
                persistentAttributesFromComponents.put(name, recordComponent);
            }
        }
    }

    public XClass getEntityAtStake() {
        return entityAtStake;
    }

    public XClass getDeclaringClass() {
        return xClass;
    }

    public AccessType getClassLevelAccessType() {
        return classLevelAccessType;
    }

    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(xClass, xProperty)) {
                String msg = "Property '" + StringHelper.qualify(xClass.getName(), xProperty.getName()) +
                        "' has an unbound type and no explicit target entity (resolve this generics usage issue" +
                        " or set an explicit target attribute with '@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.DEFAULT;
        Access access = xClass.getAnnotation(Access.class);
        if (access != null) {
            classDefinedAccessType = AccessType.getAccessStrategy(access.value());
        }
        return classDefinedAccessType;
    }

    private static boolean discoverTypeWithoutReflection(XClass clazz, XProperty property) {
        if (property.isAnnotationPresent(OneToOne.class) && !property.getAnnotation(OneToOne.class)
                .targetEntity()
                .equals(void.class)) {
            return true;
        } else if (property.isAnnotationPresent(OneToMany.class) && !property.getAnnotation(OneToMany.class)
                .targetEntity()
                .equals(void.class)) {
            return true;
        } else if (property.isAnnotationPresent(ManyToOne.class) && !property.getAnnotation(ManyToOne.class)
                .targetEntity()
                .equals(void.class)) {
            return true;
        } else if (property.isAnnotationPresent(ManyToMany.class) && !property.getAnnotation(ManyToMany.class)
                .targetEntity()
                .equals(void.class)) {
            return true;
        } else if (property.isAnnotationPresent(org.hibernate.annotations.Any.class)) {
            return true;
        } else if (property.isAnnotationPresent(ManyToAny.class)) {
            if (!property.isCollection() && !property.isArray()) {
                throw new AnnotationException("Property '" + StringHelper.qualify(clazz.getName(), property.getName())
                        + "' annotated '@ManyToAny' is neither a collection nor an array");
            }
            return true;
        } else if (property.isAnnotationPresent(Basic.class)) {
            return true;
        } else if (property.isAnnotationPresent(Type.class)) {
            return true;
        } else if (property.isAnnotationPresent(JavaType.class)) {
            return true;
        } else if (property.isAnnotationPresent(JdbcTypeCode.class)) {
            return true;
        } else if (property.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());
    }
}

修改逻辑主要在于localAttributeMap变量的创建过程,原来是有条件的创建localAttributeMap变量,修改为固定使用final Map<String, XProperty> localAttributeMap = new LinkedHashMap<>();进行创建

2. InheritanceState.java(实现实体类继承指定父类时调整父类字段)

package org.hibernate.boot.model.internal;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.PersistentClass;

import jakarta.persistence.Access;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MappedSuperclass;

import static jakarta.persistence.InheritanceType.SINGLE_TABLE;
import static jakarta.persistence.InheritanceType.TABLE_PER_CLASS;
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;

/**
 * SpringBoot Jpa按照顺序生成数据库表字段
 * 1.当实体类继承有父类时,重新自定义生成表顺序,配合{@link PropertyContainer}使用!
 * 2.此类依赖父类“cn.keyidea.common.bean.BaseModel”全路径地址,移动后请更改!
 * <p>
 * 依赖包见:hibernate-core-6.4.9.Final.jar
 * 源码参见:org.hibernate.boot.model.internal.InheritanceState
 *
 * @author qyd
 * @since 2024-06-14
 */
public class InheritanceState {
    private XClass clazz;

    /**
     * Has sibling (either mappedsuperclass entity)
     */
    private boolean hasSiblings = false;

    /**
     * a mother entity is available
     */
    private boolean hasParents = false;
    private InheritanceType type;
    private boolean isEmbeddableSuperclass = false;
    private final Map<XClass, InheritanceState> inheritanceStatePerClass;
    private final List<XClass> classesToProcessForMappedSuperclass = new ArrayList<>();
    private final MetadataBuildingContext buildingContext;
    private AccessType accessType;
    private ElementsToProcess elementsToProcess;
    private Boolean hasIdClassOrEmbeddedId;

    public InheritanceState(
            XClass clazz,
            Map<XClass, InheritanceState> inheritanceStatePerClass,
            MetadataBuildingContext buildingContext) {
        this.setClazz( clazz );
        this.buildingContext = buildingContext;
        this.inheritanceStatePerClass = inheritanceStatePerClass;
        extractInheritanceType();
    }

    private void extractInheritanceType() {
        XAnnotatedElement element = getClazz();
        Inheritance inhAnn = element.getAnnotation( Inheritance.class );
        MappedSuperclass mappedSuperClass = element.getAnnotation( MappedSuperclass.class );
        if ( mappedSuperClass != null ) {
            setEmbeddableSuperclass( true );
            setType( inhAnn == null ? null : inhAnn.strategy() );
        }
        else {
            setType( inhAnn == null ? SINGLE_TABLE : inhAnn.strategy() );
        }
    }

    public boolean hasTable() {
        return !hasParents() || SINGLE_TABLE != getType();
    }

    public boolean hasDenormalizedTable() {
        return hasParents() && TABLE_PER_CLASS == getType();
    }

    public static InheritanceState getInheritanceStateOfSuperEntity(XClass clazz, Map<XClass, InheritanceState> states) {
        XClass superclass = clazz;
        do {
            superclass = superclass.getSuperclass();
            final InheritanceState currentState = states.get( superclass );
            if ( currentState != null && !currentState.isEmbeddableSuperclass() ) {
                return currentState;
            }
        }
        while ( superclass != null && !Object.class.getName().equals( superclass.getName() ) );
        return null;
    }

    public static InheritanceState getSuperclassInheritanceState(XClass clazz, Map<XClass, InheritanceState> states) {
        XClass superclass = clazz;
        do {
            superclass = superclass.getSuperclass();
            InheritanceState currentState = states.get( superclass );
            if ( currentState != null ) {
                return currentState;
            }
        }
        while ( superclass != null && !Object.class.getName().equals( superclass.getName() ) );
        return null;
    }

    public XClass getClazz() {
        return clazz;
    }

    public void setClazz(XClass clazz) {
        this.clazz = clazz;
    }

    public boolean hasSiblings() {
        return hasSiblings;
    }

    public void setHasSiblings(boolean hasSiblings) {
        this.hasSiblings = hasSiblings;
    }

    public boolean hasParents() {
        return hasParents;
    }

    public void setHasParents(boolean hasParents) {
        this.hasParents = hasParents;
    }

    public InheritanceType getType() {
        return type;
    }

    public void setType(InheritanceType type) {
        this.type = type;
    }

    public boolean isEmbeddableSuperclass() {
        return isEmbeddableSuperclass;
    }

    public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
        isEmbeddableSuperclass = embeddableSuperclass;
    }

    public ElementsToProcess postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
        //make sure we run elements to process
        getElementsToProcess();
        addMappedSuperClassInMetadata( persistenceClass );
        entityBinder.setPropertyAccessType( accessType );
        return elementsToProcess;
    }

    public XClass getClassWithIdClass(boolean evenIfSubclass) {
        if ( !evenIfSubclass && hasParents() ) {
            return null;
        }
        else if ( clazz.isAnnotationPresent( IdClass.class ) ) {
            return clazz;
        }
        else {
            final InheritanceState state = getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
            if ( state != null ) {
                return state.getClassWithIdClass( true );
            }
            else {
                return null;
            }
        }
    }

    public Boolean hasIdClassOrEmbeddedId() {
        if ( hasIdClassOrEmbeddedId == null ) {
            hasIdClassOrEmbeddedId = false;
            if ( getClassWithIdClass( true ) != null ) {
                hasIdClassOrEmbeddedId = true;
            }
            else {
                final ElementsToProcess process = getElementsToProcess();
                for ( PropertyData property : process.getElements() ) {
                    if ( property.getProperty().isAnnotationPresent( EmbeddedId.class ) ) {
                        hasIdClassOrEmbeddedId = true;
                        break;
                    }
                }
            }
        }
        return hasIdClassOrEmbeddedId;
    }

    /*
     * Get the annotated elements and determine access type from hierarchy,
     * guessing from @Id or @EmbeddedId presence if not specified.
     * Change EntityBinder by side effect
     */
    private ElementsToProcess getElementsToProcess() {
        if ( elementsToProcess == null ) {
            InheritanceState inheritanceState = inheritanceStatePerClass.get( clazz );
            assert !inheritanceState.isEmbeddableSuperclass();

            getMappedSuperclassesTillNextEntityOrdered();

            accessType = determineDefaultAccessType();

            final ArrayList<PropertyData> elements = new ArrayList<>();
            int idPropertyCount = 0;

            for ( XClass classToProcessForMappedSuperclass : classesToProcessForMappedSuperclass ) {
                PropertyContainer propertyContainer = new PropertyContainer(
                        classToProcessForMappedSuperclass,
                        clazz,
                        accessType
                );
                int currentIdPropertyCount = addElementsOfClass(
                        elements,
                        propertyContainer,
                        buildingContext
                );
                idPropertyCount += currentIdPropertyCount;
            }

            if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
                throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
                        + " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
            }
            elements.trimToSize();

            // TODO 自定义排序字段
            if (Objects.equals(elements.getFirst().getDeclaringClass().toString(), "cn.keyidea.common.bean.BaseModel")) {
                // logger.info("此表继承父类:{}", elements.get(5).getDeclaringClass().toString());
                for (int i = 0; i < 4; i++) {
                    // 由于BaseModel父类中有4个元素,因此循环4次,将父类中除id之外的四个属性移到末尾
                    moveElementToLast(elements, 1);
                }
            } else {
                // logger.info("此表不继承父类:{}", elements.get(5).getDeclaringClass().toString());
            }

            // logger.info("element={}", elements);
            // for (PropertyData element : elements) {
            //     logger.info("表字段属性:{}", element.getPropertyName());
            // }

            elementsToProcess = new ElementsToProcess( elements, idPropertyCount );
        }
        return elementsToProcess;
    }

    private AccessType determineDefaultAccessType() {
        for ( XClass xclass = clazz; xclass != null; xclass = xclass.getSuperclass() ) {
            if ( ( xclass.getSuperclass() == null || Object.class.getName().equals( xclass.getSuperclass().getName() ) )
                    && ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) )
                    && xclass.isAnnotationPresent( Access.class ) ) {
                return AccessType.getAccessStrategy( xclass.getAnnotation( Access.class ).value() );
            }
        }
        // Guess from identifier.
        // FIX: Shouldn't this be determined by the first attribute (i.e., field or property) with annotations,
        // but without an explicit Access annotation, according to JPA 2.0 spec 2.3.1: Default Access Type?
        for ( XClass xclass = clazz;
                xclass != null && !Object.class.getName().equals( xclass.getName() );
                xclass = xclass.getSuperclass() ) {
            if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
                for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
                    final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
                    if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
                        return AccessType.PROPERTY;
                    }
                }
                for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
                    final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
                    if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
                        return AccessType.FIELD;
                    }
                }
                for ( XProperty prop : xclass.getDeclaredProperties( AccessType.RECORD.getType() ) ) {
                    final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
                    if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
                        return AccessType.RECORD;
                    }
                }
            }
        }
        throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
                + " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
    }

    private void getMappedSuperclassesTillNextEntityOrdered() {
        //ordered to allow proper messages on properties subclassing
        XClass currentClassInHierarchy = clazz;
        InheritanceState superclassState;
        do {
            classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );
            XClass superClass = currentClassInHierarchy;
            do {
                superClass = superClass.getSuperclass();
                superclassState = inheritanceStatePerClass.get( superClass );
            }
            while ( superClass != null
                    && !buildingContext.getBootstrapContext().getReflectionManager().equals( superClass, Object.class )
                    && superclassState == null );

            currentClassInHierarchy = superClass;
        }
        while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
    }

    private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
        //add @MappedSuperclass in the metadata
        // classes from 0 to n-1 are @MappedSuperclass and should be linked
        final InheritanceState superEntityState = getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
        final PersistentClass superEntity =
                superEntityState != null ?
                        buildingContext.getMetadataCollector().getEntityBinding( superEntityState.getClazz().getName() ) :
                        null;
        final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
        org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
        for ( int index = 0; index < lastMappedSuperclass; index++ ) {
            org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
            final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager()
                    .toClass( classesToProcessForMappedSuperclass.get( index ) );
            //add MappedSuperclass if not already there
            mappedSuperclass = buildingContext.getMetadataCollector().getMappedSuperclass( type );
            if ( mappedSuperclass == null ) {
                mappedSuperclass = new org.hibernate.mapping.MappedSuperclass( parentSuperclass, superEntity, persistentClass.getImplicitTable() );
                mappedSuperclass.setMappedClass( type );
                buildingContext.getMetadataCollector().addMappedSuperclass( type, mappedSuperclass );
            }
        }
        if ( mappedSuperclass != null ) {
            persistentClass.setSuperMappedSuperclass( mappedSuperclass );
        }
    }

    public static final class ElementsToProcess {
        private final List<PropertyData> properties;
        private final int idPropertyCount;

        public List<PropertyData> getElements() {
            return properties;
        }

        public int getIdPropertyCount() {
            return idPropertyCount;
        }

        private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
            this.properties = properties;
            this.idPropertyCount = idPropertyCount;
        }
    }

    /**
     * 将List集中中指定元素移动到最后
     *
     * @param list  集合对象
     * @param index 集合序号
     * @param <T>   返回集合
     */
    public static <T> void moveElementToLast(List<T> list, int index) {
        if (list != null && index >= 0 && index < list.size()) {
            // 移除指定位置的元素
            T element = list.remove(index);
            // 添加该元素到列表的末尾
            list.add(element);
        }
    }
}

修改逻辑在于:在getElementsToProcess方法中获取返回的继承有父类的实体时,针对父类子类全部字段属性进行自定义排序,这里的BaseModel即为基类(父类),将父类中的字段移到表的指定位置
注意:此类强耦合基类,基类地址变更后,需修改该类中的基类地址!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容