Java tutorial
package myorg.relex; import static org.junit.Assert.*; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Random; import javax.persistence.TemporalType; import myorg.relex.one2one.Applicant; import myorg.relex.one2one.Application; import myorg.relex.one2one.Attendee; import myorg.relex.one2one.Auto; import myorg.relex.one2one.Auto2; import myorg.relex.one2one.Coach; import myorg.relex.one2one.Driver; import myorg.relex.one2one.Driver2; import myorg.relex.one2one.Employee; import myorg.relex.one2one.License; import myorg.relex.one2one.LicenseApplication; import myorg.relex.one2one.Member; import myorg.relex.one2one.Passenger; import myorg.relex.one2one.Person; import myorg.relex.one2one.Player; import myorg.relex.one2one.Residence; import myorg.relex.one2one.ShowEvent; import myorg.relex.one2one.ShowEventPK; import myorg.relex.one2one.ShowTickets; import myorg.relex.one2one.BoxOffice; import myorg.relex.one2one.Ticket; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.*; /** * Verifies several one-to-one relationships. */ public class One2OneTest extends JPATestBase { private static Log log = LogFactory.getLog(One2OneTest.class); /** * This test provides a demonstration of a simple one-to-one, uni-directional * relationship realized through a foreign key join (FK-join). * The ID of the parent and dependent classes are independent of one * another and the dependent maintains a separate FK to reference the * parent. */ @Test public void testOne2OneUniFK() { log.info("*** testOne2OneUniFK ***"); Person person = new Person(); person.setName("Johnny Unitas"); Player player = new Player(); player.setPerson(person); player.setPosition(Player.Position.OFFENSE); em.persist(person); em.persist(player); //provider will propagate person.id to player.FK //clear the persistence context and get new instances em.flush(); em.clear(); Player player2 = em.find(Player.class, player.getId()); assertEquals("unexpected position", player.getPosition(), player2.getPosition()); assertEquals("unexpected name", player.getPerson().getName(), player2.getPerson().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em.createNativeQuery("select person.id person_id, person.name, " + "player.id player_id, player.person_id player_person_id " + "from RELATIONEX_PLAYER player " + "join RELATIONEX_PERSON person on person.id = player.person_id " + "where player.id = ?1") .setParameter(1, player.getId()).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected person_id", person.getId(), ((Number) cols[0]).intValue()); assertEquals("unexpected person_name", person.getName(), (String) cols[1]); assertEquals("unexpected player_id", player.getId(), ((Number) cols[2]).intValue()); assertEquals("unexpected player_person_id", person.getId(), ((Number) cols[3]).intValue()); //remove the objects and flush commands to the database em.remove(player2); em.remove(player2.getPerson()); em.flush(); assertNull("person not deleted", em.find(Person.class, person.getId())); assertNull("player not deleted", em.find(Player.class, player.getId())); } /** * This test provides a demonstration of a simple one-to-one, uni-directional * relationship realized through a separate join table. The ID of the parent and * dependent are independent of one another and these values are kept in a separate * table to realize the relationship. */ @Test public void testOne2OneUniJoinTable() { log.info("*** testOne2OneUniJoinTable ***"); Person person = new Person(); person.setName("Joe Smith"); Member member = new Member(person); member.setRole(Member.Role.SECONDARY); em.persist(person); em.persist(member); //provider will propagate person.id to player.FK //clear the persistence context and get new instances em.flush(); em.clear(); Member member2 = em.find(Member.class, member.getId()); assertEquals("unexpected role", member.getRole(), member2.getRole()); assertEquals("unexpected name", member.getPerson().getName(), member2.getPerson().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em.createNativeQuery("select person.id person_id, person.name, " + "member.id member_id, member.role member_role, " + "link.member_id link_member, link.person_id link_person " + "from RELATIONEX_MEMBER member " + "join RELATIONEX_MEMBER_PERSON link on link.member_id = member.id " + "join RELATIONEX_PERSON person on link.person_id = person.id " + "where member.id = ?1") .setParameter(1, member.getId()).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected person_id", person.getId(), ((Number) cols[0]).intValue()); assertEquals("unexpected person_name", person.getName(), (String) cols[1]); assertEquals("unexpected member_id", member.getId(), ((Number) cols[2]).intValue()); assertEquals("unexpected member_role", member.getRole().name(), (String) cols[3]); assertEquals("unexpected link_member_id", member.getId(), ((Number) cols[4]).intValue()); assertEquals("unexpected link_person_id", person.getId(), ((Number) cols[5]).intValue()); //remove the objects and flush commands to the database em.remove(member2); em.remove(member2.getPerson()); em.flush(); assertNull("person not deleted", em.find(Person.class, person.getId())); assertNull("member not deleted", em.find(Member.class, member.getId())); } /** * This test provides a demonstration of a one-to-one primary key join. * In this case, the child and parent tables using the same primary key value * and the child re-uses its PK-column as the FK-column. No separate FK-column * is created in the child -- unlike the one-to-one FK-join case above. However, * the provider does not automatically propagate the parent.PK to the dependent.PK * during the persist(). */ @Test public void testOne2OneUniPKJ() { log.info("*** testOne2OneUniPKJ ***"); Person person = new Person(); person.setName("Ozzie Newsome"); em.persist(person); em.flush(); //generate the PK for the person Employee employee = new Employee(person);//set PK/FK -- provider will not auto propagate employee.setHireDate(new GregorianCalendar(1996, Calendar.JANUARY, 1).getTime()); //em.persist(person); em.persist(employee); //clear the persistence context and get new instances em.flush(); em.clear(); Employee employee2 = em.find(Employee.class, employee.getPerson().getId()); log.info("calling person..."); assertEquals("unexpected name", employee.getPerson().getName(), employee2.getPerson().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em .createNativeQuery("select person.id person_id, person.name, " + "employee.id employee_id " + "from RELATIONEX_EMPLOYEE employee " + "join RELATIONEX_PERSON person on person.id = employee.id " + "where employee.id = ?1") .setParameter(1, employee.getId()).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected person_id", person.getId(), ((Number) cols[0]).intValue()); assertEquals("unexpected person_name", person.getName(), (String) cols[1]); assertEquals("unexpected employee_id", employee.getId(), ((Number) cols[2]).intValue()); //remove the objects and flush commands to the database em.remove(employee2); em.remove(employee2.getPerson()); em.flush(); assertNull("person not deleted", em.find(Person.class, person.getId())); assertNull("employee not deleted", em.find(Employee.class, employee.getId())); } /** * This test provides a demonstration of a one-to-on primary key join. * In this case, the entity is configured to use the foreign key mapping as the * primary key using @MapsId. This works much easier than the case where the PK is used * as the FK. The provider automatically propagates the parent.PK to the dependent.FK, * which is re-used to be the dependent.PK. */ @Test public void testOne2OneUniMapsId() { log.info("*** testOne2OneUniMapsId ***"); Person person = new Person(); person.setName("John Harbaugh"); Coach coach = new Coach(person); coach.setType(Coach.Type.HEAD); em.persist(person); em.persist(coach); //provider auto propagates person.id to coach.FK mapped to coach.PK //flush commands to database, clear cache, and pull back new instance em.flush(); em.clear(); Coach coach2 = em.find(Coach.class, coach.getId()); log.info("calling person..."); assertEquals("unexpected name", coach.getPerson().getName(), coach2.getPerson().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em.createNativeQuery("select person.id person_id, person.name, " + "coach.person_id coach_id " + "from RELATIONEX_COACH coach " + "join RELATIONEX_PERSON person on person.id = coach.person_id " + "where coach.person_id = ?1") .setParameter(1, coach.getId()).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected person_id", person.getId(), ((Number) cols[0]).intValue()); assertEquals("unexpected person_name", person.getName(), (String) cols[1]); assertEquals("unexpected coach_id", coach.getId(), ((Number) cols[2]).intValue()); //remove the objects and flush commands to the database em.remove(coach2); em.remove(coach2.getPerson()); em.flush(); assertNull("person not deleted", em.find(Person.class, person.getId())); assertNull("coach not deleted", em.find(Coach.class, coach.getId())); } /** * This test provides a demonstration of creating a one-to-one, uni-directional * relationship to a parent class that uses a composite primary key mapped thru an @IdClass */ @Test public void testOne2OneUniIdClass() { log.info("*** testOne2OneUniIdClass ***"); Date showDate = new GregorianCalendar(1975 + new Random().nextInt(100), Calendar.JANUARY, 1).getTime(); Date showTime = new GregorianCalendar(0, 0, 0, 0, 0, 0).getTime(); ShowEvent show = new ShowEvent(showDate, showTime); show.setName("Rocky Horror"); ShowTickets tickets = new ShowTickets(show); //parent already has natural PK by this point tickets.setTicketsLeft(300); em.persist(show); em.persist(tickets); //flush commands to database, clear cache, and pull back new instance em.flush(); em.clear(); ShowTickets tickets2 = em.find(ShowTickets.class, new ShowEventPK(tickets.getDate(), tickets.getTime())); log.info("calling parent..."); assertEquals("unexpected name", tickets.getShow().getName(), tickets2.getShow().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em.createNativeQuery("select show.date show_date, show.time show_time, " + "tickets.ticket_date ticket_date, tickets.ticket_time ticket_time, tickets.tickets " + "from RELATIONEX_SHOWEVENT show " + "join RELATIONEX_SHOWTICKETS tickets on show.date = tickets.ticket_date and show.time = tickets.ticket_time " + "where tickets.ticket_date = ?1 and tickets.ticket_time = ?2") .setParameter(1, tickets.getShow().getDate(), TemporalType.DATE) .setParameter(2, tickets.getShow().getTime(), TemporalType.TIME).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected show_date", tickets2.getShow().getDate(), (Date) cols[0]); assertEquals("unexpected show_time", tickets2.getShow().getTime(), (Date) cols[1]); assertEquals("unexpected ticket_date", tickets2.getDate(), (Date) cols[2]); assertEquals("unexpected ticket_time", tickets2.getTime(), (Date) cols[3]); assertEquals("unexpected ticketsLeft", tickets2.getTicketsLeft(), ((Number) cols[4]).intValue()); //remove the objects and flush commands to the database em.remove(tickets2); em.remove(tickets2.getShow()); em.flush(); assertNull("tickets not deleted", em.find(ShowEvent.class, new ShowEventPK(show.getDate(), show.getTime()))); assertNull("show not deleted", em.find(ShowTickets.class, new ShowEventPK(tickets.getDate(), tickets.getTime()))); } /** * This test provides a demonstration of creating a one-to-one, uni-directional * relationship to a parent class that uses a composite primary key mapped thru and * @EmbeddedId */ @Test public void testOne2OneUniEmbeddedId() { log.info("*** testOne2OneUniEmbedded ***"); Date showDate = new GregorianCalendar(1975 + new Random().nextInt(100), Calendar.JANUARY, 1).getTime(); Date showTime = new GregorianCalendar(0, 0, 0, 0, 0, 0).getTime(); ShowEvent show = new ShowEvent(showDate, showTime); show.setName("Rocky Horror"); BoxOffice boxOffice = new BoxOffice(show); boxOffice.setTicketsLeft(500); em.persist(show); em.persist(boxOffice); //provider auto propagates parent.cid to dependent.FK mapped to dependent.cid //flush commands to database, clear cache, and pull back new instance em.flush(); em.clear(); BoxOffice boxOffice2 = em.find(BoxOffice.class, new ShowEventPK(boxOffice.getDate(), boxOffice.getTime())); log.info("calling parent..."); assertEquals("unexpected name", boxOffice.getShow().getName(), boxOffice2.getShow().getName()); //verify the contents of the database tables, columns, and relationships Object[] cols = (Object[]) em.createNativeQuery("select show.date show_date, show.time show_time, " + "tickets.show_date ticket_date, tickets.show_time ticket_time, tickets.tickets " + "from RELATIONEX_SHOWEVENT show " + "join RELATIONEX_BOXOFFICE tickets on show.date = tickets.show_date and show.time = tickets.show_time " + "where tickets.show_date = ?1 and tickets.show_time = ?2") .setParameter(1, boxOffice.getShow().getDate(), TemporalType.DATE) .setParameter(2, boxOffice.getShow().getTime(), TemporalType.TIME).getSingleResult(); log.info("row=" + Arrays.toString(cols)); assertEquals("unexpected show_date", boxOffice2.getShow().getDate(), (Date) cols[0]); assertEquals("unexpected show_time", boxOffice2.getShow().getTime(), (Date) cols[1]); assertEquals("unexpected ticket_date", boxOffice2.getDate(), (Date) cols[2]); assertEquals("unexpected ticket_time", boxOffice2.getTime(), (Date) cols[3]); assertEquals("unexpected ticketsLeft", boxOffice2.getTicketsLeft(), ((Number) cols[4]).intValue()); //remove the objects and flush commands to the database em.remove(boxOffice2); em.remove(boxOffice2.getShow()); em.flush(); assertNull("tickets not deleted", em.find(ShowEvent.class, new ShowEventPK(show.getDate(), show.getTime()))); assertNull("show not deleted", em.find(BoxOffice.class, new ShowEventPK(boxOffice.getDate(), boxOffice.getTime()))); } /** * This test provides a demonstration of creating a one-to-one * bi-directional relationship using entities that will share * a generated primary key value and be joined by their common * primary key value. */ @Test public void testOne2OneBiPKJ() { log.info("*** testOne2OneBiPKJ() ***"); Applicant applicant = new Applicant(); applicant.setName("Jason Garret"); Application application = new Application(applicant); application.setDesiredStartDate(new GregorianCalendar(2008, Calendar.JANUARY, 1).getTime()); em.persist(applicant); //provider will generate a PK em.persist(application); //provider will propogate parent.PK to dependent.FK/PK //clear the persistence context and get new instances from the owning side em.flush(); em.clear(); log.info("finding dependent..."); Application application2 = em.find(Application.class, application.getId()); log.info("found dependent..."); assertTrue("unexpected startDate", application.getDesiredStartDate().equals(application2.getDesiredStartDate())); log.info("calling parent..."); assertEquals("unexpected name", application.getApplicant().getName(), application2.getApplicant().getName()); //clear the persistence context and get new instances from the inverse side em.flush(); em.clear(); log.info("finding parent..."); Applicant applicant2 = em.find(Applicant.class, applicant.getId()); log.info("found parent..."); assertEquals("unexpected name", applicant.getName(), applicant2.getName()); log.info("calling dependent..."); assertTrue("unexpected startDate", applicant.getApplication().getDesiredStartDate() .equals(applicant2.getApplication().getDesiredStartDate())); //remove the objects and flush commands to the database em.remove(applicant2.getApplication()); em.remove(applicant2); em.flush(); assertNull("applicant not deleted", em.find(Applicant.class, applicant2.getId())); assertNull("application not deleted", em.find(Application.class, applicant2.getApplication().getId())); } /** * This test provides a demonstration of creating a one-to-one bi-directional * relationship where the inverse/parent side of the relation has a 0..1 with * the owning/dependent side. */ @Test public void testOne2OneBiOwningOptional() { log.info("*** testOne2OneBiOwningOptional() ***"); Auto auto = new Auto(); //auto is inverse/parent side auto.setType(Auto.Type.CAR); Driver driver = new Driver(auto); //driver is owning/dependent side driver.setName("Danica Patrick"); auto.setDriver(driver); //application must maintain inverse side em.persist(auto); em.persist(driver); //clear the persistence context and get new instances from the owning side em.flush(); em.clear(); log.info("finding dependent..."); Driver driver2 = em.find(Driver.class, driver.getId()); log.info("found dependent..."); assertEquals("unexpected name", driver.getName(), driver2.getName()); log.info("calling parent..."); assertEquals("unexpected name", driver.getAuto().getType(), driver2.getAuto().getType()); //clear the persistence context and get new instances from the inverse side em.flush(); em.clear(); log.info("finding parent..."); Auto auto2 = em.find(Auto.class, auto.getId()); log.info("found parent..."); assertEquals("unexpected type", auto.getType(), auto.getType()); log.info("calling dependent..."); assertEquals("unexpected name", auto.getDriver().getName(), auto2.getDriver().getName()); //create a new auto for the current driver to be switched to Auto truck = new Auto(); truck.setType(Auto.Type.TRUCK); em.persist(truck); driver = em.find(Driver.class, driver.getId()); //get the managed instance driver.setAuto(truck); truck.setDriver(driver); em.flush(); em.clear(); Auto auto3 = em.find(Auto.class, auto.getId()); Driver driver3 = em.find(Driver.class, driver.getId()); Auto truck3 = em.find(Auto.class, truck.getId()); assertNull("driver not removed from auto", auto3.getDriver()); assertEquals("driver not assigned to truck", truck.getId(), driver3.getAuto().getId()); assertEquals("truck not assigned to driver", driver.getId(), truck3.getDriver().getId()); //remove remaining object(s) from database em.remove(truck3.getDriver()); em.remove(truck3); em.remove(auto3); em.flush(); assertNull("driver not deleted", em.find(Driver.class, truck3.getDriver().getId())); assertNull("auto not deleted", em.find(Auto.class, auto.getId())); assertNull("truck not deleted", em.find(Auto.class, truck.getId())); } /** * This test provides a demonstration of making the inverse/parent side of * a relationship optional and the owning/dependent side required. */ @Test public void testOne2OneBiInverseOptional() { log.info("*** testOne2OneBiInverseOptional() ***"); Auto2 auto = new Auto2(); //auto is owning/dependent side auto.setType(Auto2.Type.CAR); Driver2 driver = new Driver2(auto); //driver is inverse/parent side driver.setName("Danica Patrick"); auto.setDriver(driver); //owning side must be set em.persist(driver); em.persist(auto); em.flush(); //clear the persistence context and get new instances from the inverse side em.flush(); em.clear(); log.info("finding parent..."); Driver2 driver2 = em.find(Driver2.class, driver.getId()); log.info("found parent..."); assertEquals("unexpected name", driver.getName(), driver2.getName()); log.info("calling dependent..."); assertEquals("unexpected name", driver.getAuto().getType(), driver2.getAuto().getType()); //clear the persistence context and get new instances from the owning side em.flush(); em.clear(); log.info("finding dependent..."); Auto2 auto2 = em.find(Auto2.class, auto.getId()); log.info("found dependent..."); assertEquals("unexpected type", auto.getType(), auto.getType()); log.info("calling parent..."); assertEquals("unexpected name", auto.getDriver().getName(), auto2.getDriver().getName()); //create a new auto for the current driver to be switched to Auto2 truck = new Auto2(); truck.setType(Auto2.Type.TRUCK); driver = em.find(Driver2.class, driver.getId()); //get the managed instance driver.setAuto(truck); auto2.setDriver(null); //must remove reference to former driver truck.setDriver(driver);//prior to assigning to new driver for 1:1 em.persist(truck); em.flush(); em.clear(); Auto2 auto3 = em.find(Auto2.class, auto.getId()); Driver2 driver3 = em.find(Driver2.class, driver.getId()); Auto2 truck3 = em.find(Auto2.class, truck.getId()); assertNull("driver not removed from auto", auto3.getDriver()); assertEquals("driver not assigned to truck", truck.getId(), driver3.getAuto().getId()); assertEquals("truck not assigned to driver", driver.getId(), truck3.getDriver().getId()); //remove remaining object(s) from database em.remove(truck3); em.remove(auto3); em.remove(truck3.getDriver()); em.flush(); assertNull("driver not deleted", em.find(Driver.class, truck3.getDriver().getId())); assertNull("auto not deleted", em.find(Auto.class, auto.getId())); assertNull("truck not deleted", em.find(Auto.class, truck.getId())); } @Test @Ignore public void testOne2OneOrphan() { log.info("*** testOne2OneOrphan ***"); } /** * This test demonstrates use of cascades in a one-to-one * uni-directional relationship or one where all cascades come * from the owning/dependent side. */ @Test public void testOne2OneCascadeFromOwner() { log.info("*** testOne2OneCascadeFromOwner ***"); License license = new License(); license.setRenewal(new GregorianCalendar(2012, 1, 1).getTime()); LicenseApplication licapp = new LicenseApplication(license); licapp.setUpdated(new Date()); em.persist(licapp); em.flush(); //detach the current instances and obtain new instances assertTrue("licapp was not managed???", em.contains(licapp)); assertTrue("license was not managed???", em.contains(license)); em.detach(licapp); assertFalse("licapp still managed", em.contains(licapp)); assertFalse("license still managed", em.contains(license)); licapp = em.find(LicenseApplication.class, licapp.getId()); license = licapp.getLicense(); //perform a bulk update and refresh on the entities to synchronize state Date newDate = new GregorianCalendar(2014, 1, 1).getTime(); Date newUpdate = new Date(licapp.getUpdated().getTime() + 1); assertEquals("unexpected update count", 1, em.createQuery("update License lic set lic.renewal=:renewal where lic.id=:id") .setParameter("renewal", newDate, TemporalType.DATE).setParameter("id", license.getId()) .executeUpdate()); assertEquals("unexpected update count", 1, em.createQuery("update LicenseApplication licapp set licapp.updated=:updated where licapp.id=:id") .setParameter("updated", newUpdate, TemporalType.TIMESTAMP) .setParameter("id", licapp.getId()).executeUpdate()); assertFalse("unexpected updated value prior to refresh", licapp.getUpdated().getTime() == newUpdate.getTime()); assertFalse("unexpected renewal value prior to refresh", license.getRenewal().getTime() == newDate.getTime()); log.info("database updated"); em.refresh(licapp); log.info("entities refreshed"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); assertTrue(String.format("licapp not refreshed, exp=%s, act=%s", df.format(newUpdate), df.format(licapp.getUpdated())), licapp.getUpdated().getTime() == newUpdate.getTime()); assertTrue(String.format("license not refreshed, exp=%s, act=%s", df.format(newDate), df.format(license.getRenewal())), license.getRenewal().getTime() == newDate.getTime()); //detach, change, and merge changes from the detached entities em.detach(licapp); newDate = new GregorianCalendar(2016, 1, 1).getTime(); newUpdate = new Date(licapp.getUpdated().getTime() + 1); assertFalse("licapp still managed", em.contains(licapp)); assertFalse("license still managed", em.contains(licapp.getLicense())); licapp.setUpdated(newUpdate); licapp.getLicense().setRenewal(newDate); log.info("merging changes to detached entities"); licapp = em.merge(licapp); em.flush(); log.info("merging complete"); assertTrue("merged licapp not managed", em.contains(licapp)); assertTrue("merged licapp.license not managed", em.contains(licapp.getLicense())); assertTrue(String.format("licapp not merged, exp=%s, act=%s", df.format(newUpdate), df.format(licapp.getUpdated())), licapp.getUpdated().getTime() == newUpdate.getTime()); assertTrue( String.format("license not merged, exp=%s, act=%s", df.format(newDate), df.format(license.getRenewal())), licapp.getLicense().getRenewal().getTime() == newDate.getTime()); //delete remaining objects em.remove(licapp); em.flush(); assertNull("licapp not deleted", em.find(LicenseApplication.class, licapp.getId())); assertNull("licapp.license not deleted", em.find(License.class, licapp.getLicense().getId())); } /** * This test demonstrates use of cascades in a one-to-one * bi-directional relationship where all cascades come * from the inverse/parent side of the relationship. */ @Test public void testOne2OneCascadeFromInverse() { log.info("*** testOne2OneCascadeFromInverse ***"); Ticket ticket = new Ticket(); ticket.setDate(new GregorianCalendar(2013, Calendar.MARCH, 16).getTime()); Passenger passenger = new Passenger(ticket, "Fred"); //set inverse side ticket.setPassenger(passenger); //set the owning side em.persist(ticket); //persist from inverse side em.flush(); assertTrue("ticket not managed", em.contains(ticket)); assertTrue("passenger not managed", em.contains(passenger)); log.debug("detach both instances from the persistence context"); em.detach(ticket); assertFalse("ticket managed", em.contains(ticket)); assertFalse("passenger managed", em.contains(passenger)); log.debug("perform a bulk update to both objects"); ticket = em.find(Ticket.class, ticket.getId()); Date newDate = new GregorianCalendar(2013, Calendar.APRIL, 1).getTime(); String newName = "Frederick"; em.createQuery("update Ticket t set t.date=:date").setParameter("date", newDate, TemporalType.DATE) .executeUpdate(); em.createQuery("update Passenger p set p.name=:name where p.name='Fred'").setParameter("name", newName) .executeUpdate(); assertFalse("unexpected date", newDate.equals(ticket.getDate())); assertFalse("unexpected name", newName.equals(ticket.getPassenger().getName())); em.refresh(ticket); assertTrue("date not refreshed", newDate.equals(ticket.getDate())); assertTrue("name not refreshed", newName.equals(ticket.getPassenger().getName())); log.debug("merging changes from inverse side"); Ticket ticket2 = new Ticket(ticket.getId()); ticket2.setDate(new GregorianCalendar(2014, Calendar.APRIL, 1).getTime()); Passenger passenger2 = new Passenger(passenger.getId()); passenger2.setName("Rick"); ticket2.setPassenger(passenger2); passenger2.setTicket(ticket2); assertFalse("unexpected date", ticket2.getDate().equals(ticket.getDate())); assertFalse("unexpected name", ticket2.getPassenger().getName().equals(ticket.getPassenger().getName())); ticket = em.merge(ticket2); em.flush(); assertTrue("date not merged", ticket2.getDate().equals(ticket.getDate())); assertTrue("name not merged", ticket2.getPassenger().getName().equals(ticket.getPassenger().getName())); log.debug("delete the entities from the inverse side"); assertNotNull("ticket not found", em.find(Ticket.class, ticket.getId())); assertNotNull("passenger not found", em.find(Passenger.class, ticket.getPassenger().getId())); em.remove(ticket); em.flush(); assertNull("ticket not removed", em.find(Ticket.class, ticket.getId())); assertNull("passenger not removed", em.find(Passenger.class, ticket.getPassenger().getId())); } /** * This test demonstrates the capability to have the provider automatically * delete a parent class when it becomes dereferenced from its dependent in * a relationship. */ @Test public void testOrphanRemoval() { log.info("*** testOrphanRemoval ***"); log.debug("start by verifying the state of the database"); int startCount = em.createQuery("select count(r) from Residence r", Number.class).getSingleResult() .intValue(); log.debug("create a new attendee and residence"); Attendee attendee = new Attendee(); attendee.setName("jones"); attendee.setResidence(new Residence("Columbia", "MD")); em.persist(attendee); em.flush(); log.debug("verify we have a new residence in the database"); assertEquals("unexpected number of residences", startCount + 1, em.createQuery("select count(r) from Residence r", Number.class).getSingleResult().intValue()); log.debug("verify we can find our new instance"); int originalId = attendee.getResidence().getId(); assertNotNull("could not find residence", em.find(Residence.class, originalId)); log.debug("have attendee change residence"); //ISSUE: https://hibernate.atlassian.net/browse/HHH-6484 //MORE: https://hibernate.atlassian.net/browse/HHH-5559 attendee.setResidence(null); em.flush(); attendee.setResidence(new Residence("Baltimore", "MD")); em.flush(); log.debug("verify we have the same number of residences"); assertEquals("unexpected number of residences", startCount + 1, em.createQuery("select count(r) from Residence r", Number.class).getSingleResult().intValue()); log.debug("verify the new instance replaced the original instance"); assertNull("found original residence", em.find(Residence.class, originalId)); assertNotNull("could not find new residence", em.find(Residence.class, attendee.getResidence().getId())); log.debug("remove reference to the current residence"); attendee.setResidence(null); //em.flush(); -- note flush is done during follow-on query log.debug("verify all residences created during this test have been deleted"); assertEquals("unexpected number of residences", startCount, em.createQuery("select count(r) from Residence r", Number.class).getSingleResult().intValue()); } }