Author: epbernard
Date: 2009-07-21 20:16:59 -0400 (Tue, 21 Jul 2009)
New Revision: 17187
Added:
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/MapKeyJoinColumnDelegator.java
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3Column.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/Atmosphere.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/IndexedCollectionTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/target/Brand.java
Log:
ANN-857 implement @MapKeyJoinColumn and @MapKeyJoinColumns + _KEY default column name for
key. Also keep legacy support for old naming solution
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2009-07-21
15:57:57 UTC (rev 17186)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -74,6 +74,8 @@
import javax.persistence.CollectionTable;
import javax.persistence.UniqueConstraint;
import javax.persistence.MapKeyColumn;
+import javax.persistence.MapKeyJoinColumns;
+import javax.persistence.MapKeyJoinColumn;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
@@ -135,6 +137,7 @@
import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.cfg.annotations.MapKeyColumnDelegator;
import org.hibernate.cfg.annotations.CustomizableColumns;
+import org.hibernate.cfg.annotations.MapKeyJoinColumnDelegator;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.engine.Versioning;
import org.hibernate.id.MultipleHiLoPerTableGenerator;
@@ -1540,39 +1543,88 @@
mappings
);
}
+ {
+ Column[] keyColumns = null;
+ //JPA 2 has priority and has different default column values, differenciate legacy
from JPA 2
+ Boolean isJPA2 = null;
+ if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
+ isJPA2 = Boolean.TRUE;
+ keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation(
MapKeyColumn.class ) ) };
+ }
+ else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) ) {
+ if ( isJPA2 == null) {
+ isJPA2 = Boolean.FALSE;
+ }
+ keyColumns = property.getAnnotation( org.hibernate.annotations.MapKey.class
).columns();
+ }
- Column[] keyColumns = null;
- //JPA 2 has priority
- if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
- keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation(
MapKeyColumn.class ) ) };
+ //not explicitly legacy
+ if ( isJPA2 == null) {
+ isJPA2 = Boolean.TRUE;
+ }
+
+ //nullify empty array
+ keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns :
null;
+
+ PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData,
"mapkey" );
+ Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
+ keyColumns,
+ null,
+ Nullability.FORCED_NOT_NULL,
+ propertyHolder,
+ isJPA2 ? inferredData : mapKeyVirtualProperty,
+ isJPA2 ? "_KEY" : null,
+ entityBinder.getSecondaryTables(),
+ mappings
+ );
+ collectionBinder.setMapKeyColumns( mapColumns );
}
- else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) ) {
- keyColumns = property.getAnnotation( org.hibernate.annotations.MapKey.class
).columns();
- }
- //nullify empty array
- keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns :
null;
+ {
+ JoinColumn[] joinKeyColumns = null;
+ //JPA 2 has priority and has different default column values, differenciate legacy
from JPA 2
+ Boolean isJPA2 = null;
+ if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) {
+ isJPA2 = Boolean.TRUE;
+ final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation(
MapKeyJoinColumns.class ).value();
+ joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length];
+ int index = 0;
+ for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) {
+ joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn );
+ index++;
+ }
+ if ( joinKeyColumns != null ) {
+ throw new AnnotationException( "@MapKeyJoinColumn and @MapKeyJoinColumns used
on the same property: "
+ + StringHelper.qualify( propertyHolder.getClassName(), property.getName() ) );
+ }
+ }
+ else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
+ isJPA2 = Boolean.TRUE;
+ joinKeyColumns = new JoinColumn[] { new MapKeyJoinColumnDelegator(
property.getAnnotation( MapKeyJoinColumn.class ) ) };
+ }
+ else if ( property.isAnnotationPresent(
org.hibernate.annotations.MapKeyManyToMany.class ) ) {
+ if ( isJPA2 == null) {
+ isJPA2 = Boolean.FALSE;
+ }
+ joinKeyColumns = property.getAnnotation(
org.hibernate.annotations.MapKeyManyToMany.class ).joinColumns();
+ }
- PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData,
"mapkey" );
- Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
- keyColumns,
- null,
- Nullability.FORCED_NOT_NULL,
- propertyHolder,
- mapKeyVirtualProperty,
- entityBinder.getSecondaryTables(),
- mappings
- );
- collectionBinder.setMapKeyColumns( mapColumns );
+ //not explicitly legacy
+ if ( isJPA2 == null) {
+ isJPA2 = Boolean.TRUE;
+ }
- MapKeyManyToMany mapKeyManyToMany = property.getAnnotation( MapKeyManyToMany.class );
- Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumns(
- mapKeyManyToMany != null ?
- mapKeyManyToMany.joinColumns() :
- null,
- null, entityBinder.getSecondaryTables(),
- propertyHolder, mapKeyVirtualProperty.getPropertyName(), mappings
- );
- collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
+ PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData,
"mapkey" );
+ Ejb3JoinColumn[] mapJoinColumns =
Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
+ joinKeyColumns,
+ null,
+ entityBinder.getSecondaryTables(),
+ propertyHolder,
+ isJPA2 ? inferredData.getPropertyName() : mapKeyVirtualProperty.getPropertyName(),
+ isJPA2 ? "_KEY" : null,
+ mappings
+ );
+ collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
+ }
//potential element
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3Column.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3Column.java 2009-07-21
15:57:57 UTC (rev 17186)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3Column.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -336,6 +336,18 @@
PropertyData inferredData,
Map<String, Join> secondaryTables,
ExtendedMappings mappings
+ ){
+ return buildColumnFromAnnotation(
+ anns,
+ formulaAnn, nullability, propertyHolder, inferredData, null, secondaryTables,
mappings);
+ }
+ public static Ejb3Column[] buildColumnFromAnnotation(
+ javax.persistence.Column[] anns,
+ org.hibernate.annotations.Formula formulaAnn, Nullability nullability, PropertyHolder
propertyHolder,
+ PropertyData inferredData,
+ String suffixForDefaultColumnName,
+ Map<String, Join> secondaryTables,
+ ExtendedMappings mappings
) {
Ejb3Column[] columns;
if ( formulaAnn != null ) {
@@ -361,7 +373,12 @@
log.debug( "Column(s) overridden for property {}",
inferredData.getPropertyName() );
}
if ( actualCols == null ) {
- columns = buildImplicitColumn( inferredData, secondaryTables, propertyHolder,
nullability, mappings );
+ columns = buildImplicitColumn( inferredData,
+ suffixForDefaultColumnName,
+ secondaryTables,
+ propertyHolder,
+ nullability,
+ mappings );
}
else {
final int length = actualCols.length;
@@ -376,6 +393,12 @@
column.setPrecision( col.precision() );
column.setScale( col.scale() );
column.setLogicalColumnName( col.name() );
+ //support for explicit property name + suffix
+ if ( StringHelper.isEmpty( column.getLogicalColumnName() )
+ && ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
+ column.setLogicalColumnName( inferredData.getPropertyName() +
suffixForDefaultColumnName );
+ }
+
column.setPropertyName(
BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
);
@@ -398,8 +421,12 @@
}
private static Ejb3Column[] buildImplicitColumn(
- PropertyData inferredData, Map<String, Join> secondaryTables, PropertyHolder
propertyHolder,
- Nullability nullability, ExtendedMappings mappings
+ PropertyData inferredData,
+ String suffixForDefaultColumnName,
+ Map<String, Join> secondaryTables,
+ PropertyHolder propertyHolder,
+ Nullability nullability,
+ ExtendedMappings mappings
) {
Ejb3Column[] columns;
columns = new Ejb3Column[1];
@@ -412,12 +439,22 @@
column.setNullable( false );
}
column.setLength( DEFAULT_COLUMN_LENGTH );
+ final String propertyName = inferredData.getPropertyName();
column.setPropertyName(
- BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
+ BinderHelper.getRelativePath( propertyHolder, propertyName )
);
column.setPropertyHolder( propertyHolder );
column.setJoins( secondaryTables );
column.setMappings( mappings );
+
+ // property name + suffix is an "explicit" column name
+ if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
+ column.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
+ column.setImplicit( false );
+ }
+ else {
+ column.setImplicit( true );
+ }
column.bind();
columns[0] = column;
return columns;
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java 2009-07-21
15:57:57 UTC (rev 17186)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -134,13 +134,31 @@
String propertyName,
ExtendedMappings mappings
) {
+ return buildJoinColumnsWithDefaultColumnSuffix(anns, mappedBy, joins, propertyHolder,
propertyName, "", mappings);
+ }
+
+ public static Ejb3JoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
+ JoinColumn[] anns,
+ String mappedBy, Map<String, Join> joins,
+ PropertyHolder propertyHolder,
+ String propertyName,
+ String suffixForDefaultColumnName,
+ ExtendedMappings mappings
+ ) {
JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn(
StringHelper.qualify( propertyHolder.getPath(), propertyName )
);
if ( actualColumns == null ) actualColumns = anns;
if ( actualColumns == null || actualColumns.length == 0 ) {
return new Ejb3JoinColumn[] {
- buildJoinColumn( (JoinColumn) null, mappedBy, joins, propertyHolder, propertyName,
mappings )
+ buildJoinColumn(
+ (JoinColumn) null,
+ mappedBy,
+ joins,
+ propertyHolder,
+ propertyName,
+ suffixForDefaultColumnName,
+ mappings )
};
}
else {
@@ -153,6 +171,7 @@
joins,
propertyHolder,
propertyName,
+ suffixForDefaultColumnName,
mappings
);
}
@@ -168,6 +187,7 @@
String mappedBy, Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
+ String suffixForDefaultColumnName,
ExtendedMappings mappings
) {
if ( ann != null ) {
@@ -179,6 +199,10 @@
}
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
joinColumn.setJoinAnnotation( ann, null );
+ if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() )
+ && ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
+ joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
+ }
joinColumn.setJoins( joins );
joinColumn.setPropertyHolder( propertyHolder );
joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName
) );
@@ -192,8 +216,17 @@
joinColumn.setMappedBy( mappedBy );
joinColumn.setJoins( joins );
joinColumn.setPropertyHolder( propertyHolder );
- joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName
) );
- joinColumn.setImplicit( true );
+ joinColumn.setPropertyName(
+ BinderHelper.getRelativePath( propertyHolder, propertyName )
+ );
+ // property name + suffix is an "explicit" column name
+ if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
+ joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
+ joinColumn.setImplicit( false );
+ }
+ else {
+ joinColumn.setImplicit( true );
+ }
joinColumn.setMappings( mappings );
joinColumn.bind();
return joinColumn;
Added:
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/MapKeyJoinColumnDelegator.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/MapKeyJoinColumnDelegator.java
(rev 0)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/MapKeyJoinColumnDelegator.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -0,0 +1,54 @@
+package org.hibernate.cfg.annotations;
+
+import java.lang.annotation.Annotation;
+import javax.persistence.Column;
+import javax.persistence.MapKeyJoinColumn;
+import javax.persistence.JoinColumn;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@SuppressWarnings({ "ClassExplicitlyAnnotation" })
+public class MapKeyJoinColumnDelegator implements JoinColumn {
+ private final MapKeyJoinColumn column;
+
+ public MapKeyJoinColumnDelegator(MapKeyJoinColumn column) {
+ this.column = column;
+ }
+
+ public String name() {
+ return column.name();
+ }
+
+ public String referencedColumnName() {
+ return column.referencedColumnName();
+ }
+
+ public boolean unique() {
+ return column.unique();
+ }
+
+ public boolean nullable() {
+ return column.nullable();
+ }
+
+ public boolean insertable() {
+ return column.insertable();
+ }
+
+ public boolean updatable() {
+ return column.updatable();
+ }
+
+ public String columnDefinition() {
+ return column.columnDefinition();
+ }
+
+ public String table() {
+ return column.table();
+ }
+
+ public Class<? extends Annotation> annotationType() {
+ return Column.class;
+ }
+}
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/Atmosphere.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/Atmosphere.java 2009-07-21
15:57:57 UTC (rev 17186)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/Atmosphere.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -12,10 +12,10 @@
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
+import javax.persistence.MapKeyJoinColumn;
import org.hibernate.annotations.MapKey;
import org.hibernate.annotations.CollectionOfElements;
-import org.hibernate.annotations.MapKeyManyToMany;
/**
* @author Emmanuel Bernard
@@ -31,13 +31,31 @@
public Map<String, Gas> gases = new HashMap<String, Gas>();
@ManyToMany(cascade = CascadeType.ALL)
- @MapKeyManyToMany(joinColumns = @JoinColumn(name="gas_id") )
+ @MapKeyJoinColumn(name="gas_id" )
@JoinTable(name = "Gas_per_key")
public Map<GasKey, Gas> gasesPerKey = new HashMap<GasKey, Gas>();
@CollectionOfElements //TODO migrate to @ElementCollection ; @MapKeyManyToMany ??
@Column(name="composition_rate")
- @MapKeyManyToMany(joinColumns = @JoinColumn(name="gas_id"))
+ @MapKeyJoinColumn(name="gas_id" )
@JoinTable(name = "Composition", joinColumns = @JoinColumn(name =
"atmosphere_id"))
public Map<Gas, Double> composition = new HashMap<Gas, Double>();
+
+ //use default JPA 2 column name for map key
+ @ManyToMany(cascade = CascadeType.ALL)
+ @MapKeyColumn
+ @JoinTable(name="Atm_Gas_Def")
+ public Map<String, Gas> gasesDef = new HashMap<String, Gas>();
+
+ //use default HAN legacy column name for map key
+ @ManyToMany(cascade = CascadeType.ALL)
+ @MapKey
+ @JoinTable(name="Atm_Gas_DefLeg")
+ public Map<String, Gas> gasesDefLeg = new HashMap<String, Gas>();
+
+ @ManyToMany(cascade = CascadeType.ALL)
+ @MapKeyJoinColumn
+ @JoinTable(name = "Gas_p_key_def")
+ public Map<GasKey, Gas> gasesPerKeyDef = new HashMap<GasKey, Gas>();
+
}
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/IndexedCollectionTest.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/IndexedCollectionTest.java 2009-07-21
15:57:57 UTC (rev 17186)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/indexcoll/IndexedCollectionTest.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -5,10 +5,14 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Iterator;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.Hibernate;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Column;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.test.annotations.RequiresDialect;
import org.hibernate.test.annotations.TestCase;
@@ -19,6 +23,35 @@
* @author Emmanuel Bernard
*/
public class IndexedCollectionTest extends TestCase {
+
+ public void testJPA2DefaultMapColumns() throws Exception {
+ isDefaultKeyColumnPresent( "gasesDef" );
+ isDefaultKeyColumnPresent( "gasesPerKeyDef" );
+ isNotDefaultKeyColumnPresent( "gasesDefLeg" );
+ }
+
+ private void isDefaultKeyColumnPresent(String propertyName) {
+ final Collection collection = getCfg().getCollectionMapping( Atmosphere.class.getName()
+ "." + propertyName );
+ final Iterator columnIterator = collection.getCollectionTable().getColumnIterator();
+ boolean hasDefault = false;
+ while ( columnIterator.hasNext() ) {
+ Column column = (Column) columnIterator.next();
+ if ( (propertyName + "_KEY").equals( column.getName() ) ) hasDefault =
true;
+ }
+ assertTrue( "Could not find " + propertyName + "_KEY",
hasDefault);
+ }
+
+ private void isNotDefaultKeyColumnPresent(String propertyName) {
+ final Collection collection = getCfg().getCollectionMapping( Atmosphere.class.getName()
+ "." + propertyName );
+ final Iterator columnIterator = collection.getCollectionTable().getColumnIterator();
+ boolean hasDefault = false;
+ while ( columnIterator.hasNext() ) {
+ Column column = (Column) columnIterator.next();
+ if ( (propertyName + "_KEY").equals( column.getName() ) ) hasDefault =
true;
+ }
+ assertFalse( "Could not find " + propertyName + "_KEY",
hasDefault);
+ }
+
public void testFkList() throws Exception {
Wardrobe w = new Wardrobe();
Drawer d1 = new Drawer();
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/target/Brand.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/target/Brand.java 2009-07-21
15:57:57 UTC (rev 17186)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/target/Brand.java 2009-07-22
00:16:59 UTC (rev 17187)
@@ -9,6 +9,7 @@
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.MapKeyClass;
+import javax.persistence.JoinColumn;
import org.hibernate.annotations.MapKey;
import org.hibernate.annotations.MapKeyManyToMany;
@@ -27,8 +28,8 @@
private Map<Size, Luggage> luggagesBySize = new HashMap<Size, Luggage>();
@ElementCollection(targetClass = SizeImpl.class)
- @MapKeyManyToMany(targetEntity = LuggageImpl.class)
- //TODO @MapKeyClass(LuggageImpl.class)
+ @MapKeyClass(LuggageImpl.class)
+ @MapKeyManyToMany //legacy column name: was never officially supported BTW
private Map<Luggage, Size> sizePerLuggage = new HashMap<Luggage, Size>();