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,
|
||||
XClass targetEntity,
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData, String mappedBy,
|
||||
PropertyData inferredData,
|
||||
String mappedBy,
|
||||
boolean trueOneToOne,
|
||||
boolean isIdentifierMapper,
|
||||
boolean inSecondPass,
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.mapping;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -35,7 +36,6 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.tool.hbm2ddl.ColumnMetadata;
|
||||
import org.hibernate.tool.hbm2ddl.TableMetadata;
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class Table implements RelationalModel, Serializable {
|
|||
private PrimaryKey primaryKey;
|
||||
private Map indexes = 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 boolean quoted;
|
||||
private boolean schemaQuoted;
|
||||
|
@ -248,34 +248,83 @@ public class Table implements RelationalModel, Serializable {
|
|||
}
|
||||
|
||||
Map getUniqueKeys() {
|
||||
if ( uniqueKeys.size() > 1 ) {
|
||||
//deduplicate unique constraints sharing the same columns
|
||||
//this is needed by Hibernate Annotations since it creates automagically
|
||||
// unique constraints for the user
|
||||
Iterator it = uniqueKeys.entrySet().iterator();
|
||||
Map finalUniqueKeys = new HashMap( uniqueKeys.size() );
|
||||
while ( it.hasNext() ) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
UniqueKey uk = (UniqueKey) entry.getValue();
|
||||
List columns = uk.getColumns();
|
||||
int size = finalUniqueKeys.size();
|
||||
boolean skip = false;
|
||||
Iterator tempUks = finalUniqueKeys.entrySet().iterator();
|
||||
while ( tempUks.hasNext() ) {
|
||||
final UniqueKey currentUk = (UniqueKey) ( (Map.Entry) tempUks.next() ).getValue();
|
||||
if ( currentUk.getColumns().containsAll( columns ) && columns
|
||||
.containsAll( currentUk.getColumns() ) ) {
|
||||
skip = true;
|
||||
cleanseUniqueKeyMapIfNeeded();
|
||||
return uniqueKeys;
|
||||
}
|
||||
|
||||
private int sizeOfUniqueKeyMapOnLastCleanse = 0;
|
||||
|
||||
private void cleanseUniqueKeyMapIfNeeded() {
|
||||
if ( uniqueKeys.size() == sizeOfUniqueKeyMapOnLastCleanse ) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
cleanseUniqueKeyMap();
|
||||
sizeOfUniqueKeyMapOnLastCleanse = uniqueKeys.size();
|
||||
}
|
||||
|
||||
private void cleanseUniqueKeyMap() {
|
||||
// We need to account for a few conditions here...
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if ( !skip ) finalUniqueKeys.put( entry.getKey(), uk );
|
||||
|
||||
// condition 2 : check against pk
|
||||
if ( isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
|
||||
removeIt = true;
|
||||
}
|
||||
|
||||
if ( removeIt ) {
|
||||
uniqueKeys.remove( uniqueKeyEntry.getKey() );
|
||||
}
|
||||
}
|
||||
return finalUniqueKeys;
|
||||
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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