在 jpa 中生成的表中排序错误

这应该是一件相当简单的事情,然而我正在努力。

我想要一个这样生成的表:

id
organizationNumber
name

但是,当我查看数据库时,我发现排序是错误的。有人知道如何强制 hibernate/jpa 生成排序正确的表吗?

desc Organization;
+--------------------+--------------+------+-----+---------+----------------+
| Field              | Type         | Null | Key | Default | Extra          |
+--------------------+--------------+------+-----+---------+----------------+
| id                 | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name               | varchar(255) | NO   |     | NULL    |                |
| organizationNumber | varchar(255) | NO   | UNI | NULL    |                |
+--------------------+--------------+------+-----+---------+----------------+

我的实体 bean 看起来是这样的:

@Entity
@NamedQuery(name = "allOrganizations", query = "SELECT org FROM Organization org order by name")
public class Organization {


private Long id;
private String organizationNumber;
private String name;


public Organization() {
}


public Organization(String name) {
this.name = name;
}


@Id
@GeneratedValue
public Long getId() {
return id;
}


@SuppressWarnings("unused")
private void setId(Long id) {
this.id = id;
}


@NotEmpty
@Column(unique=true, nullable=false)
public String getOrganizationNumber() {
return organizationNumber;
}
public void setOrganizationNumber(String organizationNumber) {
this.organizationNumber = organizationNumber;
}




@NotEmpty
@Column(nullable=false)
public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


@Override
public String toString() {
return this.name + " " + this.organizationNumber;
}
}
44687 次浏览

Hibernate 按 按字母顺序的顺序生成列。根据 这篇文章,原因是:

它被分类以确保确定性 跨星团排序。

我们不能依赖虚拟机来返回 方法的次序是一样的 所以我们必须做点什么。

显然,它过去是按照发生顺序排列的,但是在3.2.0 GA 和3.2.1 GA 之间发生了变化。

我还找到了 Schema auto 生成在字母顺序中为复合主键创建列,这似乎是你的问题。此票据与主键中的顺序更改有关,这会对索引性能产生负面影响。

对于这个问题没有解决办法,只有一个变通方法,即按照正确的顺序命名列(不,我不是在开玩笑)。

DataNucleus 允许扩展指定模式生成的位置,FWIW。

只要可以更改类成员的 内部名称,就可以设置所需的顺序,因为列的顺序是从字段名称获取的,而不是从 getter、 setter 或列获取的。

因此,如果类成员是私有的(按照需要) ,那么您应该只列出它们(例如,在它们前面加上“ a _”、“ b _”、“ c _”,...) ,而不改变 getter、 setter 或列名。

例如,下面的类定义:

@Id
@Column(name = "parent")
UUID parent;


@Id
@Column(name = "type", length = 10)
String type;


@Id
@Column(name = "child")
UUID child;

它生成了以下表格:

  Column   |         Type          | Collation | Nullable | Default
-----------+-----------------------+-----------+----------+---------
child     | uuid                  |           | not null |
parent    | uuid                  |           | not null |
type      | character varying(10) |           | not null |
Indexes:
"...whatever..." PRIMARY KEY, btree (child, parent, type)

这是没有效率的,因为通常我们将搜索父母和类型的关系获得的子女。

我们可以通过以下方式在不影响其余实现的情况下更改私有名称:

@Id
@Column(name = "parent")
UUID a_parent;


@Id
@Column(name = "type", length = 10)
String b_type;


@Id
@Column(name = "child")
UUID c_child;


public UUID getParent() { return a_parent; }
public UUID getChild() { return c_child; }
public String getType() { return b_type; }
public void setParent(UUID parent) { a_parent = parent; }
public void setChild(UUID child) { c_child = child; }
public void setType(String type) { b_type = type; }

因为它现在已经产生了:

  Column   |         Type          | Collation | Nullable | Default
-----------+-----------------------+-----------+----------+---------
parent    | uuid                  |           | not null |
type      | character varying(10) |           | not null |
child     | uuid                  |           | not null |
Indexes:
"...whatever..." PRIMARY KEY, btree (parent, type, child)

当然,最好不要依赖班级成员的内部字典序,但我认为没有更好的解决方案。

我也有同样的问题。最后,我找到了解决办法。

找到关于休眠核心的外部库,找到 org.hibernate.cfg.PropertyContainer类并复制内容。

enter image description here

在根文件夹中创建 org.hibernate.cfg包和 PropertyContainer类。
enter image description here

粘贴 org.hibernate.cfg.PropertyContainer内容并在创建 PropertyContainer类中将所有 TreeMap替换为 LinkedHashMap

/*
* 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
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.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 TreeMap<String, XProperty> persistentAttributeMap;


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;


this.persistentAttributeMap = new TreeMap<String, XProperty>();


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>();


collectPersistentAttributesUsingLocalAccessType(
persistentAttributeMap,
persistentAttributesFromGetters,
fields,
getters
);
collectPersistentAttributesUsingClassLevelAccessType(
persistentAttributeMap,
persistentAttributesFromGetters,
fields,
getters
);
}


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 void collectPersistentAttributesUsingLocalAccessType(
TreeMap<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 void collectPersistentAttributesUsingClassLevelAccessType(
TreeMap<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;
}


public Collection<XProperty> getProperties() {
assertTypesAreResolvable();
return Collections.unmodifiableCollection( persistentAttributeMap.values() );
}


private void assertTypesAreResolvable() {
for ( XProperty xProperty : persistentAttributeMap.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 );
}
}
}
//
//  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() );
}
}

修正了组织类。

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;


@Entity
public class Organization {
@Id
@GeneratedValue
private Long id;


@Column(unique = true, nullable = false)
private String organizationNumber;


@Column(nullable = false)
private String name;


public Organization() {
}


public Organization(String name) {
this.name = name;
}


public Long getId() {
return id;
}


@SuppressWarnings("unused")
public void setId(Long id) {
this.id = id;
}


public String getOrganizationNumber() {
return organizationNumber;
}


public void setOrganizationNumber(String organizationNumber) {
this.organizationNumber = organizationNumber;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


@Override
public String toString() {
return this.name + " " + this.organizationNumber;
}
}

启动 SpringBootApp。在控制台中查看结果。

Hibernate: create table organization (id bigint not null, organization_number varchar(255) not null, name varchar(255) not null, primary key (id)) engine=InnoDB

在数据库中查看 desc 结果。

mysql> desc organization;
+---------------------+--------------+------+-----+---------+-------+
| Field               | Type         | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| id                  | bigint(20)   | NO   | PRI | NULL    |       |
| organization_number | varchar(255) | NO   | UNI | NULL    |       |
| name                | varchar(255) | NO   |     | NULL    |       |
+---------------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

继承补编

如果你想继承一些类,比如 Auditing 类,上面的答案是不够的。

您可以像上面的方法一样复制 InheritanceState.class并将 InheritanceState.class创建到 org.hibernate.cfg package中。

修改了两行,刚刚修正了排序。

public class InheritanceState {
// ...
private void getMappedSuperclassesTillNextEntityOrdered() {


//ordered to allow proper messages on properties subclassing
XClass currentClassInHierarchy = clazz;
InheritanceState superclassState;
do {
// mark this ↓↓↓↓↓
//            classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );


// fixed the sorting, add this ↓↓↓↓↓
classesToProcessForMappedSuperclass.add( currentClassInHierarchy );
            

// ...
}
// ...
}
    

private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
//add @MappedSuperclass in the metadata
// classes from 0 to n-1 are @MappedSuperclass and should be linked
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
final InheritanceState superEntityState =
InheritanceState.getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
PersistentClass superEntity =
superEntityState != null ?
buildingContext.getMetadataCollector().getEntityBinding( superEntityState.getClazz().getName() ) :
null;
final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
        

// mark this ↓↓↓↓↓
//        for ( int index = 0; index < lastMappedSuperclass; index++ ) {


// fixed the sorting, add this ↓↓↓↓↓
for ( int index = lastMappedSuperclass; index > 0; index-- ) {
// ...
}
// ...
}
}

演示类。

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity {
@CreatedBy
@Column(name = "created_by")
private String createdBy;


@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;


@LastModifiedDate
@Column(name = "update_time")
private ZonedDateTime updatedTime;


@CreatedDate
@Column(name = "create_time")
private ZonedDateTime createTime;
}


@Entity
public class OrganizationAudit extends AuditableEntity {
// same field with Organization.class...
}

在控制台结果中。

create table organization_audit (id bigint not null, organization_number varchar(255) not null, name varchar(255) not null, created_by varchar(255), last_modified_by varchar(255), update_time datetime, create_time datetime, primary key (id)) engine=InnoDB

在数据库结果中。

mysql> desc organization_audit;
+---------------------+--------------+------+-----+---------+-------+
| Field               | Type         | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| id                  | bigint(20)   | NO   | PRI | NULL    |       |
| organization_number | varchar(255) | NO   | UNI | NULL    |       |
| name                | varchar(255) | NO   |     | NULL    |       |
| created_by          | varchar(255) | YES  |     | NULL    |       |
| last_modified_by    | varchar(255) | YES  |     | NULL    |       |
| update_time         | datetime     | YES  |     | NULL    |       |
| create_time         | datetime     | YES  |     | NULL    |       |
+---------------------+--------------+------+-----+---------+-------+
7 rows in set (0.00 sec)

示例项目是 给你

只需在数据库中使用 创造语句创建 Table,然后使用 Hibernate 向 Table 中插入一个值

只是作为示例解析可审计类的继承, JPA 不遵守 alpha 顺序,将首先创建可审计列,在这种情况下,为最后创建的可审计类的列创建可审计列!

您需要注释 @MappedSuperclass,然后使用“ create”按顺序运行,然后取消注释并使用“ ddl-auto”选项的“ update”运行。