Author: epbernard
Date: 2010-01-12 13:48:42 -0500 (Tue, 12 Jan 2010)
New Revision: 18518
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/PropertyHolder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/AssociationOverrideTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/ContactInfo.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/Employee.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/PhoneNumber.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/SocialTouchPoints.java
Log:
HHH-4782 supports @AssociationOverride.joinTable
HHH-4679 add tests for dot notations in @AssocuiationOverride
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -33,6 +33,7 @@
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
import javax.persistence.MappedSuperclass;
import org.hibernate.AssertionFailure;
@@ -50,9 +51,12 @@
private Map<String, Column[]> currentPropertyColumnOverride;
private Map<String, JoinColumn[]> holderJoinColumnOverride;
private Map<String, JoinColumn[]> currentPropertyJoinColumnOverride;
+ private Map<String, JoinTable> holderJoinTableOverride;
+ private Map<String, JoinTable> currentPropertyJoinTableOverride;
private String path;
private ExtendedMappings mappings;
+
public AbstractPropertyHolder(
String path, PropertyHolder parent, XClass clazzToProcess, ExtendedMappings mappings
) {
@@ -77,6 +81,7 @@
if ( property == null ) {
this.currentPropertyColumnOverride = null;
this.currentPropertyJoinColumnOverride = null;
+ this.currentPropertyJoinTableOverride = null;
}
else {
this.currentPropertyColumnOverride = buildColumnOverride(
@@ -93,6 +98,13 @@
if ( this.currentPropertyJoinColumnOverride.size() == 0 ) {
this.currentPropertyJoinColumnOverride = null;
}
+ this.currentPropertyJoinTableOverride = buildJoinTableOverride(
+ property,
+ getPath()
+ );
+ if ( this.currentPropertyJoinTableOverride.size() == 0 ) {
+ this.currentPropertyJoinTableOverride = null;
+ }
}
}
@@ -111,6 +123,7 @@
// WARNING: this can conflict with user's expectations if:
// - the property uses some restricted values
// - the user has overridden the column
+ // also change getOverriddenJoinColumn and getOverriddenJoinTable as well
// if ( propertyName.contains( ".key." ) ) {
// //support for legacy @AttributeOverride declarations
@@ -149,7 +162,7 @@
* Get column overriding, property first, then parent, then holder
* find the overridden rules from the exact property name.
*/
- public Column[] getExactOverriddenColumn(String propertyName) {
+ private Column[] getExactOverriddenColumn(String propertyName) {
Column[] override = null;
if ( parent != null ) {
override = parent.getExactOverriddenColumn( propertyName );
@@ -165,8 +178,25 @@
/**
* Get column overriding, property first, then parent, then holder
+ * replace the placeholder 'collection&&element' with nothing
+ *
+ * These rules are here to support both JPA 2 and legacy overriding rules.
+ *
*/
public JoinColumn[] getOverriddenJoinColumn(String propertyName) {
+ JoinColumn[] result = getExactOverriddenJoinColumn( propertyName );
+ if ( result == null && propertyName.contains(
".collection&&element." ) ) {
+ //support for non map collections where no prefix is needed
+ //TODO cache the underlying regexp
+ result = getExactOverriddenJoinColumn( propertyName.replace(
".collection&&element.", "." ) );
+ }
+ return result;
+ }
+
+ /**
+ * Get column overriding, property first, then parent, then holder
+ */
+ private JoinColumn[] getExactOverriddenJoinColumn(String propertyName) {
JoinColumn[] override = null;
if ( parent != null ) {
override = parent.getOverriddenJoinColumn( propertyName );
@@ -180,26 +210,81 @@
return override;
}
+ /**
+ * Get column overriding, property first, then parent, then holder
+ * replace the placeholder 'collection&&element' with nothing
+ *
+ * These rules are here to support both JPA 2 and legacy overriding rules.
+ *
+ */
+ public JoinTable getJoinTable(XProperty property) {
+ final String propertyName = StringHelper.qualify( getPath(), property.getName() );
+ JoinTable result = getOverriddenJoinTable( propertyName );
+ if (result == null) {
+ result = property.getAnnotation( JoinTable.class );
+ }
+ return result;
+ }
+
+ /**
+ * Get column overriding, property first, then parent, then holder
+ * replace the placeholder 'collection&&element' with nothing
+ *
+ * These rules are here to support both JPA 2 and legacy overriding rules.
+ *
+ */
+ public JoinTable getOverriddenJoinTable(String propertyName) {
+ JoinTable result = getExactOverriddenJoinTable( propertyName );
+ if ( result == null && propertyName.contains(
".collection&&element." ) ) {
+ //support for non map collections where no prefix is needed
+ //TODO cache the underlying regexp
+ result = getExactOverriddenJoinTable( propertyName.replace(
".collection&&element.", "." ) );
+ }
+ return result;
+ }
+
+ /**
+ * Get column overriding, property first, then parent, then holder
+ */
+ private JoinTable getExactOverriddenJoinTable(String propertyName) {
+ JoinTable override = null;
+ if ( parent != null ) {
+ override = parent.getOverriddenJoinTable( propertyName );
+ }
+ if ( override == null && currentPropertyJoinColumnOverride != null ) {
+ override = currentPropertyJoinTableOverride.get( propertyName );
+ }
+ if ( override == null && holderJoinTableOverride != null ) {
+ override = holderJoinTableOverride.get( propertyName );
+ }
+ return override;
+ }
+
private void buildHierarchyColumnOverride(XClass element) {
XClass current = element;
Map<String, Column[]> columnOverride = new HashMap<String, Column[]>();
Map<String, JoinColumn[]> joinColumnOverride = new HashMap<String,
JoinColumn[]>();
+ Map<String, JoinTable> joinTableOverride = new HashMap<String,
JoinTable>();
while ( current != null && !mappings.getReflectionManager().toXClass(
Object.class ).equals( current ) ) {
if ( current.isAnnotationPresent( Entity.class ) || current.isAnnotationPresent(
MappedSuperclass.class )
|| current.isAnnotationPresent( Embeddable.class ) ) {
//FIXME is embeddable override?
Map<String, Column[]> currentOverride = buildColumnOverride( current, getPath()
);
Map<String, JoinColumn[]> currentJoinOverride = buildJoinColumnOverride(
current, getPath() );
+ Map<String, JoinTable> currentJoinTableOverride = buildJoinTableOverride(
current, getPath() );
currentOverride.putAll( columnOverride ); //subclasses have precedence over
superclasses
currentJoinOverride.putAll( joinColumnOverride ); //subclasses have precedence over
superclasses
+ currentJoinOverride.putAll( joinColumnOverride ); //subclasses have precedence over
superclasses
columnOverride = currentOverride;
joinColumnOverride = currentJoinOverride;
+ joinTableOverride = currentJoinTableOverride;
}
current = current.getSuperclass();
}
holderColumnOverride = columnOverride.size() > 0 ? columnOverride : null;
holderJoinColumnOverride = joinColumnOverride.size() > 0 ? joinColumnOverride :
null;
+ holderJoinTableOverride = joinTableOverride.size() > 0 ? joinTableOverride : null;
}
private static Map<String, Column[]> buildColumnOverride(XAnnotatedElement
element, String path) {
@@ -218,7 +303,7 @@
overrides = null;
}
- //fill overriden columns
+ //fill overridden columns
if ( overrides != null ) {
for (AttributeOverride depAttr : overrides) {
columnOverride.put(
@@ -246,7 +331,7 @@
overrides = null;
}
- //fill overriden columns
+ //fill overridden columns
if ( overrides != null ) {
for (AssociationOverride depAttr : overrides) {
columnOverride.put(
@@ -258,6 +343,36 @@
return columnOverride;
}
+ private static Map<String, JoinTable> buildJoinTableOverride(XAnnotatedElement
element, String path) {
+ Map<String, JoinTable> tableOverride = new HashMap<String, JoinTable>();
+ if ( element == null ) return tableOverride;
+ AssociationOverride singleOverride = element.getAnnotation( AssociationOverride.class
);
+ AssociationOverrides multipleOverrides = element.getAnnotation(
AssociationOverrides.class );
+ AssociationOverride[] overrides;
+ if ( singleOverride != null ) {
+ overrides = new AssociationOverride[] { singleOverride };
+ }
+ else if ( multipleOverrides != null ) {
+ overrides = multipleOverrides.value();
+ }
+ else {
+ overrides = null;
+ }
+
+ //fill overridden tables
+ if ( overrides != null ) {
+ for (AssociationOverride depAttr : overrides) {
+ if ( depAttr.joinColumns().length == 0 ) {
+ tableOverride.put(
+ StringHelper.qualify( path, depAttr.name() ),
+ depAttr.joinTable()
+ );
+ }
+ }
+ }
+ return tableOverride;
+ }
+
public void setParentProperty(String parentProperty) {
throw new AssertionFailure( "Setting the parent property to a non component"
);
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -1234,8 +1234,8 @@
( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) )
) {
- if ( property.isAnnotationPresent( JoinTable.class ) ) {
- JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
+ JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
+ if ( joinTableAnn != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
@@ -1412,7 +1412,7 @@
boolean ignoreNotFound = notFound != null && notFound.action().equals(
NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null &&
OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
- JoinTable assocTable = property.getAnnotation( JoinTable.class );
+ JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for (Ejb3JoinColumn joinColumn : joinColumns) {
@@ -1447,7 +1447,7 @@
boolean ignoreNotFound = notFound != null && notFound.action().equals(
NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null &&
OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
- JoinTable assocTable = property.getAnnotation( JoinTable.class );
+ JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for (Ejb3JoinColumn joinColumn : joinColumns) {
@@ -1477,7 +1477,7 @@
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null &&
OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
- JoinTable assocTable = property.getAnnotation( JoinTable.class );
+ JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for (Ejb3JoinColumn joinColumn : joinColumns) {
@@ -1852,7 +1852,7 @@
TableBinder associationTableBinder = new TableBinder();
JoinColumn[] annJoins;
JoinColumn[] annInverseJoins;
- JoinTable assocTable = property.getAnnotation( JoinTable.class );
+ JoinTable assocTable = propertyHolder.getJoinTable( property );
CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
if ( assocTable != null || collectionTable != null ) {
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2010-01-12
18:37:19 UTC (rev 18517)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -647,4 +647,5 @@
mappings.getMappedSuperclass( mappings.getReflectionManager().toClass( declaringClass
) ) :
null;
}
+
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/PropertyHolder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/PropertyHolder.java 2010-01-12
18:37:19 UTC (rev 18517)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/PropertyHolder.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -27,6 +27,7 @@
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
+import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
@@ -72,6 +73,14 @@
*/
JoinColumn[] getOverriddenJoinColumn(String propertyName);
+ /**
+ * return
+ * - null if no join table is present,
+ * - the join table if not overridden,
+ * - the overridden join table otherwise
+ */
+ JoinTable getJoinTable(XProperty property);
+
String getEntityName();
Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation);
Modified:
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java
===================================================================
---
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -470,7 +470,7 @@
if (isMappedBy
&& (property.isAnnotationPresent( JoinColumn.class )
|| property.isAnnotationPresent( JoinColumns.class )
- || property.isAnnotationPresent( JoinTable.class ) ) ) {
+ || propertyHolder.getJoinTable( property ) != null ) ) {
String message = "Associations marked as mappedBy must not define database
mappings like @JoinTable or @JoinColumn: ";
message += StringHelper.qualify( propertyHolder.getPath(), propertyName );
throw new AnnotationException( message );
@@ -1152,7 +1152,7 @@
);
}
else if ( anyAnn != null ) {
- if ( !property.isAnnotationPresent( JoinTable.class ) ) {
+ if ( parentPropertyHolder.getJoinTable( property ) == null ) {
String path = collValue.getOwnerEntityName() + "." +
joinColumns[0].getPropertyName();
throw new AnnotationException(
"@JoinTable is mandatory when @ManyToAny is used: " + path
@@ -1160,7 +1160,7 @@
}
}
else {
- JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
+ JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0
) {
String path = collValue.getOwnerEntityName() + "." +
joinColumns[0].getPropertyName();
throw new AnnotationException(
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/AssociationOverrideTest.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/AssociationOverrideTest.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/AssociationOverrideTest.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -4,6 +4,7 @@
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.test.annotations.TestCase;
+import org.hibernate.test.util.SchemaUtil;
import java.util.ArrayList;
import java.util.Collection;
@@ -12,6 +13,16 @@
public class AssociationOverrideTest extends TestCase {
public void testDottedNotation() throws Exception {
+ assertTrue( SchemaUtil.isTablePresent( "Employee", getCfg() ) );
+ assertTrue( "Overridden @JoinColumn fails",
+ SchemaUtil.isColumnPresent( "Employee", "fld_address_fk",
getCfg() ) );
+
+ assertTrue( "Overridden @JoinTable name fails", SchemaUtil.isTablePresent(
"tbl_empl_sites", getCfg() ) );
+ assertTrue( "Overridden @JoinTable with default @JoinColumn fails",
+ SchemaUtil.isColumnPresent( "tbl_empl_sites", "employee_id",
getCfg() ) );
+ assertTrue( "Overridden @JoinTable.inverseJoinColumn fails",
+ SchemaUtil.isColumnPresent( "tbl_empl_sites", "to_website_fk",
getCfg() ) );
+
Session s = openSession();
Transaction tx = s.beginTransaction();
ContactInfo ci = new ContactInfo();
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/ContactInfo.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/ContactInfo.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/ContactInfo.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -3,6 +3,8 @@
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import java.util.List;
@@ -10,10 +12,11 @@
@Embeddable
public class ContactInfo {
- @ManyToOne(targetEntity=Address.class, cascade=CascadeType.ALL)
+ @ManyToOne(cascade = CascadeType.ALL)
+ @JoinColumn(name="address_id_fk")
Address address;
- @ManyToMany(targetEntity=PhoneNumber.class, cascade=CascadeType.ALL)
+ @ManyToMany(cascade = CascadeType.ALL)
List<PhoneNumber> phoneNumbers;
@Embedded
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/Employee.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/Employee.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/Employee.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -10,34 +10,31 @@
@Entity
public class Employee {
- @Id
+ @Id
int id;
-/* @AssociationOverride(
- name="social.website",
- joinTable=@JoinTable(
- name="xxxwebsites",
- joinColumns=@JoinColumn(name="id"),
- inverseJoinColumns=@JoinColumn(name="id" )
- )
- )
-
- @AssociationOverride(
- name="social.website",
- joinColumns=@JoinColumn(name="id"))
-*/
-
- @AssociationOverride(
- name="social.website",
- joinTable=@JoinTable(
- name="xxxwebsites",
- joinColumns=@JoinColumn(name=""),
- inverseJoinColumns=@JoinColumn(name="id" )
- )
- )
- @Embedded
+ @AssociationOverrides({
+ @AssociationOverride(
+ name = "social.website",
+ joinTable = @JoinTable(
+ name = "tbl_empl_sites",
+ inverseJoinColumns = @JoinColumn(name = "to_website_fk")
+ )
+ ),
+ @AssociationOverride(
+ name = "phoneNumbers",
+ joinTable = @JoinTable(
+ name = "tbl_empl_phone"
+ )
+ ),
+ @AssociationOverride(
+ name="address",
+ joinColumns = @JoinColumn(name="fld_address_fk")
+ )
+ })
+ @Embedded
ContactInfo contactInfo;
-
+
public int getId() {
return id;
}
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/PhoneNumber.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/PhoneNumber.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/PhoneNumber.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -17,8 +17,8 @@
}
int number;
-
- @ManyToMany(mappedBy="contactInfo.phoneNumbers", cascade= CascadeType.ALL)
+
+ @ManyToMany(mappedBy = "contactInfo.phoneNumbers", cascade = CascadeType.ALL)
Collection<Employee> employees;
public Collection<Employee> getEmployees() {
Modified:
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/SocialTouchPoints.java
===================================================================
---
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/SocialTouchPoints.java 2010-01-12
18:37:19 UTC (rev 18517)
+++
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/collectionelement/SocialTouchPoints.java 2010-01-12
18:48:42 UTC (rev 18518)
@@ -9,7 +9,7 @@
public class SocialTouchPoints {
// owning side of many to many
- @ManyToMany(targetEntity=SocialSite.class, cascade= CascadeType.ALL)
+ @ManyToMany(cascade= CascadeType.ALL)
List<SocialSite> website;
public List<SocialSite> getWebsite() {