继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即为基类(父类),将父类中的字段移到表的指定位置
注意:此类强耦合基类,基类地址变更后,需修改该类中的基类地址!!