Java jpa中生成表的顺序错误
这(应该)是一件相当简单的事情,但我正在努力 我希望生成一个如下所示的表: id organizationNumber nameJava jpa中生成表的顺序错误,java,hibernate,jpa,Java,Hibernate,Jpa,这(应该)是一件相当简单的事情,但我正在努力 我希望生成一个如下所示的表: id organizationNumber name Hibernate按字母顺序生成列。根据上述情况,原因如下: 它被分类以确保确定性 跨集群排序 我们不能依靠虚拟机来返回 方法每次按相同的顺序排列 所以我们必须做点什么 显然,它过去是按发生顺序排列的,但在3.2.0 GA和3.2.1 GA之间发生了变化 我也发现了,这似乎是你的问题。此票证是关于主键中的顺序更改,这会对索引性能产生负面影响 除了以正确的顺序命名列
Hibernate按字母顺序生成列。根据上述情况,原因如下: 它被分类以确保确定性 跨集群排序 我们不能依靠虚拟机来返回 方法每次按相同的顺序排列 所以我们必须做点什么 显然,它过去是按发生顺序排列的,但在3.2.0 GA和3.2.1 GA之间发生了变化 我也发现了,这似乎是你的问题。此票证是关于主键中的顺序更改,这会对索引性能产生负面影响
除了以正确的顺序命名列(不,我不是在开玩笑)之外,没有其他解决方法可以解决这个问题。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)
当然,依赖类成员的内部词典顺序不是最好的,但我看不到更好的解决方案。我也有同样的问题。最后,我找到了解决办法 找到关于hibernate核心的外部库,找到
org.hibernate.cfg.PropertyContainer
类并复制内容在根文件夹中创建
org.hibernate.cfg
包和PropertyContainer
类。粘贴
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() );
}
}
启动Spring启动应用程序。请在控制台中查看结果
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)
您关于为什么采用这种方式的回答是正确的,因为在注释方面没有真正的顺序。另一个解决方案是不使用模式自动生成。难道你不能使用反射、迭代字段并以这种方式构建列列表吗?LOL。我不明白在@column(order=1)中有一个值,以便它接受一个order元素,然后确保正确的排序有多困难。不过,谢谢你的回答。@Shervin我认为这很难。JPA规范中指定了
@Column
的定义,因此Hibernate无法更改它。他们需要通过工作组/社区推动在JPA的下一次迭代中包括一个新功能,这将取决于其他供应商的同意。2019年仍然没有任何结果?我建议:一旦Hibernate创建了一个表,您可以修改它,以按照您想要的方式对列进行排序。我已经这样做了。像Toad这样的工具将使通过列向上/向下选项在UI上变得非常容易。请分享,我如何使用Jpa更改我的表列向上/向下@萨巴拉尼塔努
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;
}
}
Hibernate: create table organization (id bigint not null, organization_number varchar(255) not null, name varchar(255) not null, primary key (id)) engine=InnoDB
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)