HHH-7969 initial @Table#indexes support

This commit is contained in:
Strong Liu 2013-02-06 00:09:08 +08:00
parent a3083aaa7b
commit 1d9b7a06a5
14 changed files with 275 additions and 40 deletions

View File

@ -556,8 +556,9 @@ public final class AnnotationBinder {
String table = ""; //might be no @Table annotation on the annotated class
String catalog = "";
List<UniqueConstraintHolder> uniqueConstraints = new ArrayList<UniqueConstraintHolder>();
javax.persistence.Table tabAnn = null;
if ( clazzToProcess.isAnnotationPresent( javax.persistence.Table.class ) ) {
javax.persistence.Table tabAnn = clazzToProcess.getAnnotation( javax.persistence.Table.class );
tabAnn = clazzToProcess.getAnnotation( javax.persistence.Table.class );
table = tabAnn.name();
schema = tabAnn.schema();
catalog = tabAnn.catalog();
@ -711,7 +712,7 @@ public final class AnnotationBinder {
//add process complementary Table definition (index & all)
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
entityBinder.processComplementaryTableDefinitions( tabAnn );
}
// parse everything discriminator column relevant in case of single table inheritance

View File

@ -22,10 +22,16 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
@ -36,6 +42,7 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
private Table table;
private final String indexName;
private final String[] columns;
private final String[] ordering;
private final Mappings mappings;
private final Ejb3Column column;
private final boolean unique;
@ -47,10 +54,47 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
this.table = table;
this.indexName = indexName;
this.columns = columns;
this.ordering = null;
this.mappings = mappings;
this.column = null;
this.unique = false;
}
//used for the new JPA 2.1 @Index
public IndexOrUniqueKeySecondPass(Table table, String indexName, String columnList, Mappings mappings, boolean unique) {
this.table = table;
StringTokenizer tokenizer = new StringTokenizer( columnList, "," );
List<String> tmp = new ArrayList<String>();
while ( tokenizer.hasMoreElements() ) {
tmp.add( tokenizer.nextToken().trim() );
}
this.indexName = StringHelper.isNotEmpty( indexName ) ? indexName : "IDX_" + table.uniqueColumnString( tmp.iterator() );
this.columns = new String[tmp.size()];
this.ordering = new String[tmp.size()];
initializeColumns(columns, ordering, tmp);
this.mappings = mappings;
this.column = null;
this.unique = unique;
}
private void initializeColumns(String[] columns, String[] ordering, List<String> list) {
for ( int i = 0, size = list.size(); i < size; i++ ) {
final String description = list.get( i );
final String tmp = description.toLowerCase();
if ( tmp.endsWith( " desc" ) ) {
columns[i] = description.substring( 0, description.length() - 5 );
ordering[i] = "desc";
}
else if ( tmp.endsWith( " asc" ) ) {
columns[i] = description.substring( 0, description.length() - 4 );
ordering[i] = "asc";
}
else {
columns[i] = description;
ordering[i] = null;
}
}
}
/**
* Build an index
@ -68,21 +112,23 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
this.columns = null;
this.mappings = mappings;
this.unique = unique;
this.ordering = null;
}
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
if ( columns != null ) {
for (String columnName : columns) {
addConstraintToColumn( columnName );
for ( int i = 0; i < columns.length; i++ ) {
final String order = ordering != null ? ordering[i] : null;
addConstraintToColumn( columns[i], order );
}
}
if ( column != null ) {
this.table = column.getTable();
addConstraintToColumn( mappings.getLogicalColumnName( column.getMappingColumn().getQuotedName(), table ) );
addConstraintToColumn( mappings.getLogicalColumnName( column.getMappingColumn().getQuotedName(), table ), null );
}
}
private void addConstraintToColumn(String columnName) {
private void addConstraintToColumn(final String columnName, final String ordering) {
Column column = table.getColumn(
new Column(
mappings.getPhysicalColumnName( columnName, table )
@ -94,8 +140,8 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
);
}
if ( unique )
table.getOrCreateUniqueKey( indexName ).addColumn( column );
table.getOrCreateUniqueKey( indexName ).addColumn( column, ordering );
else
table.getOrCreateIndex( indexName ).addColumn( column );
table.getOrCreateIndex( indexName ).addColumn( column, ordering );
}
}

View File

@ -786,7 +786,11 @@ public class EntityBinder {
null
);
//no check constraints available on joins
if ( secondaryTable != null ) {
TableBinder.addIndexes( table, secondaryTable.indexes(), mappings );
}
//no check constraints available on joins
join.setTable( table );
//somehow keep joins() for later.
@ -905,7 +909,10 @@ public class EntityBinder {
public void setIgnoreIdAnnotations(boolean ignoreIdAnnotations) {
this.ignoreIdAnnotations = ignoreIdAnnotations;
}
public void processComplementaryTableDefinitions(javax.persistence.Table table) {
if ( table == null ) return;
TableBinder.addIndexes( persistentClass.getTable(), table.indexes(), mappings );
}
public void processComplementaryTableDefinitions(org.hibernate.annotations.Table table) {
//comment and index are processed here
if ( table == null ) return;

View File

@ -514,6 +514,21 @@ public class TableBinder {
}
}
public static void addIndexes(Table hibTable, javax.persistence.Index[] indexes, Mappings mappings) {
for ( javax.persistence.Index index : indexes ) {
//no need to handle inSecondPass here since it is only called from EntityBinder
mappings.addSecondPass(
new IndexOrUniqueKeySecondPass(
hibTable,
index.name(),
index.columnList(),
mappings,
index.unique()
)
);
}
}
/**
* @deprecated Use {@link #buildUniqueConstraintHolders} instead
*/

View File

@ -2262,6 +2262,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
annotation.setValue( "schema", table.schema() );
annotation.setValue( "catalog", table.catalog() );
annotation.setValue( "uniqueConstraints", table.uniqueConstraints() );
annotation.setValue( "indexes", table.indexes() );
}
}
if ( StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) )

View File

@ -45,7 +45,7 @@ public class DB2UniqueDelegate extends DefaultUniqueDelegate {
if ( hasNullable( uniqueKey ) ) {
return org.hibernate.mapping.Index.buildSqlCreateIndexString(
dialect, uniqueKey.getName(), uniqueKey.getTable(),
uniqueKey.columnIterator(), true, defaultCatalog,
uniqueKey.columnIterator(), uniqueKey.getColumnOrderMap(), true, defaultCatalog,
defaultSchema );
} else {
return super.applyUniquesOnAlter(
@ -88,9 +88,9 @@ public class DB2UniqueDelegate extends DefaultUniqueDelegate {
}
private boolean hasNullable( org.hibernate.mapping.UniqueKey uniqueKey ) {
Iterator iter = uniqueKey.getColumnIterator();
Iterator<org.hibernate.mapping.Column> iter = uniqueKey.columnIterator();
while ( iter.hasNext() ) {
if ( ( ( org.hibernate.mapping.Column ) iter.next() ).isNullable() ) {
if ( iter.next().isNullable() ) {
return true;
}
}
@ -98,9 +98,8 @@ public class DB2UniqueDelegate extends DefaultUniqueDelegate {
}
private boolean hasNullable( UniqueKey uniqueKey ) {
Iterator iter = uniqueKey.getColumns().iterator();
while ( iter.hasNext() ) {
if ( ( ( Column ) iter.next() ).isNullable() ) {
for ( Column column : uniqueKey.getColumns() ) {
if ( column.isNullable() ) {
return true;
}
}

View File

@ -115,11 +115,14 @@ public class DefaultUniqueDelegate implements UniqueDelegate {
public String uniqueConstraintSql( org.hibernate.mapping.UniqueKey uniqueKey ) {
StringBuilder sb = new StringBuilder();
sb.append( " unique (" );
Iterator columnIterator = uniqueKey.getColumnIterator();
Iterator<org.hibernate.mapping.Column> columnIterator = uniqueKey.columnIterator();
while ( columnIterator.hasNext() ) {
org.hibernate.mapping.Column column
= (org.hibernate.mapping.Column) columnIterator.next();
= columnIterator.next();
sb.append( column.getQuotedName( dialect ) );
if ( uniqueKey.getColumnOrderMap().containsKey( column ) ) {
sb.append( " " ).append( uniqueKey.getColumnOrderMap().get( column ) );
}
if ( columnIterator.hasNext() ) {
sb.append( ", " );
}

View File

@ -38,7 +38,7 @@ import org.hibernate.engine.spi.Mapping;
public abstract class Constraint implements RelationalModel, Serializable {
private String name;
private final List columns = new ArrayList();
private final List<Column> columns = new ArrayList<Column>();
private Table table;
public String getName() {
@ -49,10 +49,6 @@ public abstract class Constraint implements RelationalModel, Serializable {
this.name = name;
}
public Iterator getColumnIterator() {
return columns.iterator();
}
public void addColumn(Column column) {
if ( !columns.contains( column ) ) columns.add( column );
}
@ -77,10 +73,14 @@ public abstract class Constraint implements RelationalModel, Serializable {
}
public Column getColumn(int i) {
return (Column) columns.get( i );
return columns.get( i );
}
//todo duplicated method, remove one
public Iterator<Column> getColumnIterator() {
return columns.iterator();
}
public Iterator columnIterator() {
public Iterator<Column> columnIterator() {
return columns.iterator();
}

View File

@ -24,6 +24,8 @@
package org.hibernate.mapping;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -40,7 +42,8 @@ import org.hibernate.internal.util.StringHelper;
public class Index implements RelationalModel, Serializable {
private Table table;
private List columns = new ArrayList();
private List<Column> columns = new ArrayList<Column>();
private java.util.Map<Column, String> columnOrderMap = new HashMap<Column, String>( );
private String name;
public String sqlCreateString(Dialect dialect, Mapping mapping, String defaultCatalog, String defaultSchema)
@ -50,6 +53,7 @@ public class Index implements RelationalModel, Serializable {
getName(),
getTable(),
getColumnIterator(),
columnOrderMap,
false,
defaultCatalog,
defaultSchema
@ -74,7 +78,8 @@ public class Index implements RelationalModel, Serializable {
Dialect dialect,
String name,
Table table,
Iterator columns,
Iterator<Column> columns,
java.util.Map<Column, String> columnOrderMap,
boolean unique,
String defaultCatalog,
String defaultSchema
@ -90,15 +95,30 @@ public class Index implements RelationalModel, Serializable {
.append( " on " )
.append( table.getQualifiedName( dialect, defaultCatalog, defaultSchema ) )
.append( " (" );
Iterator iter = columns;
while ( iter.hasNext() ) {
buf.append( ( (Column) iter.next() ).getQuotedName( dialect ) );
if ( iter.hasNext() ) buf.append( ", " );
while ( columns.hasNext() ) {
Column column = columns.next();
buf.append( column.getQuotedName( dialect ) );
if ( columnOrderMap.containsKey( column ) ) {
buf.append( " " ).append( columnOrderMap.get( column ) );
}
if ( columns.hasNext() ) buf.append( ", " );
}
buf.append( ")" );
return buf.toString();
}
public static String buildSqlCreateIndexString(
Dialect dialect,
String name,
Table table,
Iterator<Column> columns,
boolean unique,
String defaultCatalog,
String defaultSchema
) {
return buildSqlCreateIndexString( dialect, name, table, columns, Collections.EMPTY_MAP, unique, defaultCatalog, defaultSchema );
}
// Used only in Table for sqlCreateString (but commented out at the moment)
public String sqlConstraintString(Dialect dialect) {
@ -131,7 +151,7 @@ public class Index implements RelationalModel, Serializable {
return columns.size();
}
public Iterator getColumnIterator() {
public Iterator<Column> getColumnIterator() {
return columns.iterator();
}
@ -139,6 +159,13 @@ public class Index implements RelationalModel, Serializable {
if ( !columns.contains( column ) ) columns.add( column );
}
public void addColumn(Column column, String order) {
addColumn( column );
if ( StringHelper.isNotEmpty( order ) ) {
columnOrderMap.put( column, order );
}
}
public void addColumns(Iterator extraColumns) {
while ( extraColumns.hasNext() ) addColumn( (Column) extraColumns.next() );
}

View File

@ -54,7 +54,7 @@ public class Table implements RelationalModel, Serializable {
private Map columns = new LinkedHashMap();
private KeyValue idValue;
private PrimaryKey primaryKey;
private Map indexes = new LinkedHashMap();
private Map<String, Index> indexes = new LinkedHashMap<String, Index>();
private Map foreignKeys = new LinkedHashMap();
private Map<String,UniqueKey> uniqueKeys = new LinkedHashMap<String,UniqueKey>();
private int uniqueInteger;
@ -231,7 +231,7 @@ public class Table implements RelationalModel, Serializable {
return columns.values().iterator();
}
public Iterator getIndexIterator() {
public Iterator<Index> getIndexIterator() {
return indexes.values().iterator();
}
@ -239,11 +239,11 @@ public class Table implements RelationalModel, Serializable {
return foreignKeys.values().iterator();
}
public Iterator getUniqueKeyIterator() {
public Iterator<UniqueKey> getUniqueKeyIterator() {
return getUniqueKeys().values().iterator();
}
Map getUniqueKeys() {
Map<String, UniqueKey> getUniqueKeys() {
cleanseUniqueKeyMapIfNeeded();
return uniqueKeys;
}
@ -593,7 +593,7 @@ public class Table implements RelationalModel, Serializable {
public Index getOrCreateIndex(String indexName) {
Index index = (Index) indexes.get( indexName );
Index index = indexes.get( indexName );
if ( index == null ) {
index = new Index();
@ -606,11 +606,11 @@ public class Table implements RelationalModel, Serializable {
}
public Index getIndex(String indexName) {
return (Index) indexes.get( indexName );
return indexes.get( indexName );
}
public Index addIndex(Index index) {
Index current = (Index) indexes.get( index.getName() );
Index current = indexes.get( index.getName() );
if ( current != null ) {
throw new MappingException( "Index " + index.getName() + " already exists!" );
}
@ -705,6 +705,7 @@ public class Table implements RelationalModel, Serializable {
}
public String getSchema() {
return schema;
}

View File

@ -22,8 +22,12 @@
* Boston, MA 02110-1301 USA
*/
package org.hibernate.mapping;
import java.util.*;
import java.util.Map;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.internal.util.StringHelper;
/**
* A relational unique key constraint
@ -31,6 +35,7 @@ import org.hibernate.engine.spi.Mapping;
* @author Brett Meyer
*/
public class UniqueKey extends Constraint {
private java.util.Map<Column, String> columnOrderMap = new HashMap<Column, String>( );
@Override
public String sqlConstraintString(
@ -57,4 +62,14 @@ public class UniqueKey extends Constraint {
this, defaultCatalog, defaultSchema );
}
public void addColumn(Column column, String order) {
addColumn( column );
if ( StringHelper.isNotEmpty( order ) ) {
columnOrderMap.put( column, order );
}
}
public Map<Column, String> getColumnOrderMap() {
return columnOrderMap;
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.annotations.index.jpa;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
@Entity
@Table( indexes = {@Index( unique = true, columnList = "brand, producer")
, @Index( name = "Car_idx", columnList = "since DESC")})
public class Car {
@Id
long id;
String brand;
String producer;
long since;
}

View File

@ -0,0 +1,74 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.annotations.index.jpa;
import java.util.Iterator;
import org.junit.Test;
import static org.junit.Assert.*;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class IndexTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Car.class };
}
@Test
public void testBasicIndex() {
PersistentClass carClass = configuration().getClassMapping( Car.class.getName() );
Iterator itr = carClass.getTable().getUniqueKeyIterator();
assertTrue( itr.hasNext() );
UniqueKey uk = (UniqueKey) itr.next();
assertFalse( itr.hasNext() );
assertTrue( StringHelper.isNotEmpty( uk.getName() ) );
assertEquals( 2, uk.getColumnSpan() );
Column column = (Column) uk.getColumns().get( 0 );
assertEquals( "brand", column.getName() );
column = (Column) uk.getColumns().get( 1 );
assertEquals( "producer", column.getName() );
assertSame( carClass.getTable(), uk.getTable() );
itr = carClass.getTable().getIndexIterator();
assertTrue( itr.hasNext() );
Index index = (Index)itr.next();
assertFalse( itr.hasNext() );
assertEquals( "Car_idx", index.getName() );
assertEquals( 1, index.getColumnSpan() );
column = index.getColumnIterator().next();
assertEquals( "since", column.getName() );
assertSame( carClass.getTable(), index.getTable() );
}
}

View File

@ -29,6 +29,7 @@ hibernate.connection.username sa
hibernate.connection.pool_size 5
hibernate.show_sql false
hibernate.format_sql true
hibernate.max_fetch_depth 5