/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2011, 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; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.hibernate.Hibernate; import org.hibernate.JDBCException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; import org.junit.Test; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Many to many tests * * @author Emmanuel Bernard */ @SuppressWarnings("unchecked") public class ManyToManyTest extends BaseCoreFunctionalTestCase { @Test public void testDefault() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Store fnac = new Store(); fnac.setName( "Fnac" ); KnownClient emmanuel = new KnownClient(); emmanuel.setName( "Emmanuel" ); emmanuel.setStores( new HashSet<Store>() ); fnac.setCustomers( new HashSet<KnownClient>() ); fnac.getCustomers().add( emmanuel ); emmanuel.getStores().add( fnac ); fnac.setImplantedIn( new HashSet<City>() ); City paris = new City(); fnac.getImplantedIn().add( paris ); paris.setName( "Paris" ); s.persist( fnac ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); Store store; KnownClient knownClient; City city; store = (Store) s.get( Store.class, fnac.getId() ); assertNotNull( store ); assertNotNull( store.getCustomers() ); assertEquals( 1, store.getCustomers().size() ); knownClient = store.getCustomers().iterator().next(); assertEquals( emmanuel.getName(), knownClient.getName() ); assertNotNull( store.getImplantedIn() ); assertEquals( 1, store.getImplantedIn().size() ); city = store.getImplantedIn().iterator().next(); assertEquals( paris.getName(), city.getName() ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); knownClient = (KnownClient) s.get( KnownClient.class, emmanuel.getId() ); assertNotNull( knownClient ); assertNotNull( knownClient.getStores() ); assertEquals( 1, knownClient.getStores().size() ); store = knownClient.getStores().iterator().next(); assertEquals( fnac.getName(), store.getName() ); tx.commit(); s.close(); } @Test public void testCanUseCriteriaQuery() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Store fnac = new Store(); fnac.setName( "Fnac" ); Supplier emi = new Supplier(); emi.setName( "Emmanuel" ); emi.setSuppStores( new HashSet<Store>() ); fnac.setSuppliers( new HashSet<Supplier>() ); fnac.getSuppliers().add( emi ); emi.getSuppStores().add( fnac ); s.persist( fnac ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); List result = s.createCriteria( Supplier.class ).createAlias( "suppStores", "s" ).add( Restrictions.eq( "s.name", "Fnac" ) ).list(); assertEquals( 1, result.size() ); tx.commit(); s.close(); } @Test public void testDefaultCompositePk() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); CatPk catPk = new CatPk(); catPk.setName( "Minou" ); catPk.setThoroughbred( "Persan" ); Cat cat = new Cat(); cat.setId( catPk ); cat.setAge( 32 ); Woman woman = new Woman(); WomanPk womanPk = new WomanPk(); womanPk.setFirstName( "Emma" ); womanPk.setLastName( "Peel" ); woman.setId( womanPk ); woman.setCats( new HashSet<Cat>() ); woman.getCats().add( cat ); cat.setHumanContacts( new HashSet<Woman>() ); cat.getHumanContacts().add( woman ); s.persist( woman ); s.persist( cat ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); Cat sameCat = (Cat) s.get( Cat.class, cat.getId() ); assertNotNull( sameCat ); assertNotNull( sameCat.getHumanContacts() ); assertEquals( 1, sameCat.getHumanContacts().size() ); Woman sameWoman = sameCat.getHumanContacts().iterator().next(); assertEquals( sameWoman.getId().getLastName(), woman.getId().getLastName() ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); sameWoman = (Woman) s.get( Woman.class, woman.getId() ); assertNotNull( sameWoman ); assertNotNull( sameWoman.getCats() ); assertEquals( 1, sameWoman.getCats().size() ); sameCat = sameWoman.getCats().iterator().next(); assertEquals( cat.getAge(), sameCat.getAge() ); tx.commit(); s.close(); } @Test public void testMappedBy() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Store fnac = new Store(); fnac.setName( "Fnac" ); Supplier emi = new Supplier(); emi.setName( "Emmanuel" ); emi.setSuppStores( new HashSet<Store>() ); fnac.setSuppliers( new HashSet<Supplier>() ); fnac.getSuppliers().add( emi ); emi.getSuppStores().add( fnac ); s.persist( fnac ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); Store store; Supplier supplier; store = (Store) s.get( Store.class, fnac.getId() ); assertNotNull( store ); assertNotNull( store.getSuppliers() ); assertEquals( 1, store.getSuppliers().size() ); supplier = store.getSuppliers().iterator().next(); assertEquals( emi.getName(), supplier.getName() ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); supplier = (Supplier) s.get( Supplier.class, emi.getId() ); assertNotNull( supplier ); assertNotNull( supplier.getSuppStores() ); assertEquals( 1, supplier.getSuppStores().size() ); store = supplier.getSuppStores().iterator().next(); assertEquals( fnac.getName(), store.getName() ); tx.commit(); s.close(); } @Test public void testBasic() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Employer er = new Employer(); Employee ee = new Employee(); s.persist( ee ); Set erColl = new HashSet(); Collection eeColl = new ArrayList(); erColl.add( ee ); eeColl.add( er ); er.setEmployees( erColl ); ee.setEmployers( eeColl ); //s.persist(ee); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); er = (Employer) s.load( Employer.class, er.getId() ); assertNotNull( er ); assertNotNull( er.getEmployees() ); assertEquals( 1, er.getEmployees().size() ); Employee eeFromDb = (Employee) er.getEmployees().iterator().next(); assertEquals( ee.getId(), eeFromDb.getId() ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); ee = (Employee) s.get( Employee.class, ee.getId() ); assertNotNull( ee ); assertFalse( "ManyToMany mappedBy lazyness", Hibernate.isInitialized( ee.getEmployers() ) ); tx.commit(); assertFalse( "ManyToMany mappedBy lazyness", Hibernate.isInitialized( ee.getEmployers() ) ); s.close(); s = openSession(); tx = s.beginTransaction(); ee = (Employee) s.get( Employee.class, ee.getId() ); assertNotNull( ee ); er = ee.getEmployers().iterator().next(); assertTrue( "second join non lazy", Hibernate.isInitialized( er ) ); s.delete( er ); s.delete( ee ); tx.commit(); s.close(); } @Test public void testOrderByEmployee() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Employer employer = new Employer(); Employee employee1 = new Employee(); employee1.setName( "Emmanuel" ); Employee employee2 = new Employee(); employee2.setName( "Alice" ); s.persist( employee1 ); s.persist( employee2 ); Set erColl = new HashSet(); Collection eeColl = new ArrayList(); Collection eeColl2 = new ArrayList(); erColl.add( employee1 ); erColl.add( employee2 ); eeColl.add( employer ); eeColl2.add( employer ); employer.setEmployees( erColl ); employee1.setEmployers( eeColl ); employee2.setEmployers( eeColl2 ); s.flush(); s.clear(); employer = (Employer) s.get( Employer.class, employer.getId() ); assertNotNull( employer ); assertNotNull( employer.getEmployees() ); assertEquals( 2, employer.getEmployees().size() ); Employee eeFromDb = (Employee) employer.getEmployees().iterator().next(); assertEquals( employee2.getName(), eeFromDb.getName() ); tx.rollback(); s.close(); } @Test public void testRemoveInBetween() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Employer er = new Employer(); Employee ee = new Employee(); Employee ee2 = new Employee(); s.persist( ee ); s.persist( ee2 ); Set erColl = new HashSet(); Collection eeColl = new ArrayList(); erColl.add( ee ); erColl.add( ee2 ); eeColl.add( er ); er.setEmployees( erColl ); ee.setEmployers( eeColl ); //s.persist(ee); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); er = (Employer) s.load( Employer.class, er.getId() ); assertNotNull( er ); assertNotNull( er.getEmployees() ); assertEquals( 2, er.getEmployees().size() ); Iterator iterator = er.getEmployees().iterator(); Employee eeFromDb = (Employee) iterator.next(); if ( eeFromDb.getId().equals( ee.getId() ) ) { eeFromDb = (Employee) iterator.next(); } assertEquals( ee2.getId(), eeFromDb.getId() ); er.getEmployees().remove( eeFromDb ); eeFromDb.getEmployers().remove( er ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); ee = (Employee) s.get( Employee.class, ee.getId() ); assertNotNull( ee ); assertFalse( "ManyToMany mappedBy lazyness", Hibernate.isInitialized( ee.getEmployers() ) ); tx.commit(); assertFalse( "ManyToMany mappedBy lazyness", Hibernate.isInitialized( ee.getEmployers() ) ); s.close(); s = openSession(); tx = s.beginTransaction(); ee = (Employee) s.get( Employee.class, ee.getId() ); assertNotNull( ee ); er = ee.getEmployers().iterator().next(); assertTrue( "second join non lazy", Hibernate.isInitialized( er ) ); assertEquals( 1, er.getEmployees().size() ); s.delete( er ); s.delete( ee ); tx.commit(); s.close(); } @Test public void testSelf() throws Exception { Session s; Transaction tx; s = openSession(); tx = s.beginTransaction(); Friend f = new Friend(); Friend sndF = new Friend(); f.setName( "Starsky" ); sndF.setName( "Hutch" ); Set frnds = new HashSet(); frnds.add( sndF ); f.setFriends( frnds ); //Starsky is a friend of Hutch but hutch is not s.persist( f ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); f = (Friend) s.load( Friend.class, f.getId() ); assertNotNull( f ); assertNotNull( f.getFriends() ); assertEquals( 1, f.getFriends().size() ); Friend fromDb2ndFrnd = f.getFriends().iterator().next(); assertEquals( fromDb2ndFrnd.getId(), sndF.getId() ); assertEquals( 0, fromDb2ndFrnd.getFriends().size() ); tx.commit(); s.close(); } @Test public void testCompositePk() throws Exception { Session s; Transaction tx; ManPk m1pk = new ManPk(); m1pk.setElder( true ); m1pk.setFirstName( "Lucky" ); m1pk.setLastName( "Luke" ); ManPk m2pk = new ManPk(); m2pk.setElder( false ); m2pk.setFirstName( "Joe" ); m2pk.setLastName( "Dalton" ); Man m1 = new Man(); m1.setId( m1pk ); m1.setCarName( "Jolly Jumper" ); Man m2 = new Man(); m2.setId( m2pk ); WomanPk w1pk = new WomanPk(); w1pk.setFirstName( "Ma" ); w1pk.setLastName( "Dalton" ); WomanPk w2pk = new WomanPk(); w2pk.setFirstName( "Carla" ); w2pk.setLastName( "Bruni" ); Woman w1 = new Woman(); w1.setId( w1pk ); Woman w2 = new Woman(); w2.setId( w2pk ); Set<Woman> womens = new HashSet<Woman>(); womens.add( w1 ); m1.setWomens( womens ); Set<Woman> womens2 = new HashSet<Woman>(); womens2.add( w1 ); womens2.add( w2 ); m2.setWomens( womens2 ); Set<Man> mens = new HashSet<Man>(); mens.add( m1 ); mens.add( m2 ); w1.setMens( mens ); Set<Man> mens2 = new HashSet<Man>(); mens2.add( m2 ); w2.setMens( mens2 ); s = openSession(); tx = s.beginTransaction(); s.persist( m1 ); s.persist( m2 ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); m1 = (Man) s.load( Man.class, m1pk ); assertFalse( m1.getWomens().isEmpty() ); assertEquals( 1, m1.getWomens().size() ); w1 = (Woman) s.load( Woman.class, w1pk ); assertFalse( w1.getMens().isEmpty() ); assertEquals( 2, w1.getMens().size() ); tx.commit(); s.close(); } @Test public void testAssociationTableUniqueConstraints() throws Exception { Session s = openSession(); Permission readAccess = new Permission(); readAccess.setPermission( "read" ); readAccess.setExpirationDate( new Date() ); Collection<Permission> coll = new ArrayList<Permission>( 2 ); coll.add( readAccess ); coll.add( readAccess ); Group group = new Group(); group.setId( new Integer( 1 ) ); group.setPermissions( coll ); s.getTransaction().begin(); try { s.persist( group ); s.getTransaction().commit(); fail( "Unique constraints not applied on association table" ); } catch (JDBCException e) { //success s.getTransaction().rollback(); } finally { s.close(); } } @Test public void testAssociationTableAndOrderBy() throws Exception { Session s = openSession(); s.enableFilter( "Groupfilter" ); Permission readAccess = new Permission(); readAccess.setPermission( "read" ); readAccess.setExpirationDate( new Date() ); Permission writeAccess = new Permission(); writeAccess.setPermission( "write" ); writeAccess.setExpirationDate( new Date( new Date().getTime() - 10*60*1000 ) ); Collection<Permission> coll = new ArrayList<Permission>( 2 ); coll.add( readAccess ); coll.add( writeAccess ); Group group = new Group(); group.setId( new Integer( 1 ) ); group.setPermissions( coll ); s.getTransaction().begin(); s.persist( group ); s.flush(); s.clear(); group = (Group) s.get( Group.class, group.getId() ); s.createQuery( "select g from Group g join fetch g.permissions").list(); assertEquals( "write", group.getPermissions().iterator().next().getPermission() ); s.getTransaction().rollback(); s.close(); } @Test public void testAssociationTableAndOrderByWithSet() throws Exception { Session s = openSession(); s.enableFilter( "Groupfilter" ); Permission readAccess = new Permission(); readAccess.setPermission( "read" ); readAccess.setExpirationDate( new Date() ); Permission writeAccess = new Permission(); writeAccess.setPermission( "write" ); writeAccess.setExpirationDate( new Date( new Date().getTime() - 10*60*1000 ) ); Permission executeAccess = new Permission(); executeAccess.setPermission( "execute" ); executeAccess.setExpirationDate( new Date( new Date().getTime() - 5*60*1000 ) ); Set<Permission> coll = new HashSet<Permission>( 3 ); coll.add( readAccess ); coll.add( writeAccess ); coll.add( executeAccess ); GroupWithSet group = new GroupWithSet(); group.setId( new Integer( 1 ) ); group.setPermissions( coll ); s.getTransaction().begin(); s.persist( group ); s.flush(); s.clear(); group = (GroupWithSet) s.get( GroupWithSet.class, group.getId() ); s.createQuery( "select g from Group g join fetch g.permissions").list(); Iterator<Permission> permIter = group.getPermissions().iterator(); assertEquals( "write", permIter.next().getPermission() ); assertEquals( "execute", permIter.next().getPermission() ); assertEquals( "read", permIter.next().getPermission() ); s.getTransaction().rollback(); s.close(); } @Test public void testJoinedSubclassManyToMany() throws Exception { Session s = openSession(); Zone a = new Zone(); InspectorPrefixes ip = new InspectorPrefixes( "dgi" ); Transaction tx = s.beginTransaction(); s.save( a ); s.save( ip ); ip.getAreas().add( a ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); ip = (InspectorPrefixes) s.get( InspectorPrefixes.class, ip.getId() ); assertNotNull( ip ); assertEquals( 1, ip.getAreas().size() ); assertEquals( a.getId(), ip.getAreas().get( 0 ).getId() ); s.delete( ip ); s.delete( ip.getAreas().get( 0 ) ); tx.commit(); s.close(); } @Test public void testJoinedSubclassManyToManyWithNonPkReference() throws Exception { Session s = openSession(); Zone a = new Zone(); InspectorPrefixes ip = new InspectorPrefixes( "dgi" ); ip.setName( "Inspector" ); Transaction tx = s.beginTransaction(); s.save( a ); s.save( ip ); ip.getDesertedAreas().add( a ); tx.commit(); s.close(); s = openSession(); tx = s.beginTransaction(); ip = (InspectorPrefixes) s.get( InspectorPrefixes.class, ip.getId() ); assertNotNull( ip ); assertEquals( 1, ip.getDesertedAreas().size() ); assertEquals( a.getId(), ip.getDesertedAreas().get( 0 ).getId() ); s.delete( ip ); s.delete( ip.getDesertedAreas().get( 0 ) ); tx.commit(); s.close(); } @Test public void testReferencedColumnNameToSuperclass() throws Exception { Session s = openSession(); Transaction tx = s.beginTransaction(); BuildingCompany comp = new BuildingCompany(); comp.setFoundedIn( new Date() ); comp.setName( "Builder century corp."); s.persist( comp ); Building building = new Building(); building.setCompany( comp ); s.persist( building ); s.flush(); s.clear(); building = (Building) s.get( Building.class, building.getId() ); assertEquals( comp.getName(), building.getCompany().getName() ); tx.rollback(); s.close(); } @Test @TestForIssue( jiraKey = "HHH-4685" ) public void testManyToManyEmbeddableBiDirectionalDotNotationInMappedBy() throws Exception { // Section 11.1.25 // The ManyToMany annotation may be used within an embeddable class contained within an entity class to specify a // relationship to a collection of entities[101]. If the relationship is bidirectional and the entity containing // the embeddable class is the owner of the relationship, the non-owning side must use the mappedBy element of the // ManyToMany annotation to specify the relationship field or property of the embeddable class. The dot (".") // notation syntax must be used in the mappedBy element to indicate the relationship attribute within the embedded // attribute. The value of each identifier used with the dot notation is the name of the respective embedded field // or property. Session s; s = openSession(); s.getTransaction().begin(); Employee e = new Employee(); e.setName( "Sharon" ); List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); Collection<Employee> employees = new ArrayList<Employee>(); employees.add( e ); ContactInfo contactInfo = new ContactInfo(); PhoneNumber number = new PhoneNumber(); number.setEmployees( employees ); phoneNumbers.add( number ); contactInfo.setPhoneNumbers( phoneNumbers ); e.setContactInfo( contactInfo ); s.persist( e ); s.getTransaction().commit(); s.close(); s = openSession(); s.getTransaction().begin(); e = (Employee)s.get( e.getClass(),e.getId() ); // follow both directions of many to many association assertEquals("same employee", e.getName(), e.getContactInfo().getPhoneNumbers().get(0).getEmployees().iterator().next().getName()); s.getTransaction().commit(); s.close(); } @Test @TestForIssue( jiraKey = "HHH-4685" ) public void testOneToManyEmbeddableBiDirectionalDotNotationInMappedBy() throws Exception { // Section 11.1.26 // The ManyToOne annotation may be used within an embeddable class to specify a relationship from the embeddable // class to an entity class. If the relationship is bidirectional, the non-owning OneToMany entity side must use the // mappedBy element of the OneToMany annotation to specify the relationship field or property of the embeddable field // or property on the owning side of the relationship. The dot (".") notation syntax must be used in the mappedBy // element to indicate the relationship attribute within the embedded attribute. The value of each identifier used // with the dot notation is the name of the respective embedded field or property. Session s; s = openSession(); s.getTransaction().begin(); Employee e = new Employee(); JobInfo job = new JobInfo(); job.setJobDescription( "Sushi Chef" ); ProgramManager pm = new ProgramManager(); Collection<Employee> employees = new ArrayList<Employee>(); employees.add(e); pm.setManages( employees ); job.setPm(pm); e.setJobInfo( job ); s.persist( e ); s.getTransaction().commit(); s.close(); s = openSession(); s.getTransaction().begin(); e = (Employee) s.get( e.getClass(), e.getId() ); assertEquals( "same job in both directions", e.getJobInfo().getJobDescription(), e.getJobInfo().getPm().getManages().iterator().next().getJobInfo().getJobDescription() ); s.getTransaction().commit(); s.close(); } @Override protected Class[] getAnnotatedClasses() { return new Class[]{ Friend.class, Employer.class, Employee.class, Contractor.class, Man.class, Woman.class, Store.class, KnownClient.class, Supplier.class, City.class, Cat.class, Group.class, GroupWithSet.class, Permission.class, Zone.class, Inspector.class, InspectorPrefixes.class, BuildingCompany.class, Building.class, PhoneNumber.class, ProgramManager.class }; } }