HHH-6707 - One-to-One mapping with foreign key in target table and foreign key being the primary key fails with Oracle
This commit is contained in:
parent
c02de61f24
commit
e26b8be6a5
|
@ -2734,7 +2734,8 @@ public final class AnnotationBinder {
|
||||||
boolean cascadeOnDelete,
|
boolean cascadeOnDelete,
|
||||||
XClass targetEntity,
|
XClass targetEntity,
|
||||||
PropertyHolder propertyHolder,
|
PropertyHolder propertyHolder,
|
||||||
PropertyData inferredData, String mappedBy,
|
PropertyData inferredData,
|
||||||
|
String mappedBy,
|
||||||
boolean trueOneToOne,
|
boolean trueOneToOne,
|
||||||
boolean isIdentifierMapper,
|
boolean isIdentifierMapper,
|
||||||
boolean inSecondPass,
|
boolean inSecondPass,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.mapping;
|
package org.hibernate.mapping;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -35,7 +36,6 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.spi.Mapping;
|
import org.hibernate.engine.spi.Mapping;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
|
||||||
import org.hibernate.tool.hbm2ddl.ColumnMetadata;
|
import org.hibernate.tool.hbm2ddl.ColumnMetadata;
|
||||||
import org.hibernate.tool.hbm2ddl.TableMetadata;
|
import org.hibernate.tool.hbm2ddl.TableMetadata;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public class Table implements RelationalModel, Serializable {
|
||||||
private PrimaryKey primaryKey;
|
private PrimaryKey primaryKey;
|
||||||
private Map indexes = new HashMap();
|
private Map indexes = new HashMap();
|
||||||
private Map foreignKeys = new HashMap();
|
private Map foreignKeys = new HashMap();
|
||||||
private Map uniqueKeys = new HashMap();
|
private Map<String,UniqueKey> uniqueKeys = new HashMap<String,UniqueKey>();
|
||||||
private final int uniqueInteger;
|
private final int uniqueInteger;
|
||||||
private boolean quoted;
|
private boolean quoted;
|
||||||
private boolean schemaQuoted;
|
private boolean schemaQuoted;
|
||||||
|
@ -248,34 +248,83 @@ public class Table implements RelationalModel, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map getUniqueKeys() {
|
Map getUniqueKeys() {
|
||||||
if ( uniqueKeys.size() > 1 ) {
|
cleanseUniqueKeyMapIfNeeded();
|
||||||
//deduplicate unique constraints sharing the same columns
|
return uniqueKeys;
|
||||||
//this is needed by Hibernate Annotations since it creates automagically
|
}
|
||||||
// unique constraints for the user
|
|
||||||
Iterator it = uniqueKeys.entrySet().iterator();
|
private int sizeOfUniqueKeyMapOnLastCleanse = 0;
|
||||||
Map finalUniqueKeys = new HashMap( uniqueKeys.size() );
|
|
||||||
while ( it.hasNext() ) {
|
private void cleanseUniqueKeyMapIfNeeded() {
|
||||||
Map.Entry entry = (Map.Entry) it.next();
|
if ( uniqueKeys.size() == sizeOfUniqueKeyMapOnLastCleanse ) {
|
||||||
UniqueKey uk = (UniqueKey) entry.getValue();
|
// nothing to do
|
||||||
List columns = uk.getColumns();
|
return;
|
||||||
int size = finalUniqueKeys.size();
|
}
|
||||||
boolean skip = false;
|
cleanseUniqueKeyMap();
|
||||||
Iterator tempUks = finalUniqueKeys.entrySet().iterator();
|
sizeOfUniqueKeyMapOnLastCleanse = uniqueKeys.size();
|
||||||
while ( tempUks.hasNext() ) {
|
}
|
||||||
final UniqueKey currentUk = (UniqueKey) ( (Map.Entry) tempUks.next() ).getValue();
|
|
||||||
if ( currentUk.getColumns().containsAll( columns ) && columns
|
private void cleanseUniqueKeyMap() {
|
||||||
.containsAll( currentUk.getColumns() ) ) {
|
// We need to account for a few conditions here...
|
||||||
skip = true;
|
// 1) If there are multiple unique keys contained in the uniqueKeys Map, we need to deduplicate
|
||||||
|
// any sharing the same columns as other defined unique keys; this is needed for the annotation
|
||||||
|
// processor since it creates unique constraints automagically for the user
|
||||||
|
// 2) Remove any unique keys that share the same columns as the primary key; again, this is
|
||||||
|
// needed for the annotation processor to handle @Id @OneToOne cases. In such cases the
|
||||||
|
// unique key is unnecessary because a primary key is already unique by definition. We handle
|
||||||
|
// this case specifically because some databases fail if you try to apply a unique key to
|
||||||
|
// the primary key columns which causes schema export to fail in these cases.
|
||||||
|
if ( uniqueKeys.isEmpty() ) {
|
||||||
|
// nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ( uniqueKeys.size() == 1 ) {
|
||||||
|
// we have to worry about condition 2 above, but not condition 1
|
||||||
|
final Map.Entry<String,UniqueKey> uniqueKeyEntry = uniqueKeys.entrySet().iterator().next();
|
||||||
|
if ( isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
|
||||||
|
uniqueKeys.remove( uniqueKeyEntry.getKey() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we have to check both conditions 1 and 2
|
||||||
|
final Iterator<Map.Entry<String,UniqueKey>> uniqueKeyEntries = uniqueKeys.entrySet().iterator();
|
||||||
|
while ( uniqueKeyEntries.hasNext() ) {
|
||||||
|
final Map.Entry<String,UniqueKey> uniqueKeyEntry = uniqueKeyEntries.next();
|
||||||
|
final UniqueKey uniqueKey = uniqueKeyEntry.getValue();
|
||||||
|
boolean removeIt = false;
|
||||||
|
|
||||||
|
// condition 1 : check against other unique keys
|
||||||
|
for ( UniqueKey otherUniqueKey : uniqueKeys.values() ) {
|
||||||
|
// make sure its not the same unique key
|
||||||
|
if ( uniqueKeyEntry.getValue() == otherUniqueKey ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( otherUniqueKey.getColumns().containsAll( uniqueKey.getColumns() )
|
||||||
|
&& uniqueKey.getColumns().containsAll( otherUniqueKey.getColumns() ) ) {
|
||||||
|
removeIt = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( !skip ) finalUniqueKeys.put( entry.getKey(), uk );
|
|
||||||
|
// condition 2 : check against pk
|
||||||
|
if ( isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
|
||||||
|
removeIt = true;
|
||||||
}
|
}
|
||||||
return finalUniqueKeys;
|
|
||||||
|
if ( removeIt ) {
|
||||||
|
uniqueKeys.remove( uniqueKeyEntry.getKey() );
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return uniqueKeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSameAsPrimaryKeyColumns(UniqueKey uniqueKey) {
|
||||||
|
if ( primaryKey == null || ! primaryKey.columnIterator().hasNext() ) {
|
||||||
|
// happens for many-to-many tables
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return primaryKey.getColumns().containsAll( uniqueKey.getColumns() )
|
||||||
|
&& uniqueKey.getColumns().containsAll( primaryKey.getColumns() );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateColumns(Dialect dialect, Mapping mapping, TableMetadata tableInfo) {
|
public void validateColumns(Dialect dialect, Mapping mapping, TableMetadata tableInfo) {
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.onetoone.basic;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Florian Rampp
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
@Table( name = "CHILD")
|
||||||
|
public class Child {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
// A @OneToOne here results in the following DDL: create table child ([...] primary key
|
||||||
|
// (parent), unique (parent)).
|
||||||
|
// Oracle does not like a unique constraint and a PK on the same column (results in ORA-02261)
|
||||||
|
@OneToOne(optional = false)
|
||||||
|
private Parent parent;
|
||||||
|
|
||||||
|
public void setParent(Parent parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
<hibernate-mapping package="org.hibernate.test.onetoone.basic" default-access="field">
|
||||||
|
|
||||||
|
<class name="Parent">
|
||||||
|
<id name="id"/>
|
||||||
|
<one-to-one name="child" cascade="all" constrained="false" outer-join="false" lazy="proxy"/>
|
||||||
|
</class>
|
||||||
|
|
||||||
|
<class name="Child">
|
||||||
|
<id name=""
|
||||||
|
</class>
|
||||||
|
<class name="Address">
|
||||||
|
<id name="entityName"/>
|
||||||
|
<property name="street"/>
|
||||||
|
<property name="state"/>
|
||||||
|
<property name="zip"/>
|
||||||
|
</class>
|
||||||
|
|
||||||
|
</hibernate-mapping>
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.onetoone.basic;
|
||||||
|
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.cfg.Environment;
|
||||||
|
import org.hibernate.mapping.Table;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class OneToOneSchemaTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueKeyNotGeneratedViaAnnotations() throws Exception {
|
||||||
|
Configuration cfg = new Configuration()
|
||||||
|
.addAnnotatedClass( Parent.class )
|
||||||
|
.addAnnotatedClass( Child.class )
|
||||||
|
.setProperty( Environment.HBM2DDL_AUTO, "create" );
|
||||||
|
|
||||||
|
probeForUniqueKey( cfg );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void probeForUniqueKey(Configuration cfg) {
|
||||||
|
cfg.buildMappings();
|
||||||
|
|
||||||
|
Table childTable = cfg.createMappings().getTable( null, null, "CHILD" );
|
||||||
|
assertFalse( "UniqueKey was generated when it should not", childTable.getUniqueKeyIterator().hasNext() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.onetoone.basic;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Florian Rampp
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class Parent {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
Long id;
|
||||||
|
|
||||||
|
@OneToOne(cascade = CascadeType.ALL, mappedBy = "parent")
|
||||||
|
Child child;
|
||||||
|
|
||||||
|
void setChild(Child child) {
|
||||||
|
this.child = child;
|
||||||
|
child.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue