HHH-4782 supports @AssociationOverride.joinTable
HHH-4679 add tests for dot notations in @AssocuiationOverride git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18518 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
3f0e54c5d2
commit
b1f925458e
|
@ -33,6 +33,7 @@ import javax.persistence.Column;
|
|||
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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
if ( property == null ) {
|
||||
this.currentPropertyColumnOverride = null;
|
||||
this.currentPropertyJoinColumnOverride = null;
|
||||
this.currentPropertyJoinTableOverride = null;
|
||||
}
|
||||
else {
|
||||
this.currentPropertyColumnOverride = buildColumnOverride(
|
||||
|
@ -93,6 +98,13 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
// 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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
* 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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
|
||||
/**
|
||||
* 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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
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 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
overrides = null;
|
||||
}
|
||||
|
||||
//fill overriden columns
|
||||
//fill overridden columns
|
||||
if ( overrides != null ) {
|
||||
for (AttributeOverride depAttr : overrides) {
|
||||
columnOverride.put(
|
||||
|
@ -246,7 +331,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
overrides = null;
|
||||
}
|
||||
|
||||
//fill overriden columns
|
||||
//fill overridden columns
|
||||
if ( overrides != null ) {
|
||||
for (AssociationOverride depAttr : overrides) {
|
||||
columnOverride.put(
|
||||
|
@ -258,6 +343,36 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
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" );
|
||||
}
|
||||
|
|
|
@ -1234,8 +1234,8 @@ public final class AnnotationBinder {
|
|||
( 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 @@ public final class AnnotationBinder {
|
|||
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 @@ public final class AnnotationBinder {
|
|||
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 @@ public final class AnnotationBinder {
|
|||
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 @@ public final class AnnotationBinder {
|
|||
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 ) {
|
||||
|
|
|
@ -647,4 +647,5 @@ public class BinderHelper {
|
|||
mappings.getMappedSuperclass( mappings.getReflectionManager().toClass( declaringClass ) ) :
|
||||
null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.persistence.Column;
|
|||
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 @@ public interface PropertyHolder {
|
|||
*/
|
||||
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);
|
||||
|
|
|
@ -470,7 +470,7 @@ public abstract class CollectionBinder {
|
|||
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 @@ public abstract class CollectionBinder {
|
|||
);
|
||||
}
|
||||
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 @@ public abstract class CollectionBinder {
|
|||
}
|
||||
}
|
||||
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(
|
||||
|
|
|
@ -4,6 +4,7 @@ import junit.framework.Assert;
|
|||
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 @@ import java.util.List;
|
|||
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();
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.hibernate.test.annotations.collectionelement;
|
|||
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 @@ import java.util.List;
|
|||
|
||||
@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
|
||||
|
|
|
@ -10,34 +10,31 @@ import javax.persistence.JoinTable;
|
|||
|
||||
@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;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ public class PhoneNumber {
|
|||
}
|
||||
|
||||
int number;
|
||||
|
||||
@ManyToMany(mappedBy="contactInfo.phoneNumbers", cascade= CascadeType.ALL)
|
||||
|
||||
@ManyToMany(mappedBy = "contactInfo.phoneNumbers", cascade = CascadeType.ALL)
|
||||
Collection<Employee> employees;
|
||||
|
||||
public Collection<Employee> getEmployees() {
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||
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() {
|
||||
|
|
Loading…
Reference in New Issue