自动 Hibernate 索引创建时间过长
Automatic Hibernate index creation too long
我带着 bug/problem 回来了,现在阳光明媚了。通常我会在 H2DB 上测试本地开发和更改,但据我所知,这也必须适用于 Oracle 和 MSSQL。
现在再次在oracle上测试出现了这个问题:
密钥 COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 和 COR_VIEWSETTINGSCOR_USERSETTINGS_FK1 是自动生成的,对于 oracle 数据库来说太长了。
为了了解这些键是如何创建的,我现在将向您展示实体 UserSettings 和 UserViewSettings。
提示:如果它们让您感到困惑,您可以忽略这些实体并进一步进行编辑。也许你还能帮我。
用户设置
/**
The Class UserSettings.
*/
@org.hibernate.envers.Audited
@DataObject( value = UserSettings.DATA_OBJECT_NAME )
@CRUDDefinition( supportsRead = true, supportsCreate = true, supportsUpdate = true, supportsDelete = true )
@Entity( name = UserSettings.DATA_OBJECT_NAME )
@NamedQuery( name = UserSettings.DATA_OBJECT_NAME, query = "from userSettings e where e.name = :name" )
@javax.persistence.Inheritance( strategy = javax.persistence.InheritanceType.TABLE_PER_CLASS )
@AttributeOverrides( { @AttributeOverride( name = "id", column = @Column( name = "USERSETTINGS_ID" ) )
} )
@Table( name = "COR_USERSETTINGS", indexes = {
@javax.persistence.Index( name="COR_USERSETTINGS_FK0", columnList = "SETTINGSTYPE_ID" ),
@javax.persistence.Index( name="COR_USERSETTINGS_FK1", columnList = "USER_ID" ),
}
)
public class UserSettings extends NamedRevisionEntity implements NameSettingsType, NameSettings
{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant DATA_OBJECT_NAME. */
public static final String DATA_OBJECT_NAME = "userSettings";
@javax.persistence.Basic( fetch = javax.persistence.FetchType.EAGER, optional = false )
@Column( name = "SETTINGS", nullable = false, unique = false, insertable = true, updatable = true )
@javax.persistence.Lob
private java.lang.String settings;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = false )
@javax.persistence.JoinColumn( name = "SETTINGSTYPE_ID", nullable = false, unique = false, insertable = true, updatable = true )
private SettingsType settingsType;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = true )
@javax.persistence.JoinColumn( name = "USER_ID", nullable = true, unique = false, insertable = true, updatable = true )
private User user;
public SettingsType getSettingsType()
{
return settingsType;
}
public void setSettingsType( SettingsType settingsType )
{
this.settingsType = settingsType;
}
public User getUser()
{
return user;
}
public void setUser( User user )
{
this.user = user;
}
public java.lang.String getSettings()
{
return settings;
}
public void setSettings( java.lang.String settings )
{
this.settings = settings;
}
@Override
public String getDataObjectName()
{
return DATA_OBJECT_NAME;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder( super.toString() );
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "settingsType", "SettingsType", this.settingsType ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "user", "User", this.user ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( "]" );
return builder.toString();
}
}
用户视图设置
/**
The Class UserViewSettings.
*/
@org.hibernate.envers.Audited
@DataObject( value = UserViewSettings.DATA_OBJECT_NAME )
@CRUDDefinition( supportsRead = true, supportsCreate = true, supportsUpdate = true, supportsDelete = true )
@Entity( name = UserViewSettings.DATA_OBJECT_NAME )
@AttributeOverrides( { @AttributeOverride( name = "id", column = @Column( name = "VIEWSETTINGS_ID" ) )
} )
@Table( name = "COR_VIEWSETTINGS", uniqueConstraints = {
@javax.persistence.UniqueConstraint( name="COR_VIEWSETTINGS_UNQ1", columnNames = { "NAME", "SETTINGSTYPE_ID", "VIEW_NAME", "VIEWTYPE_ID" } ),
}
, indexes = {
@javax.persistence.Index( name="COR_VIEWSETTINGS_FK0", columnList = "VIEWTYPE_ID" ),
}
)
public class UserViewSettings extends UserSettings implements NameViewName, NameViewType
{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant DATA_OBJECT_NAME. */
public static final String DATA_OBJECT_NAME = "userViewSettings";
@javax.persistence.Basic( fetch = javax.persistence.FetchType.EAGER, optional = false )
@Column( name = "VIEW_NAME", nullable = false, unique = false, insertable = true, updatable = true )
private java.lang.String viewName;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = true )
@javax.persistence.JoinColumn( name = "VIEWTYPE_ID", nullable = true, unique = false, insertable = true, updatable = true )
private ViewType viewType;
public java.lang.String getViewName()
{
return viewName;
}
public void setViewName( java.lang.String viewName )
{
this.viewName = viewName;
}
public ViewType getViewType()
{
return viewType;
}
public void setViewType( ViewType viewType )
{
this.viewType = viewType;
}
@Override
public String getDataObjectName()
{
return DATA_OBJECT_NAME;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder( super.toString() );
builder.append( ", " );
builder.append( "viewName" );
builder.append( "=" );
builder.append( this.viewName );
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "viewType", "ViewType", this.viewType ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( "]" );
return builder.toString();
}
}
使用 Hibernate 5.2 和 Oracle 11 数据库启动 Wildfly 10.0.0 然后导致自动生成的密钥 COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 和 COR_VIEWSETTINGSCOR_USERSETTINGS_FK1对于数据库来说自然是太长了。
我查看了 Hibernate 的 NamingStrategies,甚至尝试了一些,但它们没有为我更改错误。
如何影响这些密钥的生成?
编辑:
所以打开 DEBUG 给了我这个:
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_USERSETTINGS_FK0 on COR_USERSETTINGS (SETTINGSTYPE_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_USERSETTINGS_FK1 on COR_USERSETTINGS (USER_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 on COR_VIEWSETT INGS(SETTINGSTYPE_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGSCOR_USERSETTINGS_FK1 on COR_VIEWSETTINGS (USER_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGS_FK0 on COR_VIEWSETTINGS (VIEWTYPE_ID)
现在我在包 org.hibernate.boot.model.naming 中找到了 Class ImplicitIndexNameSource 但是互联网并没有真正举例说明我可以用这个做什么,它似乎很长一段时间以来都是空的 class。
编辑 2:
之前的编辑好像走错了路。我找到了创建这些日志的地方。它是从 SchemaCreatorImpl 调用的 StandardIndexExporter。所以我需要更深入地研究框架,但如果有人看到了。这是正确的道路吗?我可以修改代码让他做我想做的事吗?这似乎是 hbm2ddl 的罪魁祸首,因为在这些行中在 StandardIndexExport 中创建了索引:
final String indexNameForCreation;
if ( dialect.qualifyIndexName() ) {
indexNameForCreation = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
new QualifiedNameImpl(
index.getTable().getQualifiedTableName().getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(),
jdbcEnvironment.getIdentifierHelper().toIdentifier( index.getName() )
),
jdbcEnvironment.getDialect()
);
}
else {
indexNameForCreation = index.getName();
}
final StringBuilder buf = new StringBuilder()
.append( "create index " )
.append( indexNameForCreation )
.append( " on " )
.append( tableName )
.append( " (" );
boolean first = true;
Iterator<Column> columnItr = index.getColumnIterator();
while ( columnItr.hasNext() ) {
final Column column = columnItr.next();
if ( first ) {
first = false;
}
else {
buf.append( ", " );
}
buf.append( ( column.getQuotedName( dialect ) ) );
}
buf.append( ")" );
return new String[] { buf.toString() };
非常感谢您的帮助。这真令人沮丧
所以我让它工作了。
为未来可能发现此问题并遇到相同问题的人回答。
索引键是由hibernate引用的oracle方言创建的。所以必须要做的是实现一个自定义的 OracleDialect,它覆盖方法 getIndexExporter 并指向自定义的 IndexExporter。在此 IndexExporter 中,您可以修改键的创建方式。在我的例子中,我修复了这样的解决方案:
/**
* Gets the correct index name if it is a index for a TABLE_PER_CLASS inheritance and longer than
* 30 chars.
*
* @param index the index to decide for
* @return the correct index name
*/
private String getCorrectIndexName( Index index )
{
if ( index.getTable() instanceof DenormalizedTable && index.getName().length() > 30 )
{
String prefixedTable = index.getTable().getName();
String tableName = prefixedTable.substring( prefixedTable.indexOf( '_' ) + 1, prefixedTable.length() );
tableName = shortenName( tableName );
Iterator<Column> columnItr = index.getColumnIterator();
String reference;
if ( columnItr.hasNext() )
{
reference = extractReference( columnItr.next() );
}
else
{
/** backup strategy to prevent exceptions */
reference = shortenName( NamingHelper.INSTANCE.hashedName( index.getName() ) );
}
return tableName + "_" + reference;
}
return index.getName();
}
/**
* Extract the reference column of the index and hash the full name before shortening it with
* shortenName().
*
* @param index the index to extract the reference from.
* @return the reference with an appended _FK(hashedReference).
*/
private String extractReference( Column column )
{
String reference = column.getQuotedName( dialect );
String md5Hash = NamingHelper.INSTANCE.hashedName( reference );
md5Hash = md5Hash.substring( md5Hash.length() - 4, md5Hash.length() );
reference = shortenName( reference );
return reference + "_FK" + md5Hash;
}
/**
* Shorten the name to a maximum of 11 chars if it's longer.
*
* @param reference the reference to shorten
* @return the shortened string
*/
private static String shortenName( String reference )
{
if ( reference.length() > 11 )
{
return reference.substring( 0, 11 );
}
return reference;
}
这必须在覆盖函数 getSqlCreateStrings 中调用。更改后的行如下所示:
String indexName = getCorrectIndexName( index );
indexNameForCreation = jdbcEnvironment.getQualifiedObjectNameFormatter()
.format(
new QualifiedNameImpl( index.getTable().getQualifiedTableName().getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(), jdbcEnvironment.getIdentifierHelper().toIdentifier( indexName ) ),
jdbcEnvironment.getDialect() );
我希望对某人有所帮助。
我带着 bug/problem 回来了,现在阳光明媚了。通常我会在 H2DB 上测试本地开发和更改,但据我所知,这也必须适用于 Oracle 和 MSSQL。 现在再次在oracle上测试出现了这个问题: 密钥 COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 和 COR_VIEWSETTINGSCOR_USERSETTINGS_FK1 是自动生成的,对于 oracle 数据库来说太长了。 为了了解这些键是如何创建的,我现在将向您展示实体 UserSettings 和 UserViewSettings。 提示:如果它们让您感到困惑,您可以忽略这些实体并进一步进行编辑。也许你还能帮我。
用户设置
/**
The Class UserSettings.
*/
@org.hibernate.envers.Audited
@DataObject( value = UserSettings.DATA_OBJECT_NAME )
@CRUDDefinition( supportsRead = true, supportsCreate = true, supportsUpdate = true, supportsDelete = true )
@Entity( name = UserSettings.DATA_OBJECT_NAME )
@NamedQuery( name = UserSettings.DATA_OBJECT_NAME, query = "from userSettings e where e.name = :name" )
@javax.persistence.Inheritance( strategy = javax.persistence.InheritanceType.TABLE_PER_CLASS )
@AttributeOverrides( { @AttributeOverride( name = "id", column = @Column( name = "USERSETTINGS_ID" ) )
} )
@Table( name = "COR_USERSETTINGS", indexes = {
@javax.persistence.Index( name="COR_USERSETTINGS_FK0", columnList = "SETTINGSTYPE_ID" ),
@javax.persistence.Index( name="COR_USERSETTINGS_FK1", columnList = "USER_ID" ),
}
)
public class UserSettings extends NamedRevisionEntity implements NameSettingsType, NameSettings
{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant DATA_OBJECT_NAME. */
public static final String DATA_OBJECT_NAME = "userSettings";
@javax.persistence.Basic( fetch = javax.persistence.FetchType.EAGER, optional = false )
@Column( name = "SETTINGS", nullable = false, unique = false, insertable = true, updatable = true )
@javax.persistence.Lob
private java.lang.String settings;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = false )
@javax.persistence.JoinColumn( name = "SETTINGSTYPE_ID", nullable = false, unique = false, insertable = true, updatable = true )
private SettingsType settingsType;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = true )
@javax.persistence.JoinColumn( name = "USER_ID", nullable = true, unique = false, insertable = true, updatable = true )
private User user;
public SettingsType getSettingsType()
{
return settingsType;
}
public void setSettingsType( SettingsType settingsType )
{
this.settingsType = settingsType;
}
public User getUser()
{
return user;
}
public void setUser( User user )
{
this.user = user;
}
public java.lang.String getSettings()
{
return settings;
}
public void setSettings( java.lang.String settings )
{
this.settings = settings;
}
@Override
public String getDataObjectName()
{
return DATA_OBJECT_NAME;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder( super.toString() );
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "settingsType", "SettingsType", this.settingsType ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "user", "User", this.user ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( "]" );
return builder.toString();
}
}
用户视图设置
/**
The Class UserViewSettings.
*/
@org.hibernate.envers.Audited
@DataObject( value = UserViewSettings.DATA_OBJECT_NAME )
@CRUDDefinition( supportsRead = true, supportsCreate = true, supportsUpdate = true, supportsDelete = true )
@Entity( name = UserViewSettings.DATA_OBJECT_NAME )
@AttributeOverrides( { @AttributeOverride( name = "id", column = @Column( name = "VIEWSETTINGS_ID" ) )
} )
@Table( name = "COR_VIEWSETTINGS", uniqueConstraints = {
@javax.persistence.UniqueConstraint( name="COR_VIEWSETTINGS_UNQ1", columnNames = { "NAME", "SETTINGSTYPE_ID", "VIEW_NAME", "VIEWTYPE_ID" } ),
}
, indexes = {
@javax.persistence.Index( name="COR_VIEWSETTINGS_FK0", columnList = "VIEWTYPE_ID" ),
}
)
public class UserViewSettings extends UserSettings implements NameViewName, NameViewType
{
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant DATA_OBJECT_NAME. */
public static final String DATA_OBJECT_NAME = "userViewSettings";
@javax.persistence.Basic( fetch = javax.persistence.FetchType.EAGER, optional = false )
@Column( name = "VIEW_NAME", nullable = false, unique = false, insertable = true, updatable = true )
private java.lang.String viewName;
@javax.persistence.ManyToOne( fetch = javax.persistence.FetchType.EAGER, optional = true )
@javax.persistence.JoinColumn( name = "VIEWTYPE_ID", nullable = true, unique = false, insertable = true, updatable = true )
private ViewType viewType;
public java.lang.String getViewName()
{
return viewName;
}
public void setViewName( java.lang.String viewName )
{
this.viewName = viewName;
}
public ViewType getViewType()
{
return viewType;
}
public void setViewType( ViewType viewType )
{
this.viewType = viewType;
}
@Override
public String getDataObjectName()
{
return DATA_OBJECT_NAME;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder( super.toString() );
builder.append( ", " );
builder.append( "viewName" );
builder.append( "=" );
builder.append( this.viewName );
builder.append( ", " );
try
{
builder.append( ToStringUtils.referenceToString( "viewType", "ViewType", this.viewType ) );
}
catch( Exception ex )
{
builder.append( ex.getClass().getName() );
builder.append( ": " );
builder.append( ex.getMessage() );
}
builder.append( "]" );
return builder.toString();
}
}
使用 Hibernate 5.2 和 Oracle 11 数据库启动 Wildfly 10.0.0 然后导致自动生成的密钥 COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 和 COR_VIEWSETTINGSCOR_USERSETTINGS_FK1对于数据库来说自然是太长了。
我查看了 Hibernate 的 NamingStrategies,甚至尝试了一些,但它们没有为我更改错误。
如何影响这些密钥的生成?
编辑:
所以打开 DEBUG 给了我这个:
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_USERSETTINGS_FK0 on COR_USERSETTINGS (SETTINGSTYPE_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_USERSETTINGS_FK1 on COR_USERSETTINGS (USER_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGSCOR_USERSETTINGS_FK0 on COR_VIEWSETT INGS(SETTINGSTYPE_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGSCOR_USERSETTINGS_FK1 on COR_VIEWSETTINGS (USER_ID)
2016-11-29 09:22:03,190 DEBUG [org.hibernate.SQL] (ServerService Thread Pool -- 58) create index COR_VIEWSETTINGS_FK0 on COR_VIEWSETTINGS (VIEWTYPE_ID)
现在我在包 org.hibernate.boot.model.naming 中找到了 Class ImplicitIndexNameSource 但是互联网并没有真正举例说明我可以用这个做什么,它似乎很长一段时间以来都是空的 class。
编辑 2:
之前的编辑好像走错了路。我找到了创建这些日志的地方。它是从 SchemaCreatorImpl 调用的 StandardIndexExporter。所以我需要更深入地研究框架,但如果有人看到了。这是正确的道路吗?我可以修改代码让他做我想做的事吗?这似乎是 hbm2ddl 的罪魁祸首,因为在这些行中在 StandardIndexExport 中创建了索引:
final String indexNameForCreation;
if ( dialect.qualifyIndexName() ) {
indexNameForCreation = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
new QualifiedNameImpl(
index.getTable().getQualifiedTableName().getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(),
jdbcEnvironment.getIdentifierHelper().toIdentifier( index.getName() )
),
jdbcEnvironment.getDialect()
);
}
else {
indexNameForCreation = index.getName();
}
final StringBuilder buf = new StringBuilder()
.append( "create index " )
.append( indexNameForCreation )
.append( " on " )
.append( tableName )
.append( " (" );
boolean first = true;
Iterator<Column> columnItr = index.getColumnIterator();
while ( columnItr.hasNext() ) {
final Column column = columnItr.next();
if ( first ) {
first = false;
}
else {
buf.append( ", " );
}
buf.append( ( column.getQuotedName( dialect ) ) );
}
buf.append( ")" );
return new String[] { buf.toString() };
非常感谢您的帮助。这真令人沮丧
所以我让它工作了。 为未来可能发现此问题并遇到相同问题的人回答。
索引键是由hibernate引用的oracle方言创建的。所以必须要做的是实现一个自定义的 OracleDialect,它覆盖方法 getIndexExporter 并指向自定义的 IndexExporter。在此 IndexExporter 中,您可以修改键的创建方式。在我的例子中,我修复了这样的解决方案:
/**
* Gets the correct index name if it is a index for a TABLE_PER_CLASS inheritance and longer than
* 30 chars.
*
* @param index the index to decide for
* @return the correct index name
*/
private String getCorrectIndexName( Index index )
{
if ( index.getTable() instanceof DenormalizedTable && index.getName().length() > 30 )
{
String prefixedTable = index.getTable().getName();
String tableName = prefixedTable.substring( prefixedTable.indexOf( '_' ) + 1, prefixedTable.length() );
tableName = shortenName( tableName );
Iterator<Column> columnItr = index.getColumnIterator();
String reference;
if ( columnItr.hasNext() )
{
reference = extractReference( columnItr.next() );
}
else
{
/** backup strategy to prevent exceptions */
reference = shortenName( NamingHelper.INSTANCE.hashedName( index.getName() ) );
}
return tableName + "_" + reference;
}
return index.getName();
}
/**
* Extract the reference column of the index and hash the full name before shortening it with
* shortenName().
*
* @param index the index to extract the reference from.
* @return the reference with an appended _FK(hashedReference).
*/
private String extractReference( Column column )
{
String reference = column.getQuotedName( dialect );
String md5Hash = NamingHelper.INSTANCE.hashedName( reference );
md5Hash = md5Hash.substring( md5Hash.length() - 4, md5Hash.length() );
reference = shortenName( reference );
return reference + "_FK" + md5Hash;
}
/**
* Shorten the name to a maximum of 11 chars if it's longer.
*
* @param reference the reference to shorten
* @return the shortened string
*/
private static String shortenName( String reference )
{
if ( reference.length() > 11 )
{
return reference.substring( 0, 11 );
}
return reference;
}
这必须在覆盖函数 getSqlCreateStrings 中调用。更改后的行如下所示:
String indexName = getCorrectIndexName( index );
indexNameForCreation = jdbcEnvironment.getQualifiedObjectNameFormatter()
.format(
new QualifiedNameImpl( index.getTable().getQualifiedTableName().getCatalogName(),
index.getTable().getQualifiedTableName().getSchemaName(), jdbcEnvironment.getIdentifierHelper().toIdentifier( indexName ) ),
jdbcEnvironment.getDialect() );
我希望对某人有所帮助。