/* * 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.annotations.manytomany.defaults; import java.util.Iterator; import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.PersistentClass; import org.hibernate.type.EntityType; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * Tests names generated for @JoinTable and @JoinColumn for unidirectional and bidirectional * many-to-many associations using the "legacy JPA" naming strategy, which does not comply * with JPA spec in all cases. See HHH-9390 for more information. * * 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 ManyToManyImplicitNamingTest extends BaseNonConfigCoreFunctionalTestCase { @Override protected void configureMetadataBuilder(MetadataBuilder metadataBuilder) { super.configureMetadataBuilder( metadataBuilder ); metadataBuilder.applyImplicitNamingStrategy( ImplicitNamingStrategyLegacyJpaImpl.INSTANCE ); } @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 @TestForIssue( jiraKey = "HHH-9390") 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 // legacy behavior would use the table name in the generated join column. checkDefaultJoinTablAndJoinColumnNames( Category.class, "clients", null, "CATEGORY_TAB_KnownClient", "CATEGORY_TAB_id", "clients_id" ); } protected void checkDefaultJoinTablAndJoinColumnNames( Class<?> ownerEntityClass, String ownerCollectionPropertyName, String inverseCollectionPropertyName, String expectedCollectionTableName, String ownerForeignKeyNameExpected, String inverseForeignKeyNameExpected) { final org.hibernate.mapping.Collection collection = metadata().getCollectionBinding( ownerEntityClass.getName() + '.' + ownerCollectionPropertyName ); final org.hibernate.mapping.Table table = collection.getCollectionTable(); assertEquals( expectedCollectionTableName, table.getName() ); final org.hibernate.mapping.Collection ownerCollection = metadata().getCollectionBinding( 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 = metadata().getEntityBinding( associatedEntityType.getAssociatedEntityName() ); assertEquals( 1, associatedPersistentClass.getKey().getColumnSpan() ); if ( inverseCollectionPropertyName != null ) { final org.hibernate.mapping.Collection inverseCollection = metadata().getCollectionBinding( 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, Employee.class, Item.class, KnownClient.class, PhoneNumber.class, Store.class, }; } }