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

She*_*ari 54 java hibernate 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;
    }
}

cle*_*tus 81

Hibernate 按字母顺序生成列.根据这篇文章,原因如下:

它被排序以确保跨群集的确定性排序.

我们不能每次都依赖vm以相同的顺序返回方法,因此我们必须做一些事情.

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

我还发现Schema auto generation会按字母顺序为复合主键创建列,这似乎就像你的问题.此票证是关于主键中的订单更改,并且会对索引性能产生负面影响.

除了以正确顺序出现的方式命名列的解决方法之外没有其他解决办法(不,我不是在开玩笑).

  • 大声笑.我无法理解在@Column(order = 1)中有一个值是多么困难,因此它需要一个order元素然后确保正确的排序.不过,谢谢你的回答. (13认同)
  • @Shervin我觉得**很难**.`@ Column`的定义在JPA规范中指定,因此不能由Hibernate来更改它.他们需要通过工作组/社区推动将新功能纳入JPA的下一次迭代 - 这将取决于其他供应商的协议. (7认同)
  • 2022年了还一事无成吗?请 (5认同)
  • 在2019年仍然没有? (3认同)
  • 实际上,请参阅@Changemyminds 的答案中的解决方案..这太神奇了,我无法理解为什么休眠人员不能采用它! (3认同)

Cha*_*nds 10

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

\n

找到关于 hibernate core 的外部库并找到该类org.hibernate.cfg.PropertyContainer并复制内容。

\n

在此输入图像描述

\n

在根文件夹中创建org.hibernate.cfg包和PropertyContainer类。
\n在此输入图像描述

\n

粘贴org.hibernate.cfg.PropertyContainer内容并将其全部替换TreeMapLinkedHashMap您的创建PropertyContainer类中。

\n
/*\n * Hibernate, Relational Persistence for Idiomatic Java\n *\n * License: GNU Lesser General Public License (LGPL), version 2.1 or later.\n * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.\n */\n\n// $Id$\n\npackage org.hibernate.cfg;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport javax.persistence.Access;\nimport javax.persistence.ManyToMany;\nimport javax.persistence.ManyToOne;\nimport javax.persistence.OneToMany;\nimport javax.persistence.OneToOne;\nimport javax.persistence.Transient;\n\nimport org.hibernate.AnnotationException;\nimport org.hibernate.MappingException;\nimport org.hibernate.annotations.ManyToAny;\nimport org.hibernate.annotations.Target;\nimport org.hibernate.annotations.Type;\nimport org.hibernate.annotations.common.reflection.XClass;\nimport org.hibernate.annotations.common.reflection.XProperty;\nimport org.hibernate.boot.jaxb.Origin;\nimport org.hibernate.boot.jaxb.SourceType;\nimport org.hibernate.cfg.annotations.HCANNHelper;\nimport org.hibernate.internal.CoreMessageLogger;\nimport org.hibernate.internal.util.StringHelper;\n\nimport org.jboss.logging.Logger;\n\n/**\n * A helper class to keep the {@code XProperty}s of a class ordered by access type.\n *\n * @author Hardy Ferentschik\n */\nclass PropertyContainer {\n//\n//    static {\n//        System.setProperty("jboss.i18n.generate-proxies", "true");\n//    }\n\n    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, PropertyContainer.class.getName());\n\n    /**\n     * The class for which this container is created.\n     */\n    private final XClass xClass;\n    private final XClass entityAtStake;\n\n    /**\n     * Holds the AccessType indicated for use at the class/container-level for cases where persistent attribute\n     * did not specify.\n     */\n    private final AccessType classLevelAccessType;\n\n    private final TreeMap<String, XProperty> persistentAttributeMap;\n\n    PropertyContainer(XClass clazz, XClass entityAtStake, AccessType defaultClassLevelAccessType) {\n        this.xClass = clazz;\n        this.entityAtStake = entityAtStake;\n\n        if ( defaultClassLevelAccessType == AccessType.DEFAULT ) {\n            // this is effectively what the old code did when AccessType.DEFAULT was passed in\n            // to getProperties(AccessType) from AnnotationBinder and InheritanceState\n            defaultClassLevelAccessType = AccessType.PROPERTY;\n        }\n\n        AccessType localClassLevelAccessType = determineLocalClassDefinedAccessStrategy();\n        assert localClassLevelAccessType != null;\n\n        this.classLevelAccessType = localClassLevelAccessType != AccessType.DEFAULT\n                ? localClassLevelAccessType\n                : defaultClassLevelAccessType;\n        assert classLevelAccessType == AccessType.FIELD || classLevelAccessType == AccessType.PROPERTY;\n\n        this.persistentAttributeMap = new TreeMap<String, XProperty>();\n\n        final List<XProperty> fields = xClass.getDeclaredProperties( AccessType.FIELD.getType() );\n        final List<XProperty> getters = xClass.getDeclaredProperties( AccessType.PROPERTY.getType() );\n\n        preFilter( fields, getters );\n\n        final Map<String,XProperty> persistentAttributesFromGetters = new HashMap<String, XProperty>();\n\n        collectPersistentAttributesUsingLocalAccessType(\n                persistentAttributeMap,\n                persistentAttributesFromGetters,\n                fields,\n                getters\n        );\n        collectPersistentAttributesUsingClassLevelAccessType(\n                persistentAttributeMap,\n                persistentAttributesFromGetters,\n                fields,\n                getters\n        );\n    }\n\n    private void preFilter(List<XProperty> fields, List<XProperty> getters) {\n        Iterator<XProperty> propertyIterator = fields.iterator();\n        while ( propertyIterator.hasNext() ) {\n            final XProperty property = propertyIterator.next();\n            if ( mustBeSkipped( property ) ) {\n                propertyIterator.remove();\n            }\n        }\n\n        propertyIterator = getters.iterator();\n        while ( propertyIterator.hasNext() ) {\n            final XProperty property = propertyIterator.next();\n            if ( mustBeSkipped( property ) ) {\n                propertyIterator.remove();\n            }\n        }\n    }\n\n    private void collectPersistentAttributesUsingLocalAccessType(\n            TreeMap<String, XProperty> persistentAttributeMap,\n            Map<String,XProperty> persistentAttributesFromGetters,\n            List<XProperty> fields,\n            List<XProperty> getters) {\n\n        // Check fields...\n        Iterator<XProperty> propertyIterator = fields.iterator();\n        while ( propertyIterator.hasNext() ) {\n            final XProperty xProperty = propertyIterator.next();\n            final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );\n            if ( localAccessAnnotation == null\n                    || localAccessAnnotation.value() != javax.persistence.AccessType.FIELD ) {\n                continue;\n            }\n\n            propertyIterator.remove();\n            persistentAttributeMap.put( xProperty.getName(), xProperty );\n        }\n\n        // Check getters...\n        propertyIterator = getters.iterator();\n        while ( propertyIterator.hasNext() ) {\n            final XProperty xProperty = propertyIterator.next();\n            final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );\n            if ( localAccessAnnotation == null\n                    || localAccessAnnotation.value() != javax.persistence.AccessType.PROPERTY ) {\n                continue;\n            }\n\n            propertyIterator.remove();\n\n            final String name = xProperty.getName();\n\n            // HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()\n            final XProperty previous = persistentAttributesFromGetters.get( name );\n            if ( previous != null ) {\n                throw new org.hibernate.boot.MappingException(\n                        LOG.ambiguousPropertyMethods(\n                                xClass.getName(),\n                                HCANNHelper.annotatedElementSignature( previous ),\n                                HCANNHelper.annotatedElementSignature( xProperty )\n                        ),\n                        new Origin( SourceType.ANNOTATION, xClass.getName() )\n                );\n            }\n\n            persistentAttributeMap.put( name, xProperty );\n            persistentAttributesFromGetters.put( name, xProperty );\n        }\n    }\n\n    private void collectPersistentAttributesUsingClassLevelAccessType(\n            TreeMap<String, XProperty> persistentAttributeMap,\n            Map<String,XProperty> persistentAttributesFromGetters,\n            List<XProperty> fields,\n            List<XProperty> getters) {\n        if ( classLevelAccessType == AccessType.FIELD ) {\n            for ( XProperty field : fields ) {\n                if ( persistentAttributeMap.containsKey( field.getName() ) ) {\n                    continue;\n                }\n\n                persistentAttributeMap.put( field.getName(), field );\n            }\n        }\n        else {\n            for ( XProperty getter : getters ) {\n                final String name = getter.getName();\n\n                // HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()\n                final XProperty previous = persistentAttributesFromGetters.get( name );\n                if ( previous != null ) {\n                    throw new org.hibernate.boot.MappingException(\n                            LOG.ambiguousPropertyMethods(\n                                    xClass.getName(),\n                                    HCANNHelper.annotatedElementSignature( previous ),\n                                    HCANNHelper.annotatedElementSignature( getter )\n                            ),\n                            new Origin( SourceType.ANNOTATION, xClass.getName() )\n                    );\n                }\n\n                if ( persistentAttributeMap.containsKey( name ) ) {\n                    continue;\n                }\n\n                persistentAttributeMap.put( getter.getName(), getter );\n                persistentAttributesFromGetters.put( name, getter );\n            }\n        }\n    }\n\n    public XClass getEntityAtStake() {\n        return entityAtStake;\n    }\n\n    public XClass getDeclaringClass() {\n        return xClass;\n    }\n\n    public AccessType getClassLevelAccessType() {\n        return classLevelAccessType;\n    }\n\n    public Collection<XProperty> getProperties() {\n        assertTypesAreResolvable();\n        return Collections.unmodifiableCollection( persistentAttributeMap.values() );\n    }\n\n    private void assertTypesAreResolvable() {\n        for ( XProperty xProperty : persistentAttributeMap.values() ) {\n            if ( !xProperty.isTypeResolved() && !discoverTypeWithoutReflection( xProperty ) ) {\n                String msg = "Property " + StringHelper.qualify( xClass.getName(), xProperty.getName() ) +\n                        " has an unbound type and no explicit target entity. Resolve this Generic usage issue" +\n                        " or set an explicit target attribute (eg @OneToMany(target=) or use an explicit @Type";\n                throw new AnnotationException( msg );\n            }\n        }\n    }\n//\n//  private void considerExplicitFieldAndPropertyAccess() {\n//      for ( XProperty property : fieldAccessMap.values() ) {\n//          Access access = property.getAnnotation( Access.class );\n//          if ( access == null ) {\n//              continue;\n//          }\n//\n//          // see "2.3.2 Explicit Access Type" of JPA 2 spec\n//          // the access type for this property is explicitly set to AccessType.FIELD, hence we have to\n//          // use field access for this property even if the default access type for the class is AccessType.PROPERTY\n//          AccessType accessType = AccessType.getAccessStrategy( access.value() );\n//            if (accessType == AccessType.FIELD) {\n//              propertyAccessMap.put(property.getName(), property);\n//          }\n//            else {\n//              LOG.debug( "Placing @Access(AccessType.FIELD) on a field does not have any effect." );\n//          }\n//      }\n//\n//      for ( XProperty property : propertyAccessMap.values() ) {\n//          Access access = property.getAnnotation( Access.class );\n//          if ( access == null ) {\n//              continue;\n//          }\n//\n//          AccessType accessType = AccessType.getAccessStrategy( access.value() );\n//\n//          // see "2.3.2 Explicit Access Type" of JPA 2 spec\n//          // the access type for this property is explicitly set to AccessType.PROPERTY, hence we have to\n//          // return use method access even if the default class access type is AccessType.FIELD\n//            if (accessType == AccessType.PROPERTY) {\n//              fieldAccessMap.put(property.getName(), property);\n//          }\n//            else {\n//              LOG.debug( "Placing @Access(AccessType.PROPERTY) on a field does not have any effect." );\n//          }\n//      }\n//  }\n\n//  /**\n//   * Retrieves all properties from the {@code xClass} with the specified access type. This method does not take\n//   * any jpa access rules/annotations into account yet.\n//   *\n//   * @param access The access type - {@code AccessType.FIELD}  or {@code AccessType.Property}\n//   *\n//   * @return A maps of the properties with the given access type keyed against their property name\n//   */\n//  private TreeMap<String, XProperty> initProperties(AccessType access) {\n//      if ( !( AccessType.PROPERTY.equals( access ) || AccessType.FIELD.equals( access ) ) ) {\n//          throw new IllegalArgumentException( "Access type has to be AccessType.FIELD or AccessType.Property" );\n//      }\n//\n//      //order so that property are used in the same order when binding native query\n//      TreeMap<String, XProperty> propertiesMap = new TreeMap<String, XProperty>();\n//      List<XProperty> properties = xClass.getDeclaredProperties( access.getType() );\n//      for ( XProperty property : properties ) {\n//          if ( mustBeSkipped( property ) ) {\n//              continue;\n//          }\n//          // HHH-10242 detect registration of the same property twice eg boolean isId() + UUID getId()\n//          XProperty oldProperty = propertiesMap.get( property.getName() );\n//          if ( oldProperty != null ) {\n//              throw new org.hibernate.boot.MappingException(\n//                      LOG.ambiguousPropertyMethods(\n//                              xClass.getName(),\n//                              HCANNHelper.annotatedElementSignature( oldProperty ),\n//                              HCANNHelper.annotatedElementSignature( property )\n//                      ),\n//                      new Origin( SourceType.ANNOTATION, xClass.getName() )\n//              );\n//          }\n//\n//          propertiesMap.put( property.getName(), property );\n//      }\n//      return propertiesMap;\n//  }\n\n    private AccessType determineLocalClassDefinedAccessStrategy() {\n        AccessType classDefinedAccessType;\n\n        AccessType hibernateDefinedAccessType = AccessType.DEFAULT;\n        AccessType jpaDefinedAccessType = AccessType.DEFAULT;\n\n        org.hibernate.annotations.AccessType accessType = xClass.getAnnotation( org.hibernate.annotations.AccessType.class );\n        if ( accessType != null ) {\n            hibernateDefinedAccessType = AccessType.getAccessStrategy( accessType.value() );\n        }\n\n        Access access = xClass.getAnnotation( Access.class );\n        if ( access != null ) {\n            jpaDefinedAccessType = AccessType.getAccessStrategy( access.value() );\n        }\n\n        if ( hibernateDefinedAccessType != AccessType.DEFAULT\n                && jpaDefinedAccessType != AccessType.DEFAULT\n                && hibernateDefinedAccessType != jpaDefinedAccessType ) {\n            throw new MappingException(\n                    "@AccessType and @Access specified with contradicting values. Use of @Access only is recommended. "\n            );\n        }\n\n        if ( hibernateDefinedAccessType != AccessType.DEFAULT ) {\n            classDefinedAccessType = hibernateDefinedAccessType;\n        }\n        else {\n            classDefinedAccessType = jpaDefinedAccessType;\n        }\n        return classDefinedAccessType;\n    }\n\n    private static boolean discoverTypeWithoutReflection(XProperty p) {\n        if ( p.isAnnotationPresent( OneToOne.class ) && !p.getAnnotation( OneToOne.class )\n                .targetEntity()\n                .equals( void.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( OneToMany.class ) && !p.getAnnotation( OneToMany.class )\n                .targetEntity()\n                .equals( void.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( ManyToOne.class ) && !p.getAnnotation( ManyToOne.class )\n                .targetEntity()\n                .equals( void.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( ManyToMany.class ) && !p.getAnnotation( ManyToMany.class )\n                .targetEntity()\n                .equals( void.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( ManyToAny.class ) ) {\n            if ( !p.isCollection() && !p.isArray() ) {\n                throw new AnnotationException( "@ManyToAny used on a non collection non array property: " + p.getName() );\n            }\n            return true;\n        }\n        else if ( p.isAnnotationPresent( Type.class ) ) {\n            return true;\n        }\n        else if ( p.isAnnotationPresent( Target.class ) ) {\n            return true;\n        }\n        return false;\n    }\n\n    private static boolean mustBeSkipped(XProperty property) {\n        //TODO make those hardcoded tests more portable (through the bytecode provider?)\n        return property.isAnnotationPresent( Transient.class )\n                || "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() )\n                || "org.hibernate.bytecode.internal.javassist.FieldHandler".equals( property.getType().getName() );\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

修复了组织类别。

\n
import javax.persistence.Column;\nimport javax.persistence.Entity;\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.Id;\n\n@Entity\npublic class Organization {\n    @Id\n    @GeneratedValue\n    private Long id;\n\n    @Column(unique = true, nullable = false)\n    private String organizationNumber;\n\n    @Column(nullable = false)\n    private String name;\n\n    public Organization() {\n    }\n\n    public Organization(String name) {\n        this.name = name;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    @SuppressWarnings("unused")\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getOrganizationNumber() {\n        return organizationNumber;\n    }\n\n    public void setOrganizationNumber(String organizationNumber) {\n        this.organizationNumber = organizationNumber;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + " " + this.organizationNumber;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

启动 Spring Boot 应用程序。在控制台中查看结果。

\n
Hibernate: create table organization (id bigint not null, organization_number varchar(255) not null, name varchar(255) not null, primary key (id)) engine=InnoDB\n
Run Code Online (Sandbox Code Playgroud)\n

在数据库中查看 desc 结果。

\n
mysql> desc organization;\n+---------------------+--------------+------+-----+---------+-------+\n| Field               | Type         | Null | Key | Default | Extra |\n+---------------------+--------------+------+-----+---------+-------+\n| id                  | bigint(20)   | NO   | PRI | NULL    |       |\n| organization_number | varchar(255) | NO   | UNI | NULL    |       |\n| name                | varchar(255) | NO   |     | NULL    |       |\n+---------------------+--------------+------+-----+---------+-------+\n3 rows in set (0.00 sec)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

关于继承的补充

\n

如果你想使用继承一些父类,如审计类。上面的答案还不够。

\n

您可以像上面的方法一样复制InheritanceState.class并创建InheritanceState.class


jos*_*uan 8

只要可以更改类成员的内部名称,您就可以设置所需的顺序,因为列的顺序取自字段名称,而不是取自 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;
Run Code Online (Sandbox Code Playgroud)

它生成下表:

  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)
Run Code Online (Sandbox Code Playgroud)

这效率不高,因为通常我们会按父母和关系类型搜索来获取孩子。

我们可以通过执行以下操作来更改私有名称而不影响其余的实现:

@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; }
Run Code Online (Sandbox Code Playgroud)

现在已经生成了:

  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)
Run Code Online (Sandbox Code Playgroud)

当然,依赖类成员的内部字典顺序并不是最好的,但我没有看到更好的解决方案。


Dat*_*eus 5

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


Rob*_*roj 5

2023 更新

Hibernate 6.2 引入了ColumnOrderingStrategy接口。默认策略现在是ColumnOrderingStrategyStandard。文档说:

标准实现按大小和名称对列进行排序,大致遵循以下顺序:order by max(physicalSizeBytes, 4), physicalSizeBytes > 2048, name

您可以了解为什么以这种方式引入和完成它的原因,而不是通过本讨论中的注释来了解。关于这个主题还有一个很好的答案。

您可以通过实现 ColumnOrderingStrategy 接口并通过hibernate.column_ordering_strategy属性或AvailableSettings.COLUMN_ORDERING_STRATEGY设置您的实现来实现您自己的策略。

您可以通过设置返回到旧的行为hibernate.column_ordering_strategy=legacy

有关更多详细信息,我写了一篇关于它的博客文章: https: //robertniestroj.hashnode.dev/ordering-columns-in-a-table-in-jpahibernate