JPA-43 - Impl Index and ForeignKey for JPA 2.1

This commit is contained in:
Strong Liu 2013-02-28 00:20:51 +08:00
parent ed549f5793
commit 3335710a38
10 changed files with 263 additions and 74 deletions

View File

@ -2172,7 +2172,6 @@ public final class AnnotationBinder {
JoinColumn[] annInverseJoins;
JoinTable assocTable = propertyHolder.getJoinTable( property );
CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
if ( assocTable != null || collectionTable != null ) {
final String catalog;
@ -2181,6 +2180,8 @@ public final class AnnotationBinder {
final UniqueConstraint[] uniqueConstraints;
final JoinColumn[] joins;
final JoinColumn[] inverseJoins;
final javax.persistence.Index[] jpaIndexes;
//JPA 2 has priority
if ( collectionTable != null ) {
@ -2190,6 +2191,7 @@ public final class AnnotationBinder {
uniqueConstraints = collectionTable.uniqueConstraints();
joins = collectionTable.joinColumns();
inverseJoins = null;
jpaIndexes = collectionTable.indexes();
}
else {
catalog = assocTable.catalog();
@ -2198,10 +2200,13 @@ public final class AnnotationBinder {
uniqueConstraints = assocTable.uniqueConstraints();
joins = assocTable.joinColumns();
inverseJoins = assocTable.inverseJoinColumns();
jpaIndexes = assocTable.indexes();
}
collectionBinder.setExplicitAssociationTable( true );
if ( jpaIndexes != null && jpaIndexes.length > 0 ) {
associationTableBinder.setJpaIndex( jpaIndexes );
}
if ( !BinderHelper.isEmptyAnnotationValue( schema ) ) {
associationTableBinder.setSchema( schema );
}
@ -2212,7 +2217,7 @@ public final class AnnotationBinder {
associationTableBinder.setName( tableName );
}
associationTableBinder.setUniqueConstraints( uniqueConstraints );
associationTableBinder.setJpaIndex( jpaIndexes );
//set check constaint in the second pass
annJoins = joins.length == 0 ? null : joins;
annInverseJoins = inverseJoins == null || inverseJoins.length == 0 ? null : inverseJoins;

View File

@ -251,6 +251,7 @@ public class Configuration implements Serializable {
private Set<String> defaultNamedGenerators;
private Map<String, Properties> generatorTables;
private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable;
private Map<Table, List<JPAIndexHolder>> jpaIndexHoldersByTable;
private Map<String, String> mappedByResolver;
private Map<String, String> propertyRefResolver;
private Map<String, AnyMetaDef> anyMetaDefs;
@ -324,6 +325,7 @@ public class Configuration implements Serializable {
defaultSqlResultSetMappingNames = new HashSet<String>();
defaultNamedGenerators = new HashSet<String>();
uniqueConstraintHoldersByTable = new HashMap<Table, List<UniqueConstraintHolder>>();
jpaIndexHoldersByTable = new HashMap<Table,List<JPAIndexHolder>>( );
mappedByResolver = new HashMap<String, String>();
propertyRefResolver = new HashMap<String, String>();
caches = new ArrayList<CacheHolder>();
@ -1380,6 +1382,17 @@ public class Configuration implements Serializable {
buildUniqueKeyFromColumnNames( table, keyName, holder.getColumns() );
}
}
for(Table table : jpaIndexHoldersByTable.keySet()){
final List<JPAIndexHolder> jpaIndexHolders = jpaIndexHoldersByTable.get( table );
int uniqueIndexPerTable = 0;
for ( JPAIndexHolder holder : jpaIndexHolders ) {
uniqueIndexPerTable++;
final String keyName = StringHelper.isEmpty( holder.getName() )
? "idx_"+table.getName()+"_" + uniqueIndexPerTable
: holder.getName();
buildUniqueKeyFromColumnNames( table, keyName, holder.getColumns(), holder.getOrdering(), holder.isUnique() );
}
}
}
private void processSecondPassesOfType(Class<? extends SecondPass> type) {
@ -1533,7 +1546,11 @@ public class Configuration implements Serializable {
}
}
private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames) {
private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames){
buildUniqueKeyFromColumnNames( table, keyName, columnNames, null, true );
}
private void buildUniqueKeyFromColumnNames(Table table, String keyName, String[] columnNames, String[] orderings, boolean unique) {
keyName = normalizer.normalizeIdentifierQuoting( keyName );
int size = columnNames.length;
@ -1553,13 +1570,29 @@ public class Configuration implements Serializable {
unboundNoLogical.add( new Column( logicalColumnName ) );
}
}
UniqueKey uk = table.getOrCreateUniqueKey( keyName );
for ( Column column : columns ) {
if ( table.containsColumn( column ) ) {
uk.addColumn( column );
unbound.remove( column );
if ( unique ) {
UniqueKey uk = table.getOrCreateUniqueKey( keyName );
for ( int i = 0; i < columns.length; i++ ) {
Column column = columns[i];
String order = orderings != null ? orderings[i] : null;
if ( table.containsColumn( column ) ) {
uk.addColumn( column, order );
unbound.remove( column );
}
}
}
else {
Index index = table.getOrCreateIndex( keyName );
for ( int i = 0; i < columns.length; i++ ) {
Column column = columns[i];
String order = orderings != null ? orderings[i] : null;
if ( table.containsColumn( column ) ) {
index.addColumn( column, order );
unbound.remove( column );
}
}
}
if ( unbound.size() > 0 || unboundNoLogical.size() > 0 ) {
StringBuilder sb = new StringBuilder( "Unable to create unique key constraint (" );
for ( String columnName : columnNames ) {
@ -3309,6 +3342,15 @@ public class Configuration implements Serializable {
holderList.addAll( uniqueConstraintHolders );
}
public void addJpaIndexHolders(Table table, List<JPAIndexHolder> holders) {
List<JPAIndexHolder> holderList = jpaIndexHoldersByTable.get( table );
if ( holderList == null ) {
holderList = new ArrayList<JPAIndexHolder>();
jpaIndexHoldersByTable.put( table, holderList );
}
holderList.addAll( holders );
}
public void addMappedBy(String entityName, String propertyName, String inversePropertyName) {
mappedByResolver.put( entityName + "." + propertyName, inversePropertyName );
}

View File

@ -42,7 +42,6 @@ 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;
@ -54,46 +53,10 @@ 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;
}
}
}
/**
@ -112,23 +75,21 @@ 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 ( int i = 0; i < columns.length; i++ ) {
final String order = ordering != null ? ordering[i] : null;
addConstraintToColumn( columns[i], order );
addConstraintToColumn( columns[i] );
}
}
if ( column != null ) {
this.table = column.getTable();
addConstraintToColumn( mappings.getLogicalColumnName( column.getMappingColumn().getQuotedName(), table ), null );
addConstraintToColumn( mappings.getLogicalColumnName( column.getMappingColumn().getQuotedName(), table ) );
}
}
private void addConstraintToColumn(final String columnName, final String ordering) {
private void addConstraintToColumn(final String columnName ) {
Column column = table.getColumn(
new Column(
mappings.getPhysicalColumnName( columnName, table )
@ -140,8 +101,8 @@ public class IndexOrUniqueKeySecondPass implements SecondPass {
);
}
if ( unique )
table.getOrCreateUniqueKey( indexName ).addColumn( column, ordering );
table.getOrCreateUniqueKey( indexName ).addColumn( column );
else
table.getOrCreateIndex( indexName ).addColumn( column, ordering );
table.getOrCreateIndex( indexName ).addColumn( column );
}
}

View File

@ -0,0 +1,88 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, 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.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.persistence.Index;
/**
* @author Strong Liu <stliu@hibernate.org>
*/
public class JPAIndexHolder {
private final String name;
private final String[] columns;
private final String[] ordering;
private final boolean unique;
public JPAIndexHolder(Index index) {
StringTokenizer tokenizer = new StringTokenizer( index.columnList(), "," );
List<String> tmp = new ArrayList<String>();
while ( tokenizer.hasMoreElements() ) {
tmp.add( tokenizer.nextToken().trim() );
}
this.name = index.name();
this.columns = new String[tmp.size()];
this.ordering = new String[tmp.size()];
this.unique = index.unique();
initializeColumns( columns, ordering, tmp );
}
public String[] getColumns() {
return columns;
}
public String getName() {
return name;
}
public String[] getOrdering() {
return ordering;
}
public boolean isUnique() {
return 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;
}
}
}
}

View File

@ -721,6 +721,8 @@ public interface Mappings {
public void addUniqueConstraintHolders(Table table, List<UniqueConstraintHolder> uniqueConstraintHolders);
public void addJpaIndexHolders(Table table, List<JPAIndexHolder> jpaIndexHolders);
public void addMappedBy(String entityName, String propertyName, String inversePropertyName);
public String getFromMappedBy(String entityName, String propertyName);

View File

@ -23,10 +23,7 @@
*/
package org.hibernate.cfg.annotations;
import org.jboss.logging.Logger;
import org.hibernate.annotations.OrderBy;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
@ -36,7 +33,6 @@ import org.hibernate.mapping.PersistentClass;
* @author Matthew Inger
*/
public class SetBinder extends CollectionBinder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SetBinder.class.getName());
public SetBinder() {
}

View File

@ -36,6 +36,7 @@ import org.hibernate.annotations.Index;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.Ejb3JoinColumn;
import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
import org.hibernate.cfg.JPAIndexHolder;
import org.hibernate.cfg.Mappings;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.ObjectNameNormalizer;
@ -80,6 +81,7 @@ public class TableBinder {
private String ownerEntity;
private String associatedEntity;
private boolean isJPA2ElementCollection;
private List<JPAIndexHolder> jpaIndexHolders;
public void setSchema(String schema) {
this.schema = schema;
@ -105,6 +107,10 @@ public class TableBinder {
this.uniqueConstraints = TableBinder.buildUniqueConstraintHolders( uniqueConstraints );
}
public void setJpaIndex(javax.persistence.Index[] jpaIndex){
this.jpaIndexHolders = buildJpaIndexHolder( jpaIndex );
}
public void setConstraints(String constraints) {
this.constraints = constraints;
}
@ -183,6 +189,7 @@ public class TableBinder {
namingStrategyHelper,
isAbstract,
uniqueConstraints,
jpaIndexHolders,
constraints,
denormalizedSuperTable,
mappings,
@ -190,6 +197,7 @@ public class TableBinder {
);
}
private ObjectNameSource buildNameContext(String unquotedOwnerTable, String unquotedAssocTable) {
String logicalName = mappings.getNamingStrategy().logicalCollectionTableName(
name,
@ -211,10 +219,11 @@ public class TableBinder {
ObjectNameNormalizer.NamingStrategyHelper namingStrategyHelper,
boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints,
List<JPAIndexHolder> jpaIndexHolders,
String constraints,
Table denormalizedSuperTable,
Mappings mappings,
String subselect) {
String subselect){
schema = BinderHelper.isEmptyAnnotationValue( schema ) ? mappings.getSchemaName() : schema;
catalog = BinderHelper.isEmptyAnnotationValue( catalog ) ? mappings.getCatalogName() : catalog;
@ -244,10 +253,14 @@ public class TableBinder {
);
}
if ( uniqueConstraints != null && uniqueConstraints.size() > 0 ) {
if ( CollectionHelper.isNotEmpty( uniqueConstraints ) ) {
mappings.addUniqueConstraintHolders( table, uniqueConstraints );
}
if ( CollectionHelper.isNotEmpty( jpaIndexHolders ) ) {
mappings.addJpaIndexHolders( table, jpaIndexHolders );
}
if ( constraints != null ) table.addCheckConstraint( constraints );
// logicalName is null if we are in the second pass
@ -258,6 +271,23 @@ public class TableBinder {
return table;
}
public static Table buildAndFillTable(
String schema,
String catalog,
ObjectNameSource nameSource,
ObjectNameNormalizer.NamingStrategyHelper namingStrategyHelper,
boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints,
String constraints,
Table denormalizedSuperTable,
Mappings mappings,
String subselect) {
return buildAndFillTable( schema, catalog, nameSource, namingStrategyHelper, isAbstract, uniqueConstraints, null, constraints
, denormalizedSuperTable, mappings, subselect);
}
/**
* @deprecated Use {@link #buildAndFillTable} instead.
*/
@ -515,18 +545,15 @@ 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()
)
);
mappings.addJpaIndexHolders( hibTable, buildJpaIndexHolder( indexes ) );
}
public static List<JPAIndexHolder> buildJpaIndexHolder(javax.persistence.Index[] indexes){
List<JPAIndexHolder> holders = new ArrayList<JPAIndexHolder>( indexes.length );
for(javax.persistence.Index index : indexes){
holders.add( new JPAIndexHolder( index ) );
}
return holders;
}
/**

View File

@ -4,6 +4,7 @@ import java.util.Set;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Index;
@Entity
public class WealthyPerson extends Person {
@ -15,6 +16,6 @@ public class WealthyPerson extends Person {
protected Set<Address> legacyVacationHomes = new HashSet<Address>();
@ElementCollection
@CollectionTable(name = "WelPers_VacHomes")
@CollectionTable(name = "WelPers_VacHomes", indexes = @Index( columnList = "countryName, type_id"))
protected Set<Address> explicitVacationHomes = new HashSet<Address>();
}

View File

@ -30,13 +30,24 @@ import org.junit.Test;
import static org.junit.Assert.*;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.List;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Set;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.Value;
import org.hibernate.test.annotations.embedded.Address;
import org.hibernate.test.annotations.embedded.AddressType;
import org.hibernate.test.annotations.embedded.Book;
import org.hibernate.test.annotations.embedded.Person;
import org.hibernate.test.annotations.embedded.Summary;
import org.hibernate.test.annotations.embedded.WealthyPerson;
import org.hibernate.test.event.collection.detached.*;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
/**
@ -45,11 +56,20 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
public class IndexTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Car.class, Book.class, Summary.class };
return new Class[] { Car.class,
Book.class,
Summary.class,
WealthyPerson.class,
Person.class,
AddressType.class,
Address.class,
Alias.class,
org.hibernate.test.event.collection.detached.Character.class
};
}
@Test
public void testBasicIndex() {
public void testTableIndex() {
PersistentClass entity = configuration().getClassMapping( Car.class.getName() );
Iterator itr = entity.getTable().getUniqueKeyIterator();
assertTrue( itr.hasNext() );
@ -94,4 +114,50 @@ public class IndexTest extends BaseCoreFunctionalTestCase {
assertSame( join.getTable(), index.getTable() );
}
@Test
public void testCollectionTableIndex(){
PersistentClass entity = configuration().getClassMapping( WealthyPerson.class.getName() );
Property property = entity.getProperty( "explicitVacationHomes" );
Set set = (Set)property.getValue();
Table collectionTable = set.getCollectionTable();
Iterator<Index> itr = collectionTable.getIndexIterator();
assertTrue( itr.hasNext() );
Index index = itr.next();
assertFalse( itr.hasNext() );
assertTrue( "index name is not generated", StringHelper.isNotEmpty( index.getName() ) );
assertEquals( 2, index.getColumnSpan() );
Iterator<Column> columnIterator = index.getColumnIterator();
Column column = columnIterator.next();
assertEquals( "countryName", column.getName() );
column = columnIterator.next();
assertEquals( "type_id", column.getName() );
assertSame( collectionTable, index.getTable() );
}
@Test
public void testJoinTableIndex(){
PersistentClass entity = configuration().getClassMapping( Alias.class.getName() );
Property property = entity.getProperty( "characters" );
Bag set = (Bag)property.getValue();
Table collectionTable = set.getCollectionTable();
Iterator<UniqueKey> itr = collectionTable.getUniqueKeyIterator();
assertTrue( itr.hasNext() );
UniqueKey index = itr.next();
assertFalse( itr.hasNext() );
assertTrue( "index name is not generated", StringHelper.isNotEmpty( index.getName() ) );
assertEquals( 1, index.getColumnSpan() );
Iterator<Column> columnIterator = index.getColumnIterator();
Column column = columnIterator.next();
assertEquals( "characters_id", column.getName() );
assertSame( collectionTable, index.getTable() );
}
@Test
public void testTableGeneratorIndex(){
//todo
}
}

View File

@ -26,6 +26,7 @@ package org.hibernate.test.event.collection.detached;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@ -68,7 +69,7 @@ public class Alias implements Identifiable {
}
@ManyToMany( cascade = CascadeType.ALL )
@JoinTable( name = "CHARACTER_ALIAS" )
@JoinTable( name = "CHARACTER_ALIAS", indexes = @Index( columnList = "characters_id", unique = true))
// @JoinTable(
// name = "CHARACTER_ALIAS",
// joinColumns = @JoinColumn(name="ALIAS_ID", referencedColumnName="ID"),