/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. * * 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.cid; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Set; import org.hibernate.Session; import org.hibernate.Transaction; 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.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; /** * Tests the use of composite-id with a generator. * Test this behavior in all the various entity states (transient, managed, detached) * and the different state transitions. * * For HHH-2060. * * @author Jacob Robertson */ @TestForIssue( jiraKey = "HHH-2060" ) public class CompositeIdWithGeneratorTest extends BaseCoreFunctionalTestCase { private DateFormat df = SimpleDateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ); @Override public String[] getMappings() { return new String[] { "cid/PurchaseRecord.hbm.xml" }; } @Test public void testCompositeIdSimple() { Session s = openSession(); Transaction t = s.beginTransaction(); // persist the record to get the id generated PurchaseRecord record = new PurchaseRecord(); s.persist(record); t.commit(); s.close(); // test that the id was generated PurchaseRecord.Id generatedId = record.getId(); Date timestamp = record.getTimestamp(); assertNotNull(generatedId); assertNotNull( generatedId.getPurchaseSequence() ); assertTrue(generatedId.getPurchaseNumber() > 0); s = openSession(); t = s.beginTransaction(); // find the record, and see that the ids match PurchaseRecord find = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); assertNotNull(find); assertEquals( generatedId, find.getId() ); assertEquals( df.format(timestamp), df.format(find.getTimestamp()) ); t.commit(); s.close(); s = openSession(); t = s.beginTransaction(); // generate another new record PurchaseRecord record2 = new PurchaseRecord(); s.persist(record2); t.commit(); s.close(); PurchaseRecord.Id generatedId2 = record2.getId(); Date timestamp2 = record2.getTimestamp(); s = openSession(); t = s.beginTransaction(); PurchaseRecord find2 = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId2); t.commit(); s.close(); // test that the ids are different PurchaseRecord.Id id1 = find.getId(); PurchaseRecord.Id id2 = find2.getId(); String seq1 = id1.getPurchaseSequence(); String seq2 = id2.getPurchaseSequence(); int num1 = id1.getPurchaseNumber(); int num2 = id2.getPurchaseNumber(); assertEquals( df.format(timestamp2), df.format(find2.getTimestamp()) ); assertFalse( id1.equals(id2) ); assertFalse( seq1.equals(seq2) ); assertFalse(num1 == num2); } @Test public void testDetachedProperty() { Session s = openSession(); Transaction t = s.beginTransaction(); // persist the record PurchaseRecord record = new PurchaseRecord(); s.persist(record); // close session so we know the record is detached t.commit(); s.close(); PurchaseRecord.Id generatedId = record.getId(); // change a non-id property, but do not persist Date persistedTimestamp = record.getTimestamp(); Date newTimestamp = new Date(persistedTimestamp.getTime() + 1); record.setTimestamp(newTimestamp); s = openSession(); t = s.beginTransaction(); PurchaseRecord find = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); t.commit(); s.close(); // see that we get the original id, and the original timestamp assertEquals( generatedId, find.getId() ); assertEquals( df.format(persistedTimestamp), df.format(find.getTimestamp()) ); s = openSession(); t = s.beginTransaction(); // update with the new timestamp s.update(record); t.commit(); s.close(); // find the newly updated record s = openSession(); t = s.beginTransaction(); PurchaseRecord find2 = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); t.commit(); s.close(); // see that we get the original id, and the new timestamp assertEquals( generatedId, find2.getId() ); assertEquals( df.format(newTimestamp), df.format(find2.getTimestamp()) ); } @Test public void testDetachedId() { Session s = openSession(); Transaction t = s.beginTransaction(); Date timestamp1 = new Date(); Date timestamp2 = new Date(timestamp1.getTime() + 1); // persist two records PurchaseRecord record1 = new PurchaseRecord(); record1.setTimestamp(timestamp1); PurchaseRecord record2 = new PurchaseRecord(); record2.setTimestamp(timestamp2); s.persist(record1); s.persist(record2); // close session so we know the records are detached t.commit(); s.close(); PurchaseRecord.Id generatedId1 = record1.getId(); PurchaseRecord.Id generatedId2 = record2.getId(); // change the ids around - effectively making record1 have the same id as record2 // do not persist yet PurchaseRecord.Id toChangeId1 = new PurchaseRecord.Id(); toChangeId1.setPurchaseNumber( record2.getId().getPurchaseNumber() ); toChangeId1.setPurchaseSequence( record2.getId().getPurchaseSequence() ); record1.setId(toChangeId1); s = openSession(); t = s.beginTransaction(); PurchaseRecord find1 = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId1); PurchaseRecord find2 = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId2); t.commit(); s.close(); // see that we get the original ids (and timestamps) // i.e. weren't changed by changing the detached object assertEquals( generatedId1, find1.getId() ); assertEquals( df.format(timestamp1), df.format(find1.getTimestamp()) ); assertEquals( generatedId2, find2.getId() ); assertEquals( df.format(timestamp2), df.format(find2.getTimestamp()) ); s = openSession(); t = s.beginTransaction(); // update with the new changed record id s.update(record1); t.commit(); s.close(); // test that record1 did not get a new generated id, and kept record2's id PurchaseRecord.Id foundId1 = record1.getId(); assertSame(toChangeId1, foundId1); assertEquals( toChangeId1.getPurchaseNumber(), foundId1.getPurchaseNumber() ); assertEquals( toChangeId1.getPurchaseSequence(), foundId1.getPurchaseSequence() ); // find record 2 and see that it has the timestamp originally found in record 1 s = openSession(); t = s.beginTransaction(); find2 = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId2); t.commit(); s.close(); // see that we get the original id (2), and the new timestamp (1) assertEquals( df.format(timestamp1), df.format(find2.getTimestamp()) ); assertEquals( generatedId2, find2.getId() ); } @Test public void testSaveOrUpdate() { Session s = openSession(); Transaction t = s.beginTransaction(); Date timestamp1 = new Date(); Date timestamp2 = new Date(timestamp1.getTime() + 1); // persist the record PurchaseRecord record = new PurchaseRecord(); record.setTimestamp(timestamp1); s.saveOrUpdate(record); t.commit(); s.close(); // test that the id was generated PurchaseRecord.Id generatedId = record.getId(); assertNotNull(generatedId); assertNotNull( generatedId.getPurchaseSequence() ); // change the timestamp record.setTimestamp(timestamp2); s = openSession(); t = s.beginTransaction(); s.saveOrUpdate(record); t.commit(); s.close(); // see that we get the *same* id, and the new timestamp assertSame( generatedId, record.getId() ); assertEquals( df.format(timestamp2), df.format(record.getTimestamp()) ); } @Test public void testLoad() { Session s = openSession(); Transaction t = s.beginTransaction(); // persist the record, then get the id and timestamp back PurchaseRecord record = new PurchaseRecord(); s.persist(record); t.commit(); s.close(); PurchaseRecord.Id id = record.getId(); Date timestamp = record.getTimestamp(); // using the given id, load a transient record PurchaseRecord toLoad = new PurchaseRecord(); s = openSession(); t = s.beginTransaction(); s.load(toLoad, id); t.commit(); s.close(); // show that the correct timestamp and ids were loaded assertEquals( id, toLoad.getId() ); assertEquals( df.format(timestamp), df.format(toLoad.getTimestamp()) ); } @Test public void testEvict() { Session s = openSession(); Transaction t = s.beginTransaction(); Date timestamp1 = new Date(); Date timestamp2 = new Date(timestamp1.getTime() + 1); // persist the record, then evict it, then make changes to it ("within" the session) PurchaseRecord record = new PurchaseRecord(); record.setTimestamp(timestamp1); s.persist(record); s.flush(); s.evict(record); record.setTimestamp(timestamp2); t.commit(); s.close(); PurchaseRecord.Id generatedId = record.getId(); // now, re-fetch the record and show that the timestamp change wasn't persisted s = openSession(); t = s.beginTransaction(); PurchaseRecord persistent = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); t.commit(); s.close(); assertEquals( generatedId, persistent.getId() ); assertEquals( df.format(timestamp1), df.format(persistent.getTimestamp()) ); } @Test public void testMerge() { Session s = openSession(); Transaction t = s.beginTransaction(); Date timestamp1 = new Date(); Date timestamp2 = new Date(timestamp1.getTime() + 1); // persist the record PurchaseRecord record = new PurchaseRecord(); s.persist(record); t.commit(); s.close(); // test that the id was generated PurchaseRecord.Id generatedId = record.getId(); assertNotNull(generatedId); assertNotNull( generatedId.getPurchaseSequence() ); s = openSession(); t = s.beginTransaction(); // update detached object, retrieve persistent object, then merge PurchaseRecord detached = record; detached.setTimestamp(timestamp2); PurchaseRecord persistent = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); // show that the timestamp hasn't changed assertEquals( df.format(timestamp1), df.format(persistent.getTimestamp()) ); s.merge(detached); t.commit(); s.close(); // show that the persistent object was changed only after the session flush assertEquals( timestamp2, persistent.getTimestamp() ); // show that the persistent store was updated - not just the in-memory object s = openSession(); t = s.beginTransaction(); persistent = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); t.commit(); s.close(); assertEquals( df.format(timestamp2), df.format(persistent.getTimestamp()) ); } @Test public void testDelete() { Session s = openSession(); Transaction t = s.beginTransaction(); // persist the record PurchaseRecord record = new PurchaseRecord(); s.saveOrUpdate(record); t.commit(); s.close(); PurchaseRecord.Id generatedId = record.getId(); // re-fetch, then delete the record s = openSession(); t = s.beginTransaction(); PurchaseRecord find = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); s.delete(find); assertFalse( s.contains(find) ); t.commit(); s.close(); // attempt to re-fetch - show it was deleted s = openSession(); t = s.beginTransaction(); find = (PurchaseRecord) s.get(PurchaseRecord.class, generatedId); t.commit(); s.close(); assertNull(find); } @Test public void testGeneratedIdsWithChildren() { Session s = openSession(); Transaction t = s.beginTransaction(); // set up the record and details PurchaseRecord record = new PurchaseRecord(); Set details = record.getDetails(); details.add( new PurchaseDetail(record, "p@1", 1) ); details.add( new PurchaseDetail(record, "p@2", 2) ); s.persist(record); t.commit(); s.close(); // show that the ids were generated (non-zero) and come out the same int foundPurchaseNumber = record.getId().getPurchaseNumber(); String foundPurchaseSequence = record.getId().getPurchaseSequence(); assertNotNull( record.getId() ); assertTrue(foundPurchaseNumber > 0); assertNotNull(foundPurchaseSequence); // search on detail1 by itself and show it got the parent's id s = openSession(); t = s.beginTransaction(); // doAfterTransactionCompletion a find to show that it will wire together fine PurchaseRecord foundRecord = (PurchaseRecord) s.get(PurchaseRecord.class, new PurchaseRecord.Id(foundPurchaseNumber, foundPurchaseSequence) ); t.commit(); s.close(); // some simple test to see it fetched assertEquals( 2, foundRecord.getDetails().size() ); } }