diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java index 1b9416cebc..1db0387502 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java @@ -37,10 +37,10 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.binding.PluralAttributeBinding; +import org.hibernate.metamodel.spi.relational.ForeignKey; import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.test.annotations.Country; import org.hibernate.test.util.SchemaUtil; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -64,8 +64,8 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { boy.setLastName( "Doe" ); boy.getNickNames().add( "Johnny" ); boy.getNickNames().add( "Thing" ); - boy.getScorePerNickName().put( "Johnny", Integer.valueOf( 3 ) ); - boy.getScorePerNickName().put( "Thing", Integer.valueOf( 5 ) ); + boy.getScorePerNickName().put( "Johnny", 3 ); + boy.getScorePerNickName().put( "Thing", 5 ); int[] favNbrs = new int[4]; for (int index = 0; index < favNbrs.length - 1; index++) { favNbrs[index] = index * 3; @@ -174,8 +174,8 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { boy.setLastName( "Doe" ); boy.getNickNames().add( "Johnny" ); boy.getNickNames().add( "Thing" ); - boy.getScorePerNickName().put( "Johnny", new Integer( 3 ) ); - boy.getScorePerNickName().put( "Thing", new Integer( 5 ) ); + boy.getScorePerNickName().put( "Johnny", 3 ); + boy.getScorePerNickName().put( "Thing", 5 ); int[] favNbrs = new int[4]; for (int index = 0; index < favNbrs.length - 1; index++) { favNbrs[index] = index * 3; @@ -257,24 +257,6 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { isDefaultValueCollectionColumnPresent( Boy.class.getName(), "scorePerPreferredName"); } - @Test - public void testDefaultFKNameForElementCollection() throws Exception { - isCollectionColumnPresent( Boy.class.getName(), "hatedNames", "Boy_id" ); - } - - @Test - - @FailureExpectedWithNewMetamodel( jiraKey = "HHH-9281" ) - @TestForIssue( jiraKey = "HHH-9281") - public void testDefaultTableNameUsesJpaEntityName() { - final TableSpecification table = getCollectionTable( Matrix.class.getName(), "mvalues"); - assertEquals( "Mtx_mvalues", table.getLogicalName().getText() ); - } - - private void isLegacyValueCollectionColumnPresent(String collectionHolder, String propertyName) { - - } - private void isDefaultValueCollectionColumnPresent(String collectionOwner, String propertyName) { isCollectionColumnPresent( collectionOwner, propertyName, propertyName ); } @@ -286,12 +268,133 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { assertTrue( "Could not find " + columnName, hasColumn ); } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameNoOverrides() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Products has @Entity (no @Table) + checkDefaultCollectionTableName( BugSystem.class, "bugs", "BugSystem_bugs" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerPrimaryTableOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Boy has @Entity @Table(name="tbl_Boys") + checkDefaultCollectionTableName( Boy.class, "hatedNames", "Boy_hatedNames" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + @FailureExpected( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + checkDefaultCollectionTableName( Matrix.class, "mvalues", "Mtx_mvalues" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + @FailureExpected( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + checkDefaultCollectionTableName( Owner.class, "elements", "OWNER_elements" ); + } + + private void checkDefaultCollectionTableName( + Class ownerEntityClass, + String ownerCollectionPropertyName, + String expectedCollectionTableName) { + final TableSpecification table = + getCollectionTable( ownerEntityClass.getName(), ownerCollectionPropertyName ); + assertEquals( expectedCollectionTableName, table.getLogicalName().getText() ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnNoOverrides() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Products has @Entity (no @Table) + checkDefaultJoinColumnName( BugSystem.class, "bugs", "BugSystem_id" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + @FailureExpected( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerPrimaryTableOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Boy has @Entity @Table(name="tbl_Boys") + checkDefaultJoinColumnName( Boy.class, "hatedNames", "Boy_id" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + checkDefaultJoinColumnName( Matrix.class, "mvalues", "Mtx_mId" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + @FailureExpected( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + checkDefaultJoinColumnName( Owner.class, "elements", "OWNER_id" ); + } + + private void checkDefaultJoinColumnName( + Class ownerEntityClass, + String ownerCollectionPropertyName, + String ownerForeignKeyNameExpected) { + final EntityBinding entityBinding = metadata().getEntityBinding( ownerEntityClass.getName() ); + // The default owner join column can only be computed if it has a PK with 1 column. + assertEquals( + 1, + entityBinding.getHierarchyDetails() + .getEntityIdentifier() + .getEntityIdentifierBinding() + .getRelationalValueBindings() + .size() + ); + + final ForeignKey ownerForeignKey = getCollectionForeignKey( ownerEntityClass.getName(), ownerCollectionPropertyName ); + assertEquals( ownerForeignKeyNameExpected, ownerForeignKey.getColumns().get( 0 ).getColumnName().getText() ); + } + private TableSpecification getCollectionTable(String collectionOwner, String propertyName) { final EntityBinding entityBinding = metadata().getEntityBinding( collectionOwner ); final PluralAttributeBinding binding = (PluralAttributeBinding) entityBinding.locateAttributeBinding( propertyName ); return binding.getPluralAttributeKeyBinding().getCollectionTable(); } + private ForeignKey getCollectionForeignKey(String collectionOwner, String propertyName) { + final EntityBinding entityBinding = metadata().getEntityBinding( collectionOwner ); + final PluralAttributeBinding binding = (PluralAttributeBinding) entityBinding.locateAttributeBinding( propertyName ); + return binding.getPluralAttributeKeyBinding().getForeignKey(); + } + @Override protected Class[] getAnnotatedClasses() { @@ -306,7 +409,11 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { LocalizedString.class, Toy.class, CountryAttitude.class, - Brand.class + Brand.class, + Owner.class, + BugSystem.class, + Bug.class, + Person.class }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Matrix.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Matrix.java index 7e107c82b0..cc67090f1b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Matrix.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Matrix.java @@ -3,7 +3,7 @@ package org.hibernate.test.annotations.collectionelement; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; - +import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -21,6 +21,7 @@ import org.hibernate.annotations.Type; public class Matrix { @Id @GeneratedValue + @Column(name="mId") private Integer id; @MapKeyType( @Type(type="integer") ) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Owner.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Owner.java new file mode 100644 index 0000000000..4a9f16af15 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/Owner.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, 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.annotations.collectionelement; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; + +/** + * @author Gail Badner + */ +@Entity( name="OWNER") +@Table( name="OWNER_TABLE") +public class Owner { + private Integer id; + private Set elements = new HashSet(); + + @Id + @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @ElementCollection + public Set getElements() { + return elements; + } + + public void setElements(Set elements) { + this.elements = elements; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Category.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Category.java new file mode 100644 index 0000000000..9df506539b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Category.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, 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.annotations.manytomany.defaults; + +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.Table; + +import org.hibernate.test.annotations.manytomany.KnownClient; + +/** + * @author Gail Badner + */ +@Entity(name="CATEGORY") +@Table(name="CATEGORY_TAB") +public class Category { + private Integer id; + private Set clients; + + @Id + @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @ManyToMany + public Set getClients() { + return clients; + } + + public void setClients(Set clients) { + this.clients = clients; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/City.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/City.java new file mode 100644 index 0000000000..6ff26534c6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/City.java @@ -0,0 +1,48 @@ +//$Id$ +package org.hibernate.test.annotations.manytomany.defaults; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.Table; + +import org.hibernate.test.annotations.manytomany.Item; + +/** + * @author Emmanuel Bernard + */ +@Entity +@Table(name = "tbl_city") +public class City { + private Integer id; + private String name; + private Set stolenItems; + + @Id + @GeneratedValue + public Integer getId() { + return id; + } + + @ManyToMany + public Set getStolenItems() { + return stolenItems; + } + + public void setStolenItems(Set stolenItems) { + this.stolenItems = stolenItems; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ContactInfo.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ContactInfo.java new file mode 100644 index 0000000000..34352f3ef4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ContactInfo.java @@ -0,0 +1,25 @@ +package org.hibernate.test.annotations.manytomany.defaults; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.ManyToMany; + +import org.hibernate.test.annotations.manytomany.PhoneNumber; + +@Embeddable +public class ContactInfo { +// @ManyToOne +// Address address; // Unidirectional + + List phoneNumbers; // Bidirectional + + @ManyToMany(cascade= CascadeType.ALL) + public List getPhoneNumbers() { + return phoneNumbers; + } + + public void setPhoneNumbers(List phoneNumbers) { + this.phoneNumbers = phoneNumbers; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Employee.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Employee.java new file mode 100644 index 0000000000..d0dd4e547e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Employee.java @@ -0,0 +1,88 @@ +//$Id$ +package org.hibernate.test.annotations.manytomany.defaults; +import java.io.Serializable; +import java.util.Collection; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToMany; + +import org.hibernate.annotations.Cascade; +import org.hibernate.test.annotations.manytomany.ContactInfo; +import org.hibernate.test.annotations.manytomany.Employer; +import org.hibernate.test.annotations.manytomany.JobInfo; + +/** + * Employee in an Employer-Employee relationship + * + * @author Emmanuel Bernard + */ +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +@SuppressWarnings("serial") +public class Employee implements Serializable { + private Integer id; + private Collection employers; + private String name; + ContactInfo contactInfo; + JobInfo jobInfo; + + // ContactInfo is for ManyToMany testing + @Embedded + public ContactInfo getContactInfo() { + return contactInfo; + } + + public void setContactInfo(ContactInfo contactInfo) { + this.contactInfo = contactInfo; + } + + // JobInfo is for OneToMany testing + @Embedded + public JobInfo getJobInfo() { + return jobInfo; + } + + public void setJobInfo(JobInfo jobInfo) { + this.jobInfo = jobInfo; + } + + + @Column(name="fld_name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Id + @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer integer) { + id = integer; + } + + @ManyToMany( + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, + mappedBy = "employees" + ) + @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, + org.hibernate.annotations.CascadeType.PERSIST}) + public Collection getEmployers() { + return employers; + } + + public void setEmployers(Collection employers) { + this.employers = employers; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Item.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Item.java new file mode 100644 index 0000000000..c023e6ce7e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Item.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, 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.annotations.manytomany.defaults; + +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; + +import org.hibernate.test.annotations.manytomany.*; + +/** + * @author Gail Badner + */ +@Entity(name="ITEM") +public class Item { + private Integer id; + private Set producedInCities; + + @Id + @GeneratedValue + @Column(name="iId") + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @ManyToMany + public Set getProducedInCities() { + return producedInCities; + } + + public void setProducedInCities(Set producedInCities) { + this.producedInCities = producedInCities; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/KnownClient.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/KnownClient.java new file mode 100644 index 0000000000..d3c42031d4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/KnownClient.java @@ -0,0 +1,46 @@ +//$Id$ +package org.hibernate.test.annotations.manytomany.defaults; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; + +import org.hibernate.test.annotations.manytomany.Store; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class KnownClient { + private Integer id; + private String name; + private Set stores; + + @Id + @GeneratedValue + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ManyToMany(mappedBy = "customers") + public Set getStores() { + return stores; + } + + public void setStores(Set stores) { + this.stores = stores; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java new file mode 100644 index 0000000000..f7ab329d6e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java @@ -0,0 +1,275 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, 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.annotations.manytomany.defaults; + +import java.util.Iterator; + +import org.junit.Test; + +import org.hibernate.mapping.ForeignKey; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.test.annotations.manytomany.Category; +import org.hibernate.test.annotations.manytomany.City; +import org.hibernate.test.annotations.manytomany.Contractor; +import org.hibernate.test.annotations.manytomany.Employee; +import org.hibernate.test.annotations.manytomany.Employer; +import org.hibernate.test.annotations.manytomany.Item; +import org.hibernate.test.annotations.manytomany.KnownClient; +import org.hibernate.test.annotations.manytomany.PhoneNumber; +import org.hibernate.test.annotations.manytomany.ProgramManager; +import org.hibernate.test.annotations.manytomany.Store; +import org.hibernate.test.annotations.manytomany.Supplier; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.type.EntityType; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Tests default names for @JoinTable and @JoinColumn for unidirectional and bidirectional + * many-to-many associations. + * + * NOTE: expected primary table names and join columns are explicit here to ensure that + * entity names/tables and PK columns are not changed (which would invalidate these test cases). + * + * @author Gail Badner + */ +public class ManyToManyDefaultsTest extends BaseCoreFunctionalTestCase { + + @Test + public void testBidirNoOverrides() { + // Employee.contactInfo.phoneNumbers: associated entity: PhoneNumber + // both have @Entity with no name configured and default primary table names; + // Primary table names default to unqualified entity classes. + // PK column for Employee.id: id (default) + // PK column for PhoneNumber.phNumber: phNumber (default) + // bidirectional association + checkDefaultJoinTablAndJoinColumnNames( + Employee.class, + "contactInfo.phoneNumbers", + "employees", + "Employee_PhoneNumber", + "employees_id", + "phoneNumbers_phNumber" + ); + } + + @Test + public void testBidirOwnerPKOverride() { + // Store.customers; associated entity: KnownClient + // both have @Entity with no name configured and default primary table names + // Primary table names default to unqualified entity classes. + // PK column for Store.id: sId + // PK column for KnownClient.id: id (default) + // bidirectional association + checkDefaultJoinTablAndJoinColumnNames( + Store.class, + "customers", + "stores", + "Store_KnownClient", + "stores_sId", + "customers_id" + ); + } + + @Test + public void testUnidirOwnerPKAssocEntityNamePKOverride() { + // Store.items; associated entity: Item + // Store has @Entity with no name configured and no @Table + // Item has @Entity(name="ITEM") and no @Table + // PK column for Store.id: sId + // PK column for Item: iId + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + Store.class, + "items", + null, + "Store_ITEM", + "Store_sId", + "items_iId" + + ); + } + + @Test + public void testUnidirOwnerPKAssocPrimaryTableNameOverride() { + // Store.implantedIn; associated entity: City + // Store has @Entity with no name configured and no @Table + // City has @Entity with no name configured and @Table(name = "tbl_city") + // PK column for Store.id: sId + // PK column for City.id: id (default) + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + Store.class, + "implantedIn", + null, + "Store_tbl_city", + "Store_sId", + "implantedIn_id" + ); + } + + @Test + public void testUnidirOwnerPKAssocEntityNamePrimaryTableOverride() { + // Store.categories; associated entity: Category + // Store has @Entity with no name configured and no @Table + // Category has @Entity(name="CATEGORY") @Table(name="CATEGORY_TAB") + // PK column for Store.id: sId + // PK column for Category.id: id (default) + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + Store.class, + "categories", + null, + "Store_CATEGORY_TAB", + "Store_sId", + "categories_id" + ); + } + + @Test + public void testUnidirOwnerEntityNamePKAssocPrimaryTableOverride() { + // Item.producedInCities: associated entity: City + // Item has @Entity(name="ITEM") and no @Table + // City has @Entity with no name configured and @Table(name = "tbl_city") + // PK column for Item: iId + // PK column for City.id: id (default) + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + Item.class, + "producedInCities", + null, + "ITEM_tbl_city", + "ITEM_iId", + "producedInCities_id" + ); + } + + @Test + public void testUnidirOwnerPrimaryTableAssocEntityNamePKOverride() { + // City.stolenItems; associated entity: Item + // City has @Entity with no name configured and @Table(name = "tbl_city") + // Item has @Entity(name="ITEM") and no @Table + // PK column for City.id: id (default) + // PK column for Item: iId + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + City.class, + "stolenItems", + null, + "tbl_city_ITEM", + "City_id", + "stolenItems_iId" + ); + } + + @Test + public void testUnidirOwnerEntityNamePrimaryTableOverride() { + // Category.clients: associated entity: KnownClient + // Category has @Entity(name="CATEGORY") @Table(name="CATEGORY_TAB") + // KnownClient has @Entity with no name configured and no @Table + // PK column for Category.id: id (default) + // PK column for KnownClient.id: id (default) + // unidirectional + checkDefaultJoinTablAndJoinColumnNames( + Category.class, + "clients", + null, + "CATEGORY_TAB_KnownClient", + "CATEGORY_id", + "clients_id" + + ); + } + + private void checkDefaultJoinTablAndJoinColumnNames( + Class ownerEntityClass, + String ownerCollectionPropertyName, + String inverseCollectionPropertyName, + String expectedCollectionTableName, + String ownerForeignKeyNameExpected, + String inverseForeignKeyNameExpected) { + final org.hibernate.mapping.Collection collection = configuration().getCollectionMapping( ownerEntityClass.getName() + '.' + ownerCollectionPropertyName ); + final org.hibernate.mapping.Table table = collection.getCollectionTable(); + assertEquals( expectedCollectionTableName, table.getName() ); + + final org.hibernate.mapping.Collection ownerCollection = configuration().getCollectionMapping( + ownerEntityClass.getName() + '.' + ownerCollectionPropertyName + ); + // The default owner and inverse join columns can only be computed if they have PK with 1 column. + assertEquals ( 1, ownerCollection.getOwner().getKey().getColumnSpan() ); + assertEquals( ownerForeignKeyNameExpected, ownerCollection.getKey().getColumnIterator().next().getText() ); + + final EntityType associatedEntityType = (EntityType) ownerCollection.getElement().getType(); + final PersistentClass associatedPersistentClass = + configuration().getClassMapping( associatedEntityType.getAssociatedEntityName() ); + assertEquals( 1, associatedPersistentClass.getKey().getColumnSpan() ); + if ( inverseCollectionPropertyName != null ) { + final org.hibernate.mapping.Collection inverseCollection = configuration().getCollectionMapping( + associatedPersistentClass.getEntityName() + '.' + inverseCollectionPropertyName + ); + assertEquals( + inverseForeignKeyNameExpected, + inverseCollection.getKey().getColumnIterator().next().getText() + ); + } + boolean hasOwnerFK = false; + boolean hasInverseFK = false; + for ( Iterator it=ownerCollection.getCollectionTable().getForeignKeyIterator(); it.hasNext(); ) { + final ForeignKey fk = (ForeignKey) it.next(); + assertSame( ownerCollection.getCollectionTable(), fk.getTable() ); + if ( fk.getColumnSpan() > 1 ) { + continue; + } + if ( fk.getColumn( 0 ).getText().equals( ownerForeignKeyNameExpected ) ) { + assertSame( ownerCollection.getOwner().getTable(), fk.getReferencedTable() ); + hasOwnerFK = true; + } + else if ( fk.getColumn( 0 ).getText().equals( inverseForeignKeyNameExpected ) ) { + assertSame( associatedPersistentClass.getTable(), fk.getReferencedTable() ); + hasInverseFK = true; + } + } + assertTrue( hasOwnerFK ); + assertTrue( hasInverseFK ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + Category.class, + City.class, + Contractor.class, + Employee.class, + Employer.class, + Item.class, + KnownClient.class, + PhoneNumber.class, + ProgramManager.class, + Store.class, + Supplier.class + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/PhoneNumber.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/PhoneNumber.java new file mode 100644 index 0000000000..fc36b73015 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/PhoneNumber.java @@ -0,0 +1,32 @@ +package org.hibernate.test.annotations.manytomany.defaults; +import java.util.Collection; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToMany; + +import org.hibernate.test.annotations.manytomany.Employee; + +@Entity +public class PhoneNumber { + int phNumber; + Collection employees; + + @Id + public int getPhNumber() { + return phNumber; + } + + public void setPhNumber(int phNumber) { + this.phNumber = phNumber; + } + + @ManyToMany(mappedBy="contactInfo.phoneNumbers", cascade= CascadeType.ALL) + public Collection getEmployees() { + return employees; + } + + public void setEmployees(Collection employees) { + this.employees = employees; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Store.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Store.java new file mode 100644 index 0000000000..e7794ff696 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/Store.java @@ -0,0 +1,99 @@ +//$Id$ +package org.hibernate.test.annotations.manytomany.defaults; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; + +import org.hibernate.test.annotations.manytomany.*; +import org.hibernate.test.annotations.manytomany.City; +import org.hibernate.test.annotations.manytomany.KnownClient; + +/** + * @author Emmanuel Bernard + */ +@Entity +public class Store { + private Integer id; + private String name; + private Set customers; + private Set suppliers; + private Set items; + private Set categories; + + @ManyToMany(cascade = CascadeType.PERSIST) + public Set getImplantedIn() { + return implantedIn; + } + + public void setImplantedIn(Set implantedIn) { + this.implantedIn = implantedIn; + } + + private Set implantedIn; + + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinTable( + name = "StoreSupplier", + joinColumns = @JoinColumn(name = "store"), + inverseJoinColumns = @JoinColumn(name = "supplier") + ) + public Set getSuppliers() { + return suppliers; + } + + public void setSuppliers(Set suppliers) { + this.suppliers = suppliers; + } + + @Id + @GeneratedValue + @Column(name="sId") + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + public Set getCustomers() { + return customers; + } + + public void setCustomers(Set customers) { + this.customers = customers; + } + + @ManyToMany + public Set getItems() { + return items; + } + + public void setItems(Set items) { + this.items = items; + } + + @ManyToMany + public Set getCategories() { + return categories; + } + + public void setCategories(Set categories) { + this.categories = categories; + } +}