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会按字母顺序为复合主键创建列,这似乎就像你的问题.此票证是关于主键中的订单更改,并且会对索引性能产生负面影响.
除了以正确顺序出现的方式命名列的解决方法之外没有其他解决办法(不,我不是在开玩笑).
Cha*_*nds 10
我也有同样的问题。最后,我找到了解决方案。
\n找到关于 hibernate core 的外部库并找到该类org.hibernate.cfg.PropertyContainer
并复制内容。
在根文件夹中创建org.hibernate.cfg
包和PropertyContainer
类。
\n
粘贴org.hibernate.cfg.PropertyContainer
内容并将其全部替换TreeMap
到LinkedHashMap
您的创建PropertyContainer
类中。
/*\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修复了组织类别。
\nimport 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 应用程序。在控制台中查看结果。
\nHibernate: 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 结果。
\nmysql> 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您可以像上面的方法一样复制InheritanceState.class
并创建InheritanceState.class
只要可以更改类成员的内部名称,您就可以设置所需的顺序,因为列的顺序取自字段名称,而不是取自 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)
当然,依赖类成员的内部字典顺序并不是最好的,但我没有看到更好的解决方案。
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
归档时间: |
|
查看次数: |
24104 次 |
最近记录: |