HHH-4529 support @ManyToOne and fix FK error
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18589 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
e655b54eb8
commit
fd389b913e
|
@ -1379,12 +1379,13 @@ public final class AnnotationBinder {
|
|||
joinColumn.setSecondaryTableName( join.getTable().getName() );
|
||||
}
|
||||
}
|
||||
final boolean mandatory = !ann.optional() || property.isAnnotationPresent( MapsId.class );
|
||||
bindManyToOne(
|
||||
getCascadeStrategy( ann.cascade(), hibernateCascade, false),
|
||||
joinColumns,
|
||||
ann.optional(),
|
||||
!mandatory,
|
||||
ignoreNotFound, onDeleteCascade,
|
||||
mappings.getReflectionManager().toXClass( ann.targetEntity() ),
|
||||
ToOneBinder.getTargetEntity( inferredData, mappings ),
|
||||
propertyHolder,
|
||||
inferredData, false, isIdentifierMapper, inSecondPass, mappings
|
||||
);
|
||||
|
@ -1422,7 +1423,7 @@ public final class AnnotationBinder {
|
|||
!mandatory,
|
||||
getFetchMode( ann.fetch() ),
|
||||
ignoreNotFound, onDeleteCascade,
|
||||
mappings.getReflectionManager().toXClass( ann.targetEntity() ),
|
||||
ToOneBinder.getTargetEntity( inferredData, mappings ),
|
||||
propertyHolder,
|
||||
inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, inSecondPass, mappings
|
||||
);
|
||||
|
@ -2117,11 +2118,14 @@ public final class AnnotationBinder {
|
|||
catch ( ClassNotFoundException e ) {
|
||||
throw new AssertionFailure( "Persistence class name cannot be converted into a Class", e);
|
||||
}
|
||||
|
||||
final PropertyData annotatedWithMapsId = mappings.getPropertyAnnotatedWithMapsId( persistentXClass, "" );
|
||||
if ( annotatedWithMapsId != null ) {
|
||||
columns = buildExplicitJoinColumns( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
|
||||
if (columns == null) {
|
||||
columns = buildDefaultJoinColumnsForXToOne( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
|
||||
throw new UnsupportedOperationException( "Implicit @JoinColumn is not supported on @MapsId properties: "
|
||||
+ annotatedWithMapsId.getDeclaringClass() + " " + annotatedWithMapsId.getPropertyName() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2194,13 +2198,9 @@ public final class AnnotationBinder {
|
|||
if ( unique ) {
|
||||
value.markAsLogicalOneToOne();
|
||||
}
|
||||
if ( isDefault( targetEntity, mappings ) ) {
|
||||
value.setReferencedEntityName( inferredData.getClassOrElementName() );
|
||||
}
|
||||
else {
|
||||
value.setReferencedEntityName( targetEntity.getName() );
|
||||
}
|
||||
defineFetchingStrategy( value, inferredData.getProperty() );
|
||||
value.setReferencedEntityName( ToOneBinder.getReferenceEntityName(inferredData, targetEntity, mappings) );
|
||||
final XProperty property = inferredData.getProperty();
|
||||
defineFetchingStrategy( value, property );
|
||||
//value.setFetchMode( fetchMode );
|
||||
value.setIgnoreNotFound( ignoreNotFound );
|
||||
value.setCascadeDeleteEnabled( cascadeOnDelete );
|
||||
|
@ -2210,11 +2210,18 @@ public final class AnnotationBinder {
|
|||
column.setNullable( false );
|
||||
}
|
||||
}
|
||||
if ( property.isAnnotationPresent( MapsId.class ) ) {
|
||||
//read only
|
||||
for (Ejb3JoinColumn column : columns) {
|
||||
column.setInsertable( false );
|
||||
column.setUpdatable( false );
|
||||
}
|
||||
}
|
||||
value.setTypeName( inferredData.getClassOrElementName() );
|
||||
final String propertyName = inferredData.getPropertyName();
|
||||
value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName );
|
||||
|
||||
ForeignKey fk = inferredData.getProperty().getAnnotation( ForeignKey.class );
|
||||
ForeignKey fk = property.getAnnotation( ForeignKey.class );
|
||||
String fkName = fk != null ?
|
||||
fk.name() :
|
||||
"";
|
||||
|
@ -2250,7 +2257,7 @@ public final class AnnotationBinder {
|
|||
}
|
||||
binder.setAccessType( inferredData.getDefaultAccess() );
|
||||
binder.setCascade( cascadeStrategy );
|
||||
binder.setProperty(inferredData.getProperty());
|
||||
binder.setProperty( property );
|
||||
Property prop = binder.make();
|
||||
//composite FK columns are in the same table so its OK
|
||||
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
|
||||
|
|
|
@ -374,7 +374,7 @@ public class AnnotationConfiguration extends Configuration {
|
|||
iter = secondPasses.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
SecondPass sp = ( SecondPass ) iter.next();
|
||||
//do the SecondaryTable second pass before any association becasue associations can be built on joins
|
||||
//do the SecondaryTable second pass before any association because associations can be built on joins
|
||||
if ( sp instanceof SecondaryTableSecondPass ) {
|
||||
sp.doSecondPass( classes );
|
||||
iter.remove();
|
||||
|
|
|
@ -393,6 +393,31 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
String logicalReferencedColumn = getMappings().getLogicalColumnName(
|
||||
referencedColumn.getQuotedName(), referencedEntity.getTable()
|
||||
);
|
||||
columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
|
||||
//yuk side effect on an implicit column
|
||||
setLogicalColumnName( columnName );
|
||||
setReferencedColumn( logicalReferencedColumn );
|
||||
initMappingColumn(
|
||||
columnName,
|
||||
null, referencedColumn.getLength(),
|
||||
referencedColumn.getPrecision(),
|
||||
referencedColumn.getScale(),
|
||||
getMappingColumn() != null ? getMappingColumn().isNullable() : false,
|
||||
referencedColumn.getSqlType(),
|
||||
getMappingColumn() != null ? getMappingColumn().isUnique() : false,
|
||||
false
|
||||
);
|
||||
linkWithValue( value );
|
||||
}
|
||||
|
||||
public void addDefaultJoinColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
|
||||
final String columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
|
||||
getMappingColumn().setName( columnName );
|
||||
setLogicalColumnName( columnName );
|
||||
}
|
||||
|
||||
private String buildDefaultColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
|
||||
String columnName;
|
||||
boolean mappedBySide = mappedByTableName != null || mappedByPropertyName != null;
|
||||
boolean ownerSide = getPropertyName() != null;
|
||||
|
||||
|
@ -443,20 +468,7 @@ public class Ejb3JoinColumn extends Ejb3Column {
|
|||
columnName = StringHelper.quote( columnName );
|
||||
}
|
||||
}
|
||||
//yuk side effect on an implicit column
|
||||
setLogicalColumnName( columnName );
|
||||
setReferencedColumn( logicalReferencedColumn );
|
||||
initMappingColumn(
|
||||
columnName,
|
||||
null, referencedColumn.getLength(),
|
||||
referencedColumn.getPrecision(),
|
||||
referencedColumn.getScale(),
|
||||
getMappingColumn() != null ? getMappingColumn().isNullable() : false,
|
||||
referencedColumn.getSqlType(),
|
||||
getMappingColumn() != null ? getMappingColumn().isUnique() : false,
|
||||
false
|
||||
);
|
||||
linkWithValue( value );
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -88,13 +88,7 @@ public class OneToOneSecondPass implements SecondPass {
|
|||
);
|
||||
final String propertyName = inferredData.getPropertyName();
|
||||
value.setPropertyName( propertyName );
|
||||
String referencedEntityName;
|
||||
if ( AnnotationBinder.isDefault( targetEntity, mappings ) ) {
|
||||
referencedEntityName = inferredData.getClassOrElementName();
|
||||
}
|
||||
else {
|
||||
referencedEntityName = targetEntity.getName();
|
||||
}
|
||||
String referencedEntityName = ToOneBinder.getReferenceEntityName(inferredData, targetEntity, mappings);
|
||||
value.setReferencedEntityName( referencedEntityName );
|
||||
AnnotationBinder.defineFetchingStrategy( value, inferredData.getProperty() );
|
||||
//value.setFetchMode( fetchMode );
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package org.hibernate.cfg;
|
||||
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
|
||||
/**
|
||||
* Work in progress
|
||||
* The goal of this class is to aggregate all operations
|
||||
* related to ToOne binding operations
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class ToOneBinder {
|
||||
public static String getReferenceEntityName(PropertyData propertyData, XClass targetEntity, ExtendedMappings mappings) {
|
||||
if ( AnnotationBinder.isDefault( targetEntity, mappings ) ) {
|
||||
return propertyData.getClassOrElementName();
|
||||
}
|
||||
else {
|
||||
return targetEntity.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getReferenceEntityName(PropertyData propertyData, ExtendedMappings mappings) {
|
||||
XClass targetEntity = getTargetEntity( propertyData, mappings );
|
||||
if ( AnnotationBinder.isDefault( targetEntity, mappings ) ) {
|
||||
return propertyData.getClassOrElementName();
|
||||
}
|
||||
else {
|
||||
return targetEntity.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static XClass getTargetEntity(PropertyData propertyData, ExtendedMappings mappings) {
|
||||
XProperty property = propertyData.getProperty();
|
||||
return mappings.getReflectionManager().toXClass( getTargetEntityClass( property ) );
|
||||
}
|
||||
|
||||
private static Class<?> getTargetEntityClass(XProperty property) {
|
||||
final ManyToOne mTo = property.getAnnotation( ManyToOne.class );
|
||||
if (mTo != null) {
|
||||
return mTo.targetEntity();
|
||||
}
|
||||
final OneToOne oTo = property.getAnnotation( OneToOne.class );
|
||||
if (oTo != null) {
|
||||
return oTo.targetEntity();
|
||||
}
|
||||
throw new AssertionFailure("Unexpected discovery of a targetEntity: " + property.getName() );
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.io.Serializable;
|
|||
import java.sql.Types;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Lob;
|
||||
|
@ -46,6 +47,7 @@ import org.hibernate.cfg.BinderHelper;
|
|||
import org.hibernate.cfg.Ejb3Column;
|
||||
import org.hibernate.cfg.ExtendedMappings;
|
||||
import org.hibernate.cfg.NotYetImplementedException;
|
||||
import org.hibernate.cfg.SecondPass;
|
||||
import org.hibernate.cfg.SetSimpleValueTypeSecondPass;
|
||||
import org.hibernate.mapping.SimpleValue;
|
||||
import org.hibernate.mapping.Table;
|
||||
|
@ -289,14 +291,12 @@ public class SimpleValueBinder {
|
|||
}
|
||||
simpleValue = new SimpleValue( table );
|
||||
|
||||
for (Ejb3Column column : columns) {
|
||||
column.linkWithValue( simpleValue );
|
||||
}
|
||||
linkWithValue();
|
||||
|
||||
boolean isInSecondPass = mappings.isInSecondPass();
|
||||
SetSimpleValueTypeSecondPass secondPass = new SetSimpleValueTypeSecondPass(this);
|
||||
if (!isInSecondPass) {
|
||||
//Defer this to the second pass
|
||||
SetSimpleValueTypeSecondPass secondPass = new SetSimpleValueTypeSecondPass(this);
|
||||
mappings.addSecondPass(secondPass);
|
||||
}
|
||||
else {
|
||||
|
@ -306,6 +306,12 @@ public class SimpleValueBinder {
|
|||
return simpleValue;
|
||||
}
|
||||
|
||||
public void linkWithValue() {
|
||||
for ( Ejb3Column column : columns) {
|
||||
column.linkWithValue( simpleValue );
|
||||
}
|
||||
}
|
||||
|
||||
public void fillSimpleValue() {
|
||||
|
||||
log.debug( "setting SimpleValue typeName for {}", propertyName );
|
||||
|
|
|
@ -314,7 +314,7 @@ public class TableBinder {
|
|||
) {
|
||||
PersistentClass associatedClass;
|
||||
if ( destinationEntity != null ) {
|
||||
//overidden destination
|
||||
//overridden destination
|
||||
associatedClass = destinationEntity;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e1.b;
|
||||
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
public class Dependent {
|
||||
@EmbeddedId
|
||||
DependentId id;
|
||||
|
||||
// id attribute mapped by join column default
|
||||
@MapsId("empPK") // maps empPK attribute of embedded id
|
||||
@ManyToOne
|
||||
Employee emp;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e1.b;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Embeddable
|
||||
public class DependentId implements Serializable {
|
||||
String name;
|
||||
long empPK; // corresponds to PK type of Employee
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e1.b;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.test.annotations.TestCase;
|
||||
import org.hibernate.test.util.SchemaUtil;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
|
||||
|
||||
// public void testIt() throws Exception {
|
||||
// assertTrue( SchemaUtil.isColumnPresent( "Dependent", "empPK", getCfg() ) );
|
||||
// assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "emp_empId", getCfg() ) );
|
||||
// Employee e = new Employee();
|
||||
// e.empId = 1;
|
||||
// e.empName = "Emmanuel";
|
||||
// Session s = openSession( );
|
||||
// s.getTransaction().begin();
|
||||
// s.persist( e );
|
||||
// Dependent d = new Dependent();
|
||||
// d.emp = e;
|
||||
// d.id = new DependentId();
|
||||
// d.id.name = "Doggy";
|
||||
// d.id.empPK = e.empId; //FIXME not needed when foreign is enabled
|
||||
// s.persist( d );
|
||||
// s.flush();
|
||||
// s.clear();
|
||||
// d = (Dependent) s.get( Dependent.class, d.id );
|
||||
// assertEquals( d.id.empPK, d.emp.empId );
|
||||
// s.getTransaction().rollback();
|
||||
// s.close();
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getMappings() {
|
||||
return new Class<?>[] {
|
||||
Dependent.class,
|
||||
Employee.class
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e1.b;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
public class Employee {
|
||||
@Id
|
||||
long empId;
|
||||
String empName;
|
||||
}
|
|
@ -11,7 +11,7 @@ import org.hibernate.test.util.SchemaUtil;
|
|||
*/
|
||||
public class DerivedIdentitySimpleParentSimpleDepTest extends TestCase {
|
||||
|
||||
public void testIt() throws Exception {
|
||||
public void testOneToOneExplicitJoinColumn() throws Exception {
|
||||
assertTrue( SchemaUtil.isColumnPresent( "MedicalHistory", "FK", getCfg() ) );
|
||||
assertTrue( ! SchemaUtil.isColumnPresent( "MedicalHistory", "id", getCfg() ) );
|
||||
Person e = new Person();
|
||||
|
@ -36,11 +36,37 @@ public class DerivedIdentitySimpleParentSimpleDepTest extends TestCase {
|
|||
s.close();
|
||||
}
|
||||
|
||||
public void testManyToOneExplicitJoinColumn() throws Exception {
|
||||
assertTrue( SchemaUtil.isColumnPresent( "FinancialHistory", "FK", getCfg() ) );
|
||||
assertTrue( ! SchemaUtil.isColumnPresent( "FinancialHistory", "id", getCfg() ) );
|
||||
Person e = new Person();
|
||||
e.ssn = "aaa";
|
||||
Session s = openSession( );
|
||||
s.getTransaction().begin();
|
||||
s.persist( e );
|
||||
FinancialHistory d = new FinancialHistory();
|
||||
d.patient = e;
|
||||
d.id = "aaa"; //FIXME not needed when foreign is enabled
|
||||
s.persist( d );
|
||||
s.flush();
|
||||
s.clear();
|
||||
d = (FinancialHistory) s.get( FinancialHistory.class, d.id );
|
||||
assertEquals( d.id, d.patient.ssn );
|
||||
d.lastupdate = new Date();
|
||||
s.flush();
|
||||
s.clear();
|
||||
d = (FinancialHistory) s.get( FinancialHistory.class, d.id );
|
||||
assertNotNull( d.lastupdate );
|
||||
s.getTransaction().rollback();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getMappings() {
|
||||
return new Class<?>[] {
|
||||
MedicalHistory.class,
|
||||
Person.class
|
||||
Person.class,
|
||||
FinancialHistory.class
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.hibernate.test.annotations.derivedidentities.e4.a;
|
||||
|
||||
import java.util.Date;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
public class FinancialHistory {
|
||||
@Id
|
||||
String id; // overriding not allowed ... // default join column name is overridden @MapsId
|
||||
@Temporal(TemporalType.DATE)
|
||||
Date lastupdate;
|
||||
|
||||
@JoinColumn(name = "FK")
|
||||
@MapsId
|
||||
@ManyToOne
|
||||
Person patient;
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import java.util.Date;
|
|||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Temporal;
|
||||
|
|
Loading…
Reference in New Issue