/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.constraint;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.CollectionTable;
import javax.persistence.ConstraintMode;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.MapKeyJoinColumns;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SecondaryTable;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.mapping.Column;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Christian Beikov
*/
@TestForIssue( jiraKey = "HHH-11180" )
public class ForeignKeyConstraintTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
CreditCard.class,
Person.class,
Student.class,
Professor.class,
Vehicle.class,
VehicleBuyInfo.class,
Car.class,
Truck.class,
Company.class
};
}
@Test
public void testJoinColumn() {
assertForeignKey( "FK_CAR_OWNER", "OWNER_PERSON_ID" );
assertForeignKey( "FK_CAR_OWNER3", "OWNER_PERSON_ID3" );
assertForeignKey( "FK_PERSON_CC", "PERSON_CC_ID" );
assertNoForeignKey( "FK_CAR_OWNER2", "OWNER_PERSON_ID2" );
assertNoForeignKey( "FK_CAR_OWNER4", "OWNER_PERSON_ID4" );
assertNoForeignKey( "FK_PERSON_CC2", "PERSON_CC_ID2" );
}
@Test
public void testJoinColumns() {
assertForeignKey( "FK_STUDENT_CAR", "CAR_NR", "CAR_VENDOR_NR" );
assertForeignKey( "FK_STUDENT_CAR3", "CAR_NR3", "CAR_VENDOR_NR3" );
assertNoForeignKey( "FK_STUDENT_CAR2", "CAR_NR2", "CAR_VENDOR_NR2" );
assertNoForeignKey( "FK_STUDENT_CAR4", "CAR_NR4", "CAR_VENDOR_NR4" );
}
@Test
public void testJoinTable() {
assertForeignKey( "FK_VEHICLE_BUY_INFOS_STUDENT", "STUDENT_ID" );
}
@Test
public void testJoinTableInverse() {
assertForeignKey( "FK_VEHICLE_BUY_INFOS_VEHICLE_BUY_INFO", "VEHICLE_BUY_INFO_ID" );
}
@Test
public void testPrimaryKeyJoinColumn() {
assertForeignKey( "FK_STUDENT_PERSON", "PERSON_ID" );
assertNoForeignKey( "FK_PROFESSOR_PERSON", "PERSON_ID" );
}
@Test
public void testPrimaryKeyJoinColumns() {
assertForeignKey( "FK_CAR_VEHICLE", "CAR_NR", "VENDOR_NR" );
assertNoForeignKey( "FK_TRUCK_VEHICLE", "CAR_NR", "VENDOR_NR" );
}
@Test
public void testCollectionTable() {
assertForeignKey( "FK_OWNER_INFO_CAR", "CAR_NR", "VENDOR_NR" );
}
@Test
public void testMapKeyJoinColumn() {
assertForeignKey( "FK_OWNER_INFO_PERSON", "PERSON_ID" );
}
@Test
public void testMapKeyJoinColumns() {
assertForeignKey( "FK_VEHICLE_BUY_INFOS_VEHICLE", "VEHICLE_NR", "VEHICLE_VENDOR_NR" );
}
@Test
public void testAssociationOverride() {
// class level association overrides
assertForeignKey( "FK_COMPANY_OWNER", "OWNER_PERSON_ID" );
assertForeignKey( "FK_COMPANY_CREDIT_CARD", "CREDIT_CARD_ID" );
assertForeignKey( "FK_COMPANY_CREDIT_CARD3", "CREDIT_CARD_ID3" );
assertNoForeignKey( "FK_COMPANY_OWNER2", "OWNER_PERSON_ID2" );
assertNoForeignKey( "FK_COMPANY_CREDIT_CARD2", "CREDIT_CARD_ID2" );
assertNoForeignKey( "FK_COMPANY_CREDIT_CARD4", "CREDIT_CARD_ID4" );
// embeddable association overrides
assertForeignKey( "FK_COMPANY_CARD", "AO_CI_CC_ID" );
assertNoForeignKey( "FK_COMPANY_CARD2", "AO_CI_CC_ID2" );
assertForeignKey( "FK_COMPANY_CARD3", "AO_CI_CC_ID3" );
assertNoForeignKey( "FK_COMPANY_CARD4", "AO_CI_CC_ID4" );
}
@Test
public void testSecondaryTable() {
assertForeignKey( "FK_CAR_DETAILS_CAR", "CAR_NR", "CAR_VENDOR_NR" );
}
private void assertForeignKey(String foreignKeyName, String... columns) {
Set<String> columnSet = new LinkedHashSet<>( Arrays.asList( columns ) );
for ( Namespace namespace : metadata().getDatabase().getNamespaces() ) {
for ( org.hibernate.mapping.Table table : namespace.getTables() ) {
Iterator<org.hibernate.mapping.ForeignKey> fkItr = table.getForeignKeyIterator();
while ( fkItr.hasNext() ) {
org.hibernate.mapping.ForeignKey fk = fkItr.next();
if ( foreignKeyName.equals( fk.getName() ) ) {
assertEquals( "ForeignKey column count not like expected", columnSet.size(), fk.getColumnSpan() );
List<String> columnNames = fk.getColumns().stream().map(Column::getName).collect(Collectors.toList());
assertTrue(
"ForeignKey columns [" + columnNames + "] do not match expected columns [" + columnSet + "]",
columnSet.containsAll( columnNames )
);
return;
}
}
}
}
fail( "ForeignKey '" + foreignKeyName + "' could not be found!" );
}
private void assertNoForeignKey(String foreignKeyName, String... columns) {
Set<String> columnSet = new LinkedHashSet<>( Arrays.asList( columns ) );
for ( Namespace namespace : metadata().getDatabase().getNamespaces() ) {
for ( org.hibernate.mapping.Table table : namespace.getTables() ) {
Iterator<org.hibernate.mapping.ForeignKey> fkItr = table.getForeignKeyIterator();
while ( fkItr.hasNext() ) {
org.hibernate.mapping.ForeignKey fk = fkItr.next();
assertFalse(
"ForeignKey [" + foreignKeyName + "] defined and shouldn't have been.",
foreignKeyName.equals( fk.getName() )
);
}
}
}
}
@Entity(name = "CreditCard")
public static class CreditCard {
@Id
public String number;
}
@Entity(name = "Person")
@Inheritance( strategy = InheritanceType.JOINED )
public static class Person {
@Id
@GeneratedValue
@javax.persistence.Column( nullable = false, unique = true)
public long id;
@OneToMany
@JoinColumn(name = "PERSON_CC_ID", foreignKey = @ForeignKey( name = "FK_PERSON_CC" ) )
public List<CreditCard> creditCards;
@OneToMany
@JoinColumn(name = "PERSON_CC_ID2", foreignKey = @ForeignKey( name = "FK_PERSON_CC2", value = ConstraintMode.NO_CONSTRAINT ) )
public List<CreditCard> creditCards2;
}
@Entity(name = "Professor")
@PrimaryKeyJoinColumn(
name = "PERSON_ID",
foreignKey = @ForeignKey( name = "FK_PROFESSOR_PERSON", value = ConstraintMode.NO_CONSTRAINT )
)
public static class Professor extends Person {
}
@Entity(name = "Student")
@PrimaryKeyJoinColumn( name = "PERSON_ID", foreignKey = @ForeignKey( name = "FK_STUDENT_PERSON" ) )
public static class Student extends Person {
@javax.persistence.Column( name = "MATRICULATION_NUMBER" )
public String matriculationNumber;
@ManyToOne
@JoinColumns(
value = {
@JoinColumn( name = "CAR_NR", referencedColumnName = "CAR_NR" ),
@JoinColumn( name = "CAR_VENDOR_NR", referencedColumnName = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_STUDENT_CAR" )
)
public Car car;
@ManyToOne
@JoinColumns(
value = {
@JoinColumn( name = "CAR_NR2", referencedColumnName = "CAR_NR" ),
@JoinColumn( name = "CAR_VENDOR_NR2", referencedColumnName = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_STUDENT_CAR2", value = ConstraintMode.NO_CONSTRAINT )
)
public Car car2;
@OneToOne
@JoinColumns(
value = {
@JoinColumn( name = "CAR_NR3", referencedColumnName = "CAR_NR" ),
@JoinColumn( name = "CAR_VENDOR_NR3", referencedColumnName = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_STUDENT_CAR3" )
)
public Car car3;
@OneToOne
@JoinColumns(
value = {
@JoinColumn( name = "CAR_NR4", referencedColumnName = "CAR_NR" ),
@JoinColumn( name = "CAR_VENDOR_NR4", referencedColumnName = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_STUDENT_CAR4", value = ConstraintMode.NO_CONSTRAINT )
)
public Car car4;
@ManyToMany
@JoinTable(
name = "VEHICLE_BUY_INFOS",
foreignKey = @ForeignKey( name = "FK_VEHICLE_BUY_INFOS_STUDENT" ),
inverseForeignKey = @ForeignKey( name = "FK_VEHICLE_BUY_INFOS_VEHICLE_BUY_INFO" ),
joinColumns = @JoinColumn( name = "STUDENT_ID"),
inverseJoinColumns = @JoinColumn( name = "VEHICLE_BUY_INFO_ID" )
)
@MapKeyJoinColumns(
value = {
@MapKeyJoinColumn( name = "VEHICLE_NR", referencedColumnName = "VEHICLE_NR" ),
@MapKeyJoinColumn( name = "VEHICLE_VENDOR_NR", referencedColumnName = "VEHICLE_VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_VEHICLE_BUY_INFOS_VEHICLE" )
)
public Map<Vehicle, VehicleBuyInfo> vehicleBuyInfos = new HashMap<>();
}
@Entity(name = "VehicleBuyInfo")
public static class VehicleBuyInfo {
@Id
@GeneratedValue
public long id;
public String info;
}
@Entity(name = "Vehicle")
@Inheritance( strategy = InheritanceType.JOINED )
public static class Vehicle {
@EmbeddedId
public VehicleId id;
}
@Embeddable
public static class VehicleId implements Serializable {
@javax.persistence.Column( name = "VEHICLE_VENDOR_NR" )
public long vehicleVendorNumber;
@javax.persistence.Column( name = "VEHICLE_NR" )
public long vehicleNumber;
@Override
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof VehicleId ) ) return false;
VehicleId vehicleId = (VehicleId) o;
if ( vehicleVendorNumber != vehicleId.vehicleVendorNumber ) return false;
return vehicleNumber == vehicleId.vehicleNumber;
}
@Override
public int hashCode() {
int result = (int) ( vehicleVendorNumber ^ ( vehicleVendorNumber >>> 32 ) );
result = 31 * result + (int) ( vehicleNumber ^ ( vehicleNumber >>> 32 ) );
return result;
}
}
@Entity(name = "Car")
@SecondaryTable(
name = "CAR_DETAILS",
pkJoinColumns = {
@PrimaryKeyJoinColumn( name = "CAR_NR", referencedColumnName = "CAR_NR" ),
@PrimaryKeyJoinColumn( name = "CAR_VENDOR_NR", referencedColumnName = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_CAR_DETAILS_CAR" )
)
@PrimaryKeyJoinColumns(
value = {
@PrimaryKeyJoinColumn( name = "CAR_NR", referencedColumnName = "VEHICLE_NR" ),
@PrimaryKeyJoinColumn( name = "VENDOR_NR", referencedColumnName = "VEHICLE_VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_CAR_VEHICLE" )
)
public static class Car extends Vehicle {
public String color;
@ManyToOne
@JoinColumn( name = "OWNER_PERSON_ID", foreignKey = @ForeignKey( name = "FK_CAR_OWNER") )
public Person owner;
@ManyToOne
@JoinColumn( name = "OWNER_PERSON_ID2", foreignKey = @ForeignKey( name = "FK_CAR_OWNER2", value = ConstraintMode.NO_CONSTRAINT ) )
public Person owner2;
@OneToOne
@JoinColumn( name = "OWNER_PERSON_ID3", foreignKey = @ForeignKey( name = "FK_CAR_OWNER3") )
public Person owner3;
@OneToOne
@JoinColumn( name = "OWNER_PERSON_ID4", foreignKey = @ForeignKey( name = "FK_CAR_OWNER4", value = ConstraintMode.NO_CONSTRAINT ) )
public Person owner4;
@ElementCollection
@CollectionTable(
name = "OWNER_INFO",
joinColumns = {
@JoinColumn( name = "CAR_NR" ),
@JoinColumn( name = "VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_OWNER_INFO_CAR" )
)
@MapKeyJoinColumn( name = "PERSON_ID", foreignKey = @ForeignKey( name = "FK_OWNER_INFO_PERSON" ) )
public Map<Person, String> ownerInfo = new HashMap<>();
}
@Entity(name = "Truck")
@PrimaryKeyJoinColumns(
value = {
@PrimaryKeyJoinColumn( name = "CAR_NR", referencedColumnName = "VEHICLE_NR" ),
@PrimaryKeyJoinColumn( name = "VENDOR_NR", referencedColumnName = "VEHICLE_VENDOR_NR" )
},
foreignKey = @ForeignKey( name = "FK_TRUCK_VEHICLE", value = ConstraintMode.NO_CONSTRAINT )
)
public static class Truck extends Vehicle {
public boolean fourWheelDrive;
}
@MappedSuperclass
public static abstract class AbstractCompany {
@Id
@GeneratedValue
public long id;
@ManyToOne
@JoinColumn( name = "OWNER_ID" )
public Person owner;
@ManyToOne
@JoinColumn( name = "OWNER_ID2" )
public Person owner2;
@OneToOne
@JoinColumn( name = "CC_ID" )
public CreditCard creditCard;
@OneToOne
@JoinColumn( name = "CC_ID2" )
public CreditCard creditCard2;
@OneToMany
@JoinColumn( name = "CC_ID3" )
public List<CreditCard> creditCards1;
@OneToMany
@JoinColumn( name = "CC_ID4" )
public List<CreditCard> creditCards2;
}
@Embeddable
public static class CompanyInfo {
public String data;
@OneToMany
@JoinColumn( name = "CI_CC_ID", foreignKey = @ForeignKey( name = "FK_CI_CC" ) )
public List<CreditCard> cards;
@OneToMany
@JoinColumn( name = "CI_CC_ID2", foreignKey = @ForeignKey( name = "FK_CI_CC2" ) )
public List<CreditCard> cards2;
@ManyToOne
@JoinColumn( name = "CI_CC_ID3", foreignKey = @ForeignKey( name = "FK_CI_CC3" ) )
public CreditCard cards3;
@ManyToOne
@JoinColumn( name = "CI_CC_ID4", foreignKey = @ForeignKey( name = "FK_CI_CC4" ) )
public CreditCard cards4;
}
@Entity(name = "Company")
@AssociationOverrides({
@AssociationOverride(
name = "owner",
joinColumns = @JoinColumn( name = "OWNER_PERSON_ID" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_OWNER" )
),
@AssociationOverride(
name = "owner2",
joinColumns = @JoinColumn( name = "OWNER_PERSON_ID2" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_OWNER2", value = ConstraintMode.NO_CONSTRAINT )
),
@AssociationOverride(
name = "creditCard",
joinColumns = @JoinColumn( name = "CREDIT_CARD_ID" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CREDIT_CARD" )
),
@AssociationOverride(
name = "creditCard2",
joinColumns = @JoinColumn( name = "CREDIT_CARD_ID2" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CREDIT_CARD2", value = ConstraintMode.NO_CONSTRAINT )
),
@AssociationOverride(
name = "creditCards1",
joinColumns = @JoinColumn( name = "CREDIT_CARD_ID3" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CREDIT_CARD3" )
),
@AssociationOverride(
name = "creditCards2",
joinColumns = @JoinColumn( name = "CREDIT_CARD_ID4" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CREDIT_CARD4", value = ConstraintMode.NO_CONSTRAINT )
)
})
public static class Company extends AbstractCompany {
@Embedded
@AssociationOverrides({
@AssociationOverride(
name = "cards",
joinColumns = @JoinColumn( name = "AO_CI_CC_ID" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CARD" )
),
@AssociationOverride(
name = "cards2",
joinColumns = @JoinColumn( name = "AO_CI_CC_ID2" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CARD2", value = ConstraintMode.NO_CONSTRAINT )
),
@AssociationOverride(
name = "cards3",
joinColumns = @JoinColumn( name = "AO_CI_CC_ID3" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CARD3" )
),
@AssociationOverride(
name = "cards4",
joinColumns = @JoinColumn( name = "AO_CI_CC_ID4" ),
foreignKey = @ForeignKey( name = "FK_COMPANY_CARD4", value = ConstraintMode.NO_CONSTRAINT )
)
})
public CompanyInfo info;
}
}