|
From: <hib...@li...> - 2006-04-30 04:08:57
|
Author: epbernard
Date: 2006-04-30 00:08:48 -0400 (Sun, 30 Apr 2006)
New Revision: 9833
Added:
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/BiggestForest.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/ForestType.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/ManyToOneJoinTest.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/TreeType.java
Modified:
trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AbstractPropertyHolder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ClassPropertyHolder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/CollectionPropertyHolder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ComponentPropertyHolder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/Ejb3JoinColumn.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolderBuilder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ToOneMappedBySecondPass.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/EntityBinder.java
trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/TableBinder.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/Death.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/JoinTest.java
trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/onetoone/OneToOneTest.java
Log:
ANN-158 to one associations through an association table (bidirectional)
Modified: trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml
===================================================================
--- trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/doc/reference/en/modules/entity.xml 2006-04-30 04:08:48 UTC (rev 9833)
@@ -961,11 +961,13 @@
<title>One-to-one</title>
<para>You can associate entity beans through a one-to-one relationship
- using <literal>@OneToOne</literal>. There are two cases for one-to-one
- associations: either the associated entities share the same primary
- keys values or a foreign key is held by one of the entities (note that
- this FK column in the database should be constrained unique to
- simulate one-to-one multiplicity).</para>
+ using <literal>@OneToOne</literal>. There are three cases for
+ one-to-one associations: either the associated entities share the same
+ primary keys values, a foreign key is held by one of the entities
+ (note that this FK column in the database should be constrained unique
+ to simulate one-to-one multiplicity), or a association table is used
+ to store the link between the 2 entities (a unique constraint has to
+ be defined on each fk to ensure the one to one multiplicity)</para>
<para>First, we map a real one-to-one association using shared primary
keys:</para>
@@ -1051,6 +1053,53 @@
example <literal>passport_id</literal> because the property name is
<literal>passport</literal> and the column id of <literal>Passport
</literal>is <literal>id</literal>.</para>
+
+ <para>The third possibility (using an association table) is very
+ exotic.</para>
+
+ <programlisting>
+@Entity
+public class Customer implements Serializable {
+ @OneToOne(cascade = CascadeType.ALL)
+ <emphasis role="bold">@JoinTable(name = "CustomerPassports"
+ joinColumns = @JoinColumn(name="customer_fk"),
+ inverseJoinColumns = @JoinColumns(name="passport_fk")</emphasis>
+ )
+ public Passport getPassport() {
+ ...
+ }
+
+@Entity
+public class Passport implements Serializable {
+ @OneToOne(<emphasis role="bold">mappedBy = "passport"</emphasis>)
+ public Customer getOwner() {
+ ...
+}
+ </programlisting>
+
+ <para>A <classname>Customer</classname> is linked to a
+ <classname>Passport</classname> through a association table named
+ <literal>CustomerPassports</literal> ; this association table has a
+ foreign key column named <literal>passport_fk</literal> pointing to
+ the <literal>Passport</literal> table (materialized by the
+ <literal>inverseJoinColumn</literal>, and a foreign key column named
+ <literal>customer_fk</literal> pointing to the
+ <literal>Customer</literal> table materialized by the
+ <literal>joinColumns</literal> attribute.</para>
+
+ <para>The association may be bidirectional. In a bidirectional
+ relationship, one of the sides (and only one) has to be the owner: the
+ owner is responsible for the association column(s) update. To declare
+ a side as <emphasis>not</emphasis> responsible for the relationship,
+ the attribute <literal>mappedBy</literal> is used.
+ <literal>mappedBy</literal> refers to the property name of the
+ association on the owner side. In our case, this is
+ <literal>passport</literal>. As you can see, you don't have to (must
+ not) declare the join column since it has already been declared on the
+ owners side.</para>
+
+ <para>You must declare the join table name and the join columns
+ explicitly in such a mapping.</para>
</sect3>
<sect3>
@@ -1101,6 +1150,29 @@
public interface Company {
...
</programlisting>
+
+ <para>You can alse map a many to one association through an
+ association table. This association table described by the
+ <literal>@JoinTable</literal> annotation will contains a foreign key
+ referencing back the entity table (through
+ <literal>@JoinTable.joinColumns</literal>) and a a foreign key
+ referencing the target entity table (through
+ <literal>@JoinTable.inverseJoinColumns</literal>).</para>
+
+ <programlisting>
+@Entity()
+public class Flight implements Serializable {
+ @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
+ <emphasis role="bold">@JoinTable(name="Flight_Company",
+ joinColumns = @JoinColumn(name="FLIGHT_ID"),
+ inverseJoinColumns = @JoinColumns(name="COMP_ID")
+ )</emphasis>
+ public Company getCompany() {
+ return company;
+ }
+ ...
+}
+ </programlisting>
</sect3>
<sect3 id="entity-mapping-association-collections">
@@ -1308,8 +1380,8 @@
side as the owning side, you have to remove the
<literal>mappedBy</literal> element and set the many to one
<literal>@JoinColumn</literal> as insertable and updatable to
- false. This solution is obviously not optimized from the number of
- needed statements.</para>
+ false. This solution is obviously not optimized and will produce
+ some additional UPDATE statements.</para>
<programlisting>@Entity
public class Troop {
@@ -2563,7 +2635,7 @@
<para>EJB3 comes with the <literal>fetch</literal> option to define
lazy loading and fetching modes, however Hibernate has a much more
option set in this area. To fine tune the lazy loading and fetching
- strategies, some additional annotations have been introduced: </para>
+ strategies, some additional annotations have been introduced:</para>
<itemizedlist>
<listitem>
@@ -2589,7 +2661,7 @@
</listitem>
<listitem>
- <para> <literal>@Fetch</literal>: defines the fetching strategy
+ <para><literal>@Fetch</literal>: defines the fetching strategy
used to load the association. <literal>FetchMode</literal> can be
<literal>SELECT</literal> (a select is triggered when the
association needs to be loaded), <literal>SUBSELECT</literal>
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AbstractPropertyHolder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AbstractPropertyHolder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AbstractPropertyHolder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -13,17 +13,17 @@
import javax.persistence.JoinColumn;
import javax.persistence.MappedSuperclass;
+import org.hibernate.AssertionFailure;
import org.hibernate.reflection.XAnnotatedElement;
import org.hibernate.reflection.XClass;
import org.hibernate.reflection.XProperty;
import org.hibernate.util.StringHelper;
-import org.hibernate.AssertionFailure;
/**
* @author Emmanuel Bernard
*/
public abstract class AbstractPropertyHolder implements PropertyHolder {
- private PropertyHolder parent;
+ protected PropertyHolder parent;
private Map<String, Column[]> holderColumnOverride;
private Map<String, Column[]> currentPropertyColumnOverride;
private Map<String, JoinColumn[]> holderJoinColumnOverride;
@@ -183,6 +183,6 @@
}
public void setParentProperty(String parentProperty) {
- throw new AssertionFailure("Setting the parent property to a non component");
+ throw new AssertionFailure( "Setting the parent property to a non component" );
}
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/AnnotationBinder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -61,6 +61,7 @@
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.Columns;
+import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
@@ -68,6 +69,8 @@
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Index;
+import org.hibernate.annotations.LazyToOne;
+import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
@@ -75,16 +78,13 @@
import org.hibernate.annotations.OrderBy;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Parameter;
+import org.hibernate.annotations.Parent;
import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.annotations.Where;
-import org.hibernate.annotations.Parent;
-import org.hibernate.annotations.LazyToOne;
-import org.hibernate.annotations.Fetch;
-import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.Nullability;
@@ -110,8 +110,8 @@
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
+import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
-import org.hibernate.mapping.ToOne;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
@@ -377,6 +377,7 @@
final boolean hasJoinedColumns = inheritanceState.hasParents
&& InheritanceType.JOINED.equals( inheritanceState.type );
if ( hasJoinedColumns ) {
+ //@Inheritance(JOINED) subclass need to link back to the super entity
PrimaryKeyJoinColumns jcsAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumns.class );
boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
if ( explicitInheritanceJoinedColumns ) {
@@ -386,7 +387,7 @@
for ( int colIndex = 0; colIndex < nbrOfInhJoinedColumns ; colIndex++ ) {
jcAnn = jcsAnn.value()[colIndex];
inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
- jcAnn, superEntity.getIdentifier(),
+ jcAnn, null, superEntity.getIdentifier(),
(Map<String, Join>) null, (PropertyHolder) null, mappings
);
}
@@ -395,7 +396,7 @@
PrimaryKeyJoinColumn jcAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumn.class );
inheritanceJoinedColumns = new Ejb3JoinColumn[1];
inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
- jcAnn, superEntity.getIdentifier(),
+ jcAnn, null, superEntity.getIdentifier(),
(Map<String, Join>) null, (PropertyHolder) null, mappings
);
}
@@ -498,7 +499,7 @@
PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
clazzToProcess,
persistentClass,
- entityBinder.getSecondaryTables(), mappings
+ entityBinder, mappings
);
javax.persistence.SecondaryTable secTabAnn = annotatedClass.getAnnotation(
@@ -507,9 +508,7 @@
javax.persistence.SecondaryTables secTabsAnn = annotatedClass.getAnnotation(
javax.persistence.SecondaryTables.class
);
- PrimaryKeyJoinColumn joinColAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumn.class );
- PrimaryKeyJoinColumns joinColsAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumns.class );
- entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn, joinColAnn, joinColsAnn );
+ entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn );
OnDelete onDeleteAnn = annotatedClass.getAnnotation( OnDelete.class );
boolean onDeleteAppropriate = false;
@@ -977,8 +976,10 @@
propertyHolder.setParentProperty( property.getName() );
}
else {
- throw new AnnotationException("@Parent cannot be applied outside an embeddable object: "
- + StringHelper.qualify( propertyHolder.getPath(), property.getName() ) );
+ throw new AnnotationException(
+ "@Parent cannot be applied outside an embeddable object: "
+ + StringHelper.qualify( propertyHolder.getPath(), property.getName() )
+ );
}
return;
}
@@ -1026,11 +1027,17 @@
|| property.isAnnotationPresent( OneToOne.class ) )
) {
if ( property.isAnnotationPresent( JoinTable.class ) ) {
- JoinTable assocTable = property.getAnnotation( JoinTable.class );
- //entityBinder.firstLevelSecondaryTablesBinding(assocTable);
- throw new NotYetImplementedException(
- "association table on a single ended association is not yet supported"
+ JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
+ joinColumns = Ejb3JoinColumn.buildJoinColumns(
+ joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
+ propertyHolder, inferredData.getPropertyName(), mappings
);
+ if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
+ throw new AnnotationException(
+ "JoinTable.name() on a @ToOne association has to be explicit: "
+ + StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() )
+ );
+ }
}
else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
@@ -1178,6 +1185,13 @@
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 );
+ if ( assocTable != null ) {
+ Join join = propertyHolder.addJoin( assocTable, false );
+ for ( Ejb3JoinColumn joinColumn : joinColumns ) {
+ joinColumn.setSecondaryTableName( join.getTable().getName() );
+ }
+ }
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade ),
joinColumns,
@@ -1198,6 +1212,13 @@
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 );
+ if ( assocTable != null ) {
+ Join join = propertyHolder.addJoin( assocTable, false );
+ for ( Ejb3JoinColumn joinColumn : joinColumns ) {
+ joinColumn.setSecondaryTableName( join.getTable().getName() );
+ }
+ }
bindOneToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade ),
joinColumns,
@@ -1264,7 +1285,7 @@
collectionBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
Ejb3Column[] elementColumns = null;
- PropertyData wrappedInferredData = new WrappedInferredData(inferredData, "element");
+ PropertyData wrappedInferredData = new WrappedInferredData( inferredData, "element" );
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent(
Formula.class
) ) {
@@ -1299,8 +1320,10 @@
);
}
- org.hibernate.annotations.MapKey hibMapKeyAnn = property.getAnnotation( org.hibernate.annotations.MapKey.class );
- wrappedInferredData = new WrappedInferredData(inferredData, "key");
+ org.hibernate.annotations.MapKey hibMapKeyAnn = property.getAnnotation(
+ org.hibernate.annotations.MapKey.class
+ );
+ wrappedInferredData = new WrappedInferredData( inferredData, "key" );
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
hibMapKeyAnn != null && hibMapKeyAnn.columns().length > 0 ? hibMapKeyAnn.columns() : null,
null,
@@ -1310,7 +1333,7 @@
entityBinder.getSecondaryTables(),
mappings
);
- collectionBinder.setMapKeyColumns(mapColumns);
+ collectionBinder.setMapKeyColumns( mapColumns );
//potential element
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
@@ -1724,24 +1747,26 @@
ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
OneToOne oneToOne = property.getAnnotation( OneToOne.class );
FetchType fetchType;
- if (manyToOne != null) {
+ if ( manyToOne != null ) {
fetchType = manyToOne.fetch();
}
- else if (oneToOne != null) {
+ else if ( oneToOne != null ) {
fetchType = oneToOne.fetch();
}
else {
- throw new AssertionFailure( "Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne");
+ throw new AssertionFailure(
+ "Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne"
+ );
}
- if (lazy != null) {
- toOne.setLazy( ! (lazy.value() == LazyToOneOption.FALSE) );
- toOne.setUnwrapProxy( (lazy.value() == LazyToOneOption.NO_PROXY) );
+ if ( lazy != null ) {
+ toOne.setLazy( ! ( lazy.value() == LazyToOneOption.FALSE ) );
+ toOne.setUnwrapProxy( ( lazy.value() == LazyToOneOption.NO_PROXY ) );
}
else {
toOne.setLazy( fetchType == FetchType.LAZY );
toOne.setUnwrapProxy( false );
}
- if (fetch != null) {
+ if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
toOne.setFetchMode( FetchMode.JOIN );
toOne.setLazy( false );
@@ -1751,7 +1776,7 @@
toOne.setFetchMode( FetchMode.SELECT );
}
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
- throw new AnnotationException( "Use of FetchMode.SUBSELECT not allowed on ToOne associations");
+ throw new AnnotationException( "Use of FetchMode.SUBSELECT not allowed on ToOne associations" );
}
else {
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
@@ -1798,16 +1823,19 @@
if ( trueOneToOne || mapToPK || ! isDefault( mappedBy ) ) {
//is a true one-to-one
//FIXME referencedColumnName ignored => ordering may fail.
+
org.hibernate.mapping.OneToOne value = new org.hibernate.mapping.OneToOne(
propertyHolder.getTable(), propertyHolder.getPersistentClass()
);
value.setPropertyName( propertyName );
+ String referencedEntityName;
if ( isDefault( targetEntity, mappings ) ) {
- value.setReferencedEntityName( inferredData.getClassOrElementName() );
+ referencedEntityName = inferredData.getClassOrElementName();
}
else {
- value.setReferencedEntityName( targetEntity.getName() );
+ referencedEntityName = targetEntity.getName();
}
+ value.setReferencedEntityName( referencedEntityName );
defineFetchingStrategy( value, inferredData.getProperty() );
//value.setFetchMode( fetchMode );
value.setCascadeDeleteEnabled( cascadeOnDelete );
@@ -1819,7 +1847,12 @@
ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
ForeignKeyDirection.FOREIGN_KEY_TO_PARENT
);
-
+ PropertyBinder binder = new PropertyBinder();
+ binder.setName( propertyName );
+ binder.setValue( value );
+ binder.setCascade( cascadeStrategy );
+ binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
+ Property prop = binder.make();
if ( ! isDefault( mappedBy ) ) {
mappings.addSecondPass(
new ToOneMappedBySecondPass(
@@ -1827,7 +1860,7 @@
value,
propertyHolder.getEntityName(),
propertyName,
- mappings
+ prop, propertyHolder, ignoreNotFound, mappings
)
);
}
@@ -1840,17 +1873,11 @@
path, mappings
)
);
+ //no column associated since its a one to one
+ propertyHolder.addProperty( prop );
}
- PropertyBinder binder = new PropertyBinder();
- binder.setName( propertyName );
- binder.setValue( value );
- binder.setCascade( cascadeStrategy );
- binder.setPropertyAccessorName( inferredData.getDefaultAccess() );
- Property prop = binder.make();
- prop.setCascade( cascadeStrategy );
- //no column associated since its a one to one
- propertyHolder.addProperty( prop );
+
}
else {
//has a FK on the table
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ClassPropertyHolder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ClassPropertyHolder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ClassPropertyHolder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -3,7 +3,9 @@
import java.util.HashMap;
import java.util.Map;
+import javax.persistence.JoinTable;
+import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
@@ -19,6 +21,7 @@
private PersistentClass persistentClass;
private Map<String, Join> joins;
private transient Map<String, Join> joinsPerRealTableName;
+ private EntityBinder entityBinder;
public ClassPropertyHolder(
PersistentClass persistentClass, XClass clazzToProcess, Map<String, Join> joins, ExtendedMappings mappings
@@ -28,6 +31,14 @@
this.joins = joins;
}
+ public ClassPropertyHolder(
+ PersistentClass persistentClass, XClass clazzToProcess, EntityBinder entityBinder,
+ ExtendedMappings mappings
+ ) {
+ this( persistentClass, clazzToProcess, entityBinder.getSecondaryTables(), mappings );
+ this.entityBinder = entityBinder;
+ }
+
public String getEntityName() {
return persistentClass.getEntityName();
}
@@ -43,6 +54,12 @@
}
}
+ public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
+ Join join = entityBinder.addJoin( joinTableAnn, this, noDelayInPkColumnCreation );
+ this.joins = entityBinder.getSecondaryTables();
+ return join;
+ }
+
public void addProperty(Property prop) {
if ( prop.getValue() instanceof Component ) {
//TODO handle quote and non quote table comparison
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/CollectionPropertyHolder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/CollectionPropertyHolder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/CollectionPropertyHolder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -1,8 +1,11 @@
//$Id$
package org.hibernate.cfg;
+import javax.persistence.JoinTable;
+
import org.hibernate.AssertionFailure;
import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
@@ -57,4 +60,8 @@
//Ejb3Column.checkPropertyConsistency( ); //already called earlier
throw new AssertionFailure( "addProperty to a join table of a collection: does it make sense?" );
}
+
+ public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
+ throw new AssertionFailure( "Add a <join> in a second pass" );
+ }
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ComponentPropertyHolder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ComponentPropertyHolder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ComponentPropertyHolder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -3,6 +3,7 @@
import javax.persistence.Column;
import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
import org.hibernate.AnnotationException;
import org.hibernate.mapping.Component;
@@ -10,6 +11,7 @@
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Join;
/**
* Component implementation of property holder
@@ -47,6 +49,11 @@
addProperty( prop );
}
+ public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
+ return parent.addJoin( joinTableAnn, noDelayInPkColumnCreation );
+
+ }
+
public ComponentPropertyHolder(
Component component, String path, PropertyData inferredData, PropertyHolder parent,
ExtendedMappings mappings
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/Ejb3JoinColumn.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/Ejb3JoinColumn.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/Ejb3JoinColumn.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -114,7 +114,7 @@
StringHelper.qualify( propertyHolder.getPath(), propertyName )
);
if ( actualColumns == null ) actualColumns = anns;
- if ( actualColumns == null ) {
+ if ( actualColumns == null || actualColumns.length == 0 ) {
return new Ejb3JoinColumn[]{
buildJoinColumn( (JoinColumn) null, mappedBy, joins, propertyHolder, propertyName, mappings )
};
@@ -199,7 +199,8 @@
* Build JoinColumn for a JOINED hierarchy
*/
public static Ejb3JoinColumn buildJoinColumn(
- PrimaryKeyJoinColumn ann,
+ PrimaryKeyJoinColumn pkJoinAnn,
+ JoinColumn joinAnn,
Value identifier,
Map<String, Join> joins,
PropertyHolder propertyHolder, ExtendedMappings mappings
@@ -207,14 +208,27 @@
Column col = (Column) identifier.getColumnIterator().next();
String defaultName = mappings.getLogicalColumnName( col.getName(), identifier.getTable() );
- if ( ann != null ) {
- String sqlType = ann.columnDefinition().equals( "" ) ? null : ann.columnDefinition();
- String name = ann.name().equals( "" ) ? defaultName : ann.name();
+ if ( pkJoinAnn != null || joinAnn != null ) {
+ String colName;
+ String columnDefinition;
+ String referencedColumnName;
+ if ( pkJoinAnn != null ) {
+ colName = pkJoinAnn.name();
+ columnDefinition = pkJoinAnn.columnDefinition();
+ referencedColumnName = pkJoinAnn.referencedColumnName();
+ }
+ else {
+ colName = joinAnn.name();
+ columnDefinition = joinAnn.columnDefinition();
+ referencedColumnName = joinAnn.referencedColumnName();
+ }
+ String sqlType = "".equals( columnDefinition ) ? null : columnDefinition;
+ String name = "".equals( colName ) ? defaultName : colName;
return new Ejb3JoinColumn(
sqlType,
name, false, false,
true, true,
- ann.referencedColumnName(),
+ referencedColumnName,
null, joins,
propertyHolder, null, null, false, mappings
);
@@ -230,10 +244,11 @@
/**
* Override persistent class on oneToMany Cases for late settings
+ * Must only be used on second level pass binding
*/
public void setPersistentClass(PersistentClass persistentClass, Map<String, Join> joins) {
//FIXME shouldn't we deduce the classname from the persistentclasS?
- this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder( null, persistentClass, joins, getMappings() );
+ this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder( persistentClass, joins, getMappings() );
}
public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -2,11 +2,13 @@
import javax.persistence.Column;
import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Join;
/**
* Property holder abstract property containers from their direct implementation
@@ -43,4 +45,6 @@
String getEntityName();
void addProperty(Property prop, Ejb3Column[] columns);
+
+ Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation);
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolderBuilder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolderBuilder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/PropertyHolderBuilder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -3,6 +3,7 @@
import java.util.Map;
+import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
@@ -22,9 +23,11 @@
public static PropertyHolder buildPropertyHolder(
XClass clazzToProcess,
PersistentClass persistentClass,
- Map<String, Join> joins, ExtendedMappings mappings
+ EntityBinder entityBinder,
+ //Map<String, Join> joins,
+ ExtendedMappings mappings
) {
- return (PropertyHolder) new ClassPropertyHolder( persistentClass, clazzToProcess, joins, mappings );
+ return (PropertyHolder) new ClassPropertyHolder( persistentClass, clazzToProcess, entityBinder, mappings );
}
/**
@@ -52,10 +55,13 @@
return new CollectionPropertyHolder( collection, path, clazzToProcess, property, mappings );
}
+ /**
+ * must only be used on second level phases (<join> has to be settled already)
+ */
public static PropertyHolder buildPropertyHolder(
PersistentClass persistentClass, Map<String, Join> joins,
ExtendedMappings mappings
) {
- return buildPropertyHolder( null, persistentClass, joins, mappings );
+ return (PropertyHolder) new ClassPropertyHolder( persistentClass, null, joins, mappings );
}
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ToOneMappedBySecondPass.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ToOneMappedBySecondPass.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/ToOneMappedBySecondPass.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -1,42 +1,55 @@
//$Id$
package org.hibernate.cfg;
+import java.util.Iterator;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.Join;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.util.StringHelper;
public class ToOneMappedBySecondPass implements SecondPass {
private String mappedBy;
private ToOne value;
- private Mappings mappings;
+ private ExtendedMappings mappings;
private String ownerEntity;
private String ownerProperty;
+ private PropertyHolder propertyHolder;
+ private Property property;
+ private boolean ignoreNotFound;
public ToOneMappedBySecondPass(
- String mappedBy, ToOne value, String ownerEntity, String ownerProperty, Mappings mappings
+ String mappedBy, ToOne value, String ownerEntity, String ownerProperty, Property property,
+ PropertyHolder propertyHolder, boolean ignoreNotFound,
+ ExtendedMappings mappings
) {
this.ownerEntity = ownerEntity;
this.ownerProperty = ownerProperty;
this.mappedBy = mappedBy;
this.value = value;
+ this.propertyHolder = propertyHolder;
this.mappings = mappings;
+ this.property = property;
+ this.ignoreNotFound = ignoreNotFound;
}
public void doSecondPass(Map persistentClasses, Map inheritedMetas) throws MappingException {
PersistentClass otherSide = (PersistentClass) persistentClasses.get( value.getReferencedEntityName() );
- Property property;
+ Property otherSideProperty;
try {
if ( otherSide == null ) {
throw new MappingException( "Unable to find entity: " + value.getReferencedEntityName() );
}
- property = otherSide.getProperty( mappedBy );
+ otherSideProperty = otherSide.getProperty( mappedBy );
}
catch (MappingException e) {
throw new AnnotationException(
@@ -45,10 +58,56 @@
+ StringHelper.qualify( value.getReferencedEntityName(), mappedBy )
);
}
- if ( property.getValue() instanceof OneToOne ) {
- //do nothing
+ if ( otherSideProperty.getValue() instanceof OneToOne ) {
+ propertyHolder.addProperty( property );
}
- else if ( property.getValue() instanceof ManyToOne ) {
+ else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
+ Iterator it = otherSide.getJoinIterator();
+ Join otherSideJoin = null;
+ while ( it.hasNext() ) {
+ otherSideJoin = (Join) it.next();
+ if ( otherSideJoin.containsProperty( otherSideProperty ) ) {
+ break;
+ }
+ }
+ if ( otherSideJoin != null ) {
+ //@OneToOne @JoinTable
+ Join mappedByJoin = buildJoin(
+ (PersistentClass) persistentClasses.get( ownerEntity ), otherSideProperty, otherSideJoin
+ );
+ ManyToOne manyToOne = new ManyToOne( mappedByJoin.getTable() );
+ //FIXME use ignore not found here
+ manyToOne.setIgnoreNotFound( ignoreNotFound );
+ manyToOne.setCascadeDeleteEnabled( value.isCascadeDeleteEnabled() );
+ manyToOne.setEmbedded( value.isEmbedded() );
+ manyToOne.setFetchMode( value.getFetchMode() );
+ manyToOne.setLazy( value.isLazy() );
+ manyToOne.setReferencedEntityName( value.getReferencedEntityName() );
+ manyToOne.setUnwrapProxy( value.isUnwrapProxy() );
+ property.setValue( manyToOne );
+ Iterator otherSideJoinKeyColumns = otherSideJoin.getKey().getColumnIterator();
+ while ( otherSideJoinKeyColumns.hasNext() ) {
+ Column column = (Column) otherSideJoinKeyColumns.next();
+ Column copy = new Column();
+ copy.setLength( column.getLength() );
+ copy.setScale( column.getScale() );
+ copy.setValue( manyToOne );
+ copy.setName( column.getQuotedName() );
+ copy.setNullable( column.isNullable() );
+ copy.setPrecision( column.getPrecision() );
+ copy.setUnique( column.isUnique() );
+ copy.setSqlType( column.getSqlType() );
+ copy.setCheckConstraint( column.getCheckConstraint() );
+ copy.setComment( column.getComment() );
+ copy.setDefaultValue( column.getDefaultValue() );
+ manyToOne.addColumn( copy );
+ }
+ mappedByJoin.addProperty( property );
+ }
+ else {
+ propertyHolder.addProperty( property );
+ }
+
value.setReferencedPropertyName( mappedBy );
String propertyRef = value.getReferencedPropertyName();
@@ -68,4 +127,40 @@
);
}
}
+
+ //dirty dupe of EntityBinder.bindSecondaryTable
+ private Join buildJoin(PersistentClass persistentClass, Property otherSideProperty, Join originalJoin) {
+ Join join = new Join();
+ join.setPersistentClass( persistentClass );
+
+ //no check constraints available on joins
+ join.setTable( originalJoin.getTable() );
+ join.setInverse( true );
+ SimpleValue key = new DependantValue( join.getTable(), persistentClass.getIdentifier() );
+ join.setKey( key );
+ join.setSequentialSelect( false );
+ join.setOptional( true ); //perhaps not quite per-spec, but a Good Thing anyway
+ key.setCascadeDeleteEnabled( false );
+ Iterator mappedByColumns = otherSideProperty.getValue().getColumnIterator();
+ while ( mappedByColumns.hasNext() ) {
+ Column column = (Column) mappedByColumns.next();
+ Column copy = new Column();
+ copy.setLength( column.getLength() );
+ copy.setScale( column.getScale() );
+ copy.setValue( key );
+ copy.setName( column.getQuotedName() );
+ copy.setNullable( column.isNullable() );
+ copy.setPrecision( column.getPrecision() );
+ copy.setUnique( column.isUnique() );
+ copy.setSqlType( column.getSqlType() );
+ copy.setCheckConstraint( column.getCheckConstraint() );
+ copy.setComment( column.getComment() );
+ copy.setDefaultValue( column.getDefaultValue() );
+ key.addColumn( copy );
+ }
+ join.createPrimaryKey();
+ join.createForeignKey();
+ persistentClass.addJoin( join );
+ return join;
+ }
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/CollectionBinder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -258,9 +258,13 @@
log.debug( "Collection role: " + StringHelper.qualify( propertyHolder.getPath(), propertyName ) );
collection.setRole( StringHelper.qualify( propertyHolder.getPath(), propertyName ) );
- if (property.isAnnotationPresent( org.hibernate.annotations.MapKey.class) && mapKeyPropertyName != null) {
- throw new AnnotationException("Cannot mix @javax.persistence.MapKey and @org.hibernate.annotations.MapKey "
- + "on the same collection: " + StringHelper.qualify( propertyHolder.getPath(), propertyName ) );
+ if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) && mapKeyPropertyName != null ) {
+ throw new AnnotationException(
+ "Cannot mix @javax.persistence.MapKey and @org.hibernate.annotations.MapKey "
+ + "on the same collection: " + StringHelper.qualify(
+ propertyHolder.getPath(), propertyName
+ )
+ );
}
//set laziness
@@ -382,27 +386,29 @@
ManyToMany manyToMany = property.getAnnotation( ManyToMany.class );
CollectionOfElements elements = property.getAnnotation( CollectionOfElements.class );
FetchType fetchType;
- if (oneToMany != null) {
+ if ( oneToMany != null ) {
fetchType = oneToMany.fetch();
}
- else if (manyToMany != null) {
+ else if ( manyToMany != null ) {
fetchType = manyToMany.fetch();
}
- else if (elements != null) {
+ else if ( elements != null ) {
fetchType = elements.fetch();
}
else {
- throw new AssertionFailure( "Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @CollectionOfElements");
+ throw new AssertionFailure(
+ "Define fetch strategy on a property not annotated with @ManyToOne nor @OneToMany nor @CollectionOfElements"
+ );
}
- if (lazy != null) {
- collection.setLazy( ! (lazy.value() == LazyCollectionOption.FALSE) );
+ if ( lazy != null ) {
+ collection.setLazy( ! ( lazy.value() == LazyCollectionOption.FALSE ) );
collection.setExtraLazy( lazy.value() == LazyCollectionOption.EXTRA );
}
else {
collection.setLazy( fetchType == FetchType.LAZY );
collection.setExtraLazy( false );
}
- if (fetch != null) {
+ if ( fetch != null ) {
if ( fetch.value() == org.hibernate.annotations.FetchMode.JOIN ) {
collection.setFetchMode( FetchMode.JOIN );
collection.setLazy( false );
@@ -412,8 +418,8 @@
}
else if ( fetch.value() == org.hibernate.annotations.FetchMode.SUBSELECT ) {
collection.setFetchMode( FetchMode.SELECT );
- collection.setSubselectLoadable(true);
- collection.getOwner().setSubselectLoadableCollections(true);
+ collection.setSubselectLoadable( true );
+ collection.getOwner().setSubselectLoadableCollections( true );
}
else {
throw new AssertionFailure( "Unknown FetchMode: " + fetch.value() );
@@ -473,12 +479,32 @@
XProperty property, boolean unique,
TableBinder associationTableBinder, boolean ignoreNotFound, ExtendedMappings mappings
) {
- boolean isEntity = persistentClasses.containsKey( collType );
- if ( isEntity
+ PersistentClass persistentClass = (PersistentClass) persistentClasses.get( collType );
+ boolean reversePropertyInJoin = false;
+ if ( persistentClass != null && StringHelper.isNotEmpty( this.mappedBy ) ) {
+ try {
+ reversePropertyInJoin = 0 != persistentClass.getJoinNumber(
+ persistentClass.getProperty( this.mappedBy )
+ );
+ }
+ catch (MappingException e) {
+ StringBuilder error = new StringBuilder( 80 );
+ error.append( "mappedBy reference an unknown property: " )
+ .append( collType ).append( "." ).append( this.mappedBy )
+ .append( " in " )
+ .append( collection.getOwnerEntityName() )
+ .append( "." )
+ .append( this.mappedBy );
+ throw new AnnotationException( error.toString() );
+ }
+ }
+ if ( persistentClass != null
+ && ! reversePropertyInJoin
&& oneToMany
&& ! this.isExplicitAssociationTable
- && ( joinColumns[0].isImplicit() && ! AnnotationBinder.isDefault( this.mappedBy )
- || ! fkJoinColumns[0].isImplicit() ) ) {
+ && ( joinColumns[0].isImplicit() && ! AnnotationBinder.isDefault( this.mappedBy ) //implicit @JoinColumn
+ || ! fkJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn
+ ) {
//this is a Foreign key
bindOneToManySecondPass(
getCollection(),
@@ -780,7 +806,15 @@
.append( joinColumns[0].getPropertyName() );
throw new AnnotationException( error.toString() );
}
- Table table = ( (Collection) otherSideProperty.getValue() ).getCollectionTable();
+ Table table;
+ if ( otherSideProperty.getValue() instanceof Collection ) {
+ //this is a collection on the other side
+ table = ( (Collection) otherSideProperty.getValue() ).getCollectionTable();
+ }
+ else {
+ //This is a ToOne with a @JoinTable or a regular property
+ table = otherSideProperty.getValue().getTable();
+ }
collValue.setCollectionTable( table );
String entityName = collectionEntity.getEntityName();
for ( Ejb3JoinColumn column : joinColumns ) {
@@ -979,7 +1013,24 @@
final String mappedBy = columns[0].getMappedBy();
if ( StringHelper.isNotEmpty( mappedBy ) ) {
final Property property = referencedEntity.getProperty( mappedBy );
- Iterator mappedByColumns = ( (Collection) property.getValue() ).getKey().getColumnIterator();
+ Iterator mappedByColumns;
+ if ( property.getValue() instanceof Collection ) {
+ mappedByColumns = ( (Collection) property.getValue() ).getKey().getColumnIterator();
+ }
+ else {
+ //find the appropriate reference key, can be in a join
+ Iterator joinsIt = referencedEntity.getJoinIterator();
+ KeyValue key = null;
+ while ( joinsIt.hasNext() ) {
+ Join join = (Join) joinsIt.next();
+ if ( join.containsProperty( property ) ) {
+ key = join.getKey();
+ break;
+ }
+ }
+ if ( key == null ) key = property.getPersistentClass().getIdentifier();
+ mappedByColumns = key.getColumnIterator();
+ }
while ( mappedByColumns.hasNext() ) {
Column column = (Column) mappedByColumns.next();
columns[0].linkValueUsingAColumnCopy( column, value );
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/EntityBinder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/EntityBinder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/EntityBinder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -7,8 +7,9 @@
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
import javax.persistence.PrimaryKeyJoinColumn;
-import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.UniqueConstraint;
@@ -340,37 +341,62 @@
while ( joins.hasNext() ) {
Object uncastedColumn = joinColumns.next();
+ Join join = (Join) joins.next();
+ createPrimaryColumnsToSecondaryTable( uncastedColumn, propertyHolder, join );
+ }
+ mappings.addJoins( persistentClass, secondaryTables );
+ }
- Ejb3JoinColumn[] ejb3JoinColumns;
- PrimaryKeyJoinColumn[] columns = null;
- if ( uncastedColumn instanceof PrimaryKeyJoinColumn[] ) {
- columns = (PrimaryKeyJoinColumn[]) uncastedColumn;
- }
- if ( columns == null ) {
+ private void createPrimaryColumnsToSecondaryTable(Object uncastedColumn, PropertyHolder propertyHolder, Join join) {
+ Ejb3JoinColumn[] ejb3JoinColumns;
+ PrimaryKeyJoinColumn[] pkColumnsAnn = null;
+ JoinColumn[] joinColumnsAnn = null;
+ if ( uncastedColumn instanceof PrimaryKeyJoinColumn[] ) {
+ pkColumnsAnn = (PrimaryKeyJoinColumn[]) uncastedColumn;
+ }
+ if ( uncastedColumn instanceof JoinColumn[] ) {
+ joinColumnsAnn = (JoinColumn[]) uncastedColumn;
+ }
+ if ( pkColumnsAnn == null && joinColumnsAnn == null ) {
+ ejb3JoinColumns = new Ejb3JoinColumn[1];
+ ejb3JoinColumns[0] = Ejb3JoinColumn.buildJoinColumn(
+ null,
+ null,
+ persistentClass.getIdentifier(),
+ secondaryTables,
+ propertyHolder, mappings
+ );
+ }
+ else {
+ int nbrOfJoinColumns = pkColumnsAnn != null ? pkColumnsAnn.length : joinColumnsAnn.length;
+ if ( nbrOfJoinColumns == 0 ) {
ejb3JoinColumns = new Ejb3JoinColumn[1];
ejb3JoinColumns[0] = Ejb3JoinColumn.buildJoinColumn(
- (PrimaryKeyJoinColumn) uncastedColumn,
+ null,
+ null,
persistentClass.getIdentifier(),
secondaryTables,
propertyHolder, mappings
);
}
else {
- int nbrOfJoinColumns = columns.length;
- if ( nbrOfJoinColumns == 0 ) {
- ejb3JoinColumns = new Ejb3JoinColumn[1];
- ejb3JoinColumns[0] = Ejb3JoinColumn.buildJoinColumn(
- (PrimaryKeyJoinColumn) null,
- persistentClass.getIdentifier(),
- secondaryTables,
- propertyHolder, mappings
- );
+ ejb3JoinColumns = new Ejb3JoinColumn[nbrOfJoinColumns];
+ if ( pkColumnsAnn != null ) {
+ for ( int colIndex = 0; colIndex < nbrOfJoinColumns ; colIndex++ ) {
+ ejb3JoinColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
+ pkColumnsAnn[colIndex],
+ null,
+ persistentClass.getIdentifier(),
+ secondaryTables,
+ propertyHolder, mappings
+ );
+ }
}
else {
- ejb3JoinColumns = new Ejb3JoinColumn[nbrOfJoinColumns];
for ( int colIndex = 0; colIndex < nbrOfJoinColumns ; colIndex++ ) {
ejb3JoinColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
- columns[colIndex],
+ null,
+ joinColumnsAnn[colIndex],
persistentClass.getIdentifier(),
secondaryTables,
propertyHolder, mappings
@@ -378,14 +404,12 @@
}
}
}
+ }
- for ( Ejb3JoinColumn joinColumn : ejb3JoinColumns ) {
- joinColumn.forceNotNull();
- }
- Join join = (Join) joins.next();
- bindJoinToPersistentClass( join, ejb3JoinColumns, persistentClass, mappings );
+ for ( Ejb3JoinColumn joinColumn : ejb3JoinColumns ) {
+ joinColumn.forceNotNull();
}
- mappings.addJoins( persistentClass, secondaryTables );
+ bindJoinToPersistentClass( join, ejb3JoinColumns, persistentClass, mappings );
}
public static void bindJoinToPersistentClass(
@@ -404,51 +428,80 @@
}
public void firstLevelSecondaryTablesBinding(
- SecondaryTable secTable, SecondaryTables secTables, PrimaryKeyJoinColumn joinCol,
- PrimaryKeyJoinColumns joinCols
+ SecondaryTable secTable, SecondaryTables secTables
) {
if ( secTables != null ) {
//loop through it
for ( SecondaryTable tab : secTables.value() ) {
- bindFirstLevelSecondaryTable( tab, null, null );
+ addJoin( tab, null, null, false );
}
}
else {
- if ( secTable != null ) bindFirstLevelSecondaryTable( secTable, joinCol, joinCols );
+ if ( secTable != null ) addJoin( secTable, null, null, false );
}
}
- private void bindFirstLevelSecondaryTable(
- SecondaryTable tabAnn, PrimaryKeyJoinColumn joinColAnn, PrimaryKeyJoinColumns joinColsAnn
+ public Join addJoin(JoinTable joinTable, PropertyHolder holder, boolean noDelayInPkColumnCreation) {
+ Join join = addJoin( null, joinTable, holder, noDelayInPkColumnCreation );
+ join.setOptional( true );
+ return join;
+ }
+
+ /**
+ * A non null propertyHolder means than we process the Pk creation without delay
+ */
+ private Join addJoin(
+ SecondaryTable secondaryTable, JoinTable joinTable, PropertyHolder propertyHolder,
+ boolean noDelayInPkColumnCreation
) {
- List uniqueConstraints = new ArrayList();
- if ( tabAnn.uniqueConstraints().length != 0 ) {
- for ( UniqueConstraint uc : tabAnn.uniqueConstraints() ) {
+ Join join = new Join();
+ join.setPersistentClass( persistentClass );
+ String schema;
+ String catalog;
+ String table;
+ String realTable;
+ UniqueConstraint[] uniqueCosntraintsAnn;
+ if ( secondaryTable != null ) {
+ schema = secondaryTable.schema();
+ catalog = secondaryTable.catalog();
+ table = secondaryTable.name();
+ realTable = mappings.getNamingStrategy().tableName( table ); //always an explicit table name
+ uniqueCosntraintsAnn = secondaryTable.uniqueConstraints();
+ }
+ else if ( joinTable != null ) {
+ schema = joinTable.schema();
+ catalog = joinTable.catalog();
+ table = joinTable.name();
+ realTable = mappings.getNamingStrategy().tableName( table ); //always an explicit table name
+ uniqueCosntraintsAnn = joinTable.uniqueConstraints();
+ }
+ else {
+ throw new AssertionFailure( "Both JoinTable and SecondaryTable are null" );
+ }
+ List uniqueConstraints = new ArrayList( uniqueCosntraintsAnn == null ? 0 : uniqueCosntraintsAnn.length );
+ if ( uniqueCosntraintsAnn != null && uniqueCosntraintsAnn.length != 0 ) {
+ for ( UniqueConstraint uc : uniqueCosntraintsAnn ) {
uniqueConstraints.add( uc.columnNames() );
}
}
- addSecondaryTable(
- tabAnn.schema(),
- tabAnn.catalog(),
- tabAnn.name(),
- uniqueConstraints,
- joinColAnn,
- joinColsAnn == null ? tabAnn.pkJoinColumns() : joinColsAnn.value()
+ Table tableMapping = TableBinder.fillTable(
+ schema,
+ catalog,
+ realTable,
+ table, false, uniqueConstraints, null, null, mappings
);
- }
+ //no check constraints available on joins
+ join.setTable( tableMapping );
- private void addSecondaryTable(
- String schema, String catalog, String table, List uniqueConstraints, PrimaryKeyJoinColumn joinColAnn,
- PrimaryKeyJoinColumn[] joinColArray
- ) {
- Join join = buildJoin( schema, catalog, table, uniqueConstraints, persistentClass, mappings );
//somehow keep joins() for later.
//Has to do the work later because it needs persistentClass id!
- if ( joinColAnn != null ) {
- secondaryTableJoins.put( table, joinColAnn );
+ Object joinColumns = null;
+ //get the appropriate pk columns
+ if ( secondaryTable != null ) {
+ joinColumns = secondaryTable.pkJoinColumns();
}
- else {
- secondaryTableJoins.put( table, joinColArray );
+ else if ( joinTable != null ) {
+ joinColumns = joinTable.joinColumns();
}
if ( log.isInfoEnabled() ) {
log.info(
@@ -456,24 +509,14 @@
.getName()
);
}
- secondaryTables.put( table, join );
- }
+ if ( noDelayInPkColumnCreation ) {
+ createPrimaryColumnsToSecondaryTable( joinColumns, propertyHolder, join );
- private static Join buildJoin(
- String schema, String catalog, String table, List uniqueConstraints, PersistentClass persistentClass,
- ExtendedMappings mappings
- ) {
- Join join = new Join();
- join.setPersistentClass( persistentClass );
- String realTableName = mappings.getNamingStrategy().tableName( table ); //always an explicit table name
- Table tableMapping = TableBinder.fillTable(
- schema,
- catalog,
- realTableName,
- table, false, uniqueConstraints, null, null, mappings
- );
- //no check constraints available on joins
- join.setTable( tableMapping );
+ }
+ else {
+ secondaryTables.put( table, join );
+ secondaryTableJoins.put( table, joinColumns );
+ }
return join;
}
Modified: trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/TableBinder.java
===================================================================
--- trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/TableBinder.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/java/org/hibernate/cfg/annotations/TableBinder.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -128,7 +128,10 @@
mappings.addUniqueConstraints( table, uniqueConstraints );
}
if ( constraints != null ) table.addCheckConstraint( constraints );
- mappings.addTableBinding( schema, catalog, logicalName, realTableName, denormalizedSuperTable );
+ //logicalName is null if we are in the second pass
+ if ( logicalName != null ) {
+ mappings.addTableBinding( schema, catalog, logicalName, realTableName, denormalizedSuperTable );
+ }
return table;
}
Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/Death.java
===================================================================
--- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/Death.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/Death.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -8,17 +8,17 @@
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
+import javax.persistence.SecondaryTable;
import javax.persistence.PrimaryKeyJoinColumn;
-import javax.persistence.SecondaryTable;
/**
* @author Emmanuel Bernard
*/
@Entity
@SecondaryTable(
- name = "ExtendedDeath"
+ name = "ExtendedDeath",
+ pkJoinColumns = @PrimaryKeyJoinColumn(name = "DEATH_ID")
)
-@PrimaryKeyJoinColumn(name = "DEATH_ID")
public class Death implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Modified: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/JoinTest.java
===================================================================
--- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/JoinTest.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/join/JoinTest.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -9,6 +9,7 @@
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;
+import org.hibernate.mapping.Join;
import org.hibernate.test.annotations.TestCase;
/**
@@ -21,6 +22,11 @@
}
public void testDefaultValue() throws Exception {
+ Join join = (Join) getCfg().getClassMapping( Life.class.getName() ).getJoinClosureIterator().next();
+ assertEquals( "ExtendedLife", join.getTable().getName() );
+ org.hibernate.mapping.Column owner = new org.hibernate.mapping.Column();
+ owner.setName( "LIFE_ID" );
+ assertTrue( join.getTable().getPrimaryKey().containsColumn( owner ) );
Session s = openSession();
Transaction tx = s.beginTransaction();
Life life = new Life();
@@ -40,6 +46,11 @@
}
public void testCompositePK() throws Exception {
+ Join join = (Join) getCfg().getClassMapping( Dog.class.getName() ).getJoinClosureIterator().next();
+ assertEquals( "DogThoroughbred", join.getTable().getName() );
+ org.hibernate.mapping.Column owner = new org.hibernate.mapping.Column();
+ owner.setName( "OWNER_NAME" );
+ assertTrue( join.getTable().getPrimaryKey().containsColumn( owner ) );
Session s = openSession();
Transaction tx = s.beginTransaction();
Dog dog = new Dog();
Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/BiggestForest.java
===================================================================
--- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/BiggestForest.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/BiggestForest.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -0,0 +1,34 @@
+//$Id: $
+package org.hibernate.test.annotations.manytoone;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class BiggestForest {
+ private Integer id;
+ private ForestType type;
+
+ @Id @GeneratedValue
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @OneToOne(mappedBy = "biggestRepresentative")
+ public ForestType getType() {
+ return type;
+ }
+
+ public void setType(ForestType type) {
+ this.type = type;
+ }
+}
Added: trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/ForestType.java
===================================================================
--- trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/ForestType.java 2006-04-28 15:44:49 UTC (rev 9832)
+++ trunk/HibernateExt/metadata/src/test/org/hibernate/test/annotations/manytoone/ForestType.java 2006-04-30 04:08:48 UTC (rev 9833)
@@ -0,0 +1,61 @@
+//$Id: $
+package org.hibernate.test.annotations.manytoone;
+
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.JoinTable;
+import javax.persistence.JoinColumn;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class ForestType {
+ private Integer id;
+ private String name;
+ private Set<TreeType> trees;
+ private BiggestForest biggestRepresentative;
+
+ @OneToOne
+ @JoinTable(name="BiggestRepresentativePerForestType",
+ joinColumns = @JoinColumn(name="forest_type"),
+ inverseJoinColumns = @JoinColumn(name="forest")
+ )
+ public BiggestForest getBiggestRepresentative() {
+ return biggestRepresentative;
+ }
+
+ public void setBiggestRepresentative(BiggestForest biggestRepresentative) {
+ this.biggestRepresentative = biggestRepresentative;
+ }
+
+ @OneToMany(mappedBy="forestType")
+ public Set<TreeType> getTrees() {
+ return trees;
+ }
+
+ public void setTrees(Set<TreeType> trees) {
+ this.trees = trees;
+ }
+
+ @Id @GeneratedValue
+ public Integer ...
[truncated message content] |