/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2006-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.proxy; import java.math.BigDecimal; import java.util.List; import org.hibernate.FlushMode; import org.hibernate.Hibernate; import org.hibernate.LazyInitializationException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ObjectNotFoundException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.internal.SessionImpl; import org.hibernate.internal.util.SerializationHelper; import org.hibernate.proxy.HibernateProxy; import org.junit.Test; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author Gavin King */ public class ProxyTest extends BaseCoreFunctionalTestCase { @Override public String[] getMappings() { return new String[] { "proxy/DataPoint.hbm.xml" }; } @Override public void configure(Configuration cfg) { super.configure( cfg ); cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" ); // problem on HSQLDB (go figure) } @Override public String getCacheConcurrencyStrategy() { return null; } @Test public void testFinalizeFiltered() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load(DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); try { dp.getClass().getDeclaredMethod( "finalize", (Class[]) null ); fail(); } catch (NoSuchMethodException e) {} s.delete(dp); t.commit(); s.close(); } @Test public void testProxyException() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load(DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); try { dp.exception(); fail(); } catch (Exception e) { assertTrue( e.getClass()==Exception.class ); } s.delete(dp); t.commit(); s.close(); } @Test public void testProxySerializationAfterSessionClosed() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); s.close(); SerializationHelper.clone( dp ); s = openSession(); t = s.beginTransaction(); s.delete( dp ); t.commit(); s.close(); } @Test public void testInitializedProxySerializationAfterSessionClosed() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); Hibernate.initialize( dp ); assertTrue( Hibernate.isInitialized(dp) ); s.close(); SerializationHelper.clone( dp ); s = openSession(); t = s.beginTransaction(); s.delete( dp ); t.commit(); s.close(); } @Test public void testProxySerialization() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); dp.getId(); assertFalse( Hibernate.isInitialized(dp) ); dp.getDescription(); assertTrue( Hibernate.isInitialized(dp) ); Object none = s.load( DataPoint.class, new Long(666)); assertFalse( Hibernate.isInitialized(none) ); t.commit(); s.disconnect(); Object[] holder = new Object[] { s, dp, none }; holder = (Object[]) SerializationHelper.clone(holder); Session sclone = (Session) holder[0]; dp = (DataPoint) holder[1]; none = holder[2]; //close the original: s.close(); t = sclone.beginTransaction(); DataPoint sdp = (DataPoint) sclone.load( DataPoint.class, new Long( dp.getId() ) ); assertSame(dp, sdp); assertFalse(sdp instanceof HibernateProxy); Object snone = sclone.load( DataPoint.class, new Long(666) ); assertSame(none, snone); assertTrue(snone instanceof HibernateProxy); sclone.delete(dp); t.commit(); sclone.close(); } @Test public void testProxy() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription("a data point"); dp.setX( new BigDecimal(1.0) ); dp.setY( new BigDecimal(2.0) ); s.persist(dp); s.flush(); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long(dp.getId() )); assertFalse( Hibernate.isInitialized(dp) ); DataPoint dp2 = (DataPoint) s.get( DataPoint.class, new Long(dp.getId()) ); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); dp2 = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ), LockMode.NONE ); assertSame(dp, dp2); assertFalse( Hibernate.isInitialized(dp) ); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); dp2 = (DataPoint) s.load( DataPoint.class, new Long( dp.getId() ), LockMode.READ ); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long (dp.getId() )); assertFalse( Hibernate.isInitialized(dp) ); dp2 = (DataPoint) s.get( DataPoint.class, new Long ( dp.getId() ) , LockMode.READ ); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.clear(); dp = (DataPoint) s.load( DataPoint.class, new Long ( dp.getId() ) ); assertFalse( Hibernate.isInitialized(dp) ); dp2 = (DataPoint) s.createQuery("from DataPoint").uniqueResult(); assertSame(dp, dp2); assertTrue( Hibernate.isInitialized(dp) ); s.delete( dp ); t.commit(); s.close(); } @Test public void testSubsequentNonExistentProxyAccess() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint proxy = ( DataPoint ) s.load( DataPoint.class, new Long(-1) ); assertFalse( Hibernate.isInitialized( proxy ) ); try { proxy.getDescription(); fail( "proxy access did not fail on non-existent proxy" ); } catch( ObjectNotFoundException onfe ) { // expected } catch( Throwable e ) { fail( "unexpected exception type on non-existent proxy access : " + e ); } // try it a second (subsequent) time... try { proxy.getDescription(); fail( "proxy access did not fail on non-existent proxy" ); } catch( ObjectNotFoundException onfe ) { // expected } catch( Throwable e ) { fail( "unexpected exception type on non-existent proxy access : " + e ); } t.commit(); s.close(); } @SuppressWarnings( {"unchecked"}) @Test public void testProxyEviction() { Session s = openSession(); Transaction t = s.beginTransaction(); Container container = new Container( "container" ); container.setOwner( new Owner( "owner" ) ); container.setInfo( new Info( "blah blah blah" ) ); container.getDataPoints().add( new DataPoint( new BigDecimal( 1 ), new BigDecimal( 1 ), "first data point" ) ); container.getDataPoints().add( new DataPoint( new BigDecimal( 2 ), new BigDecimal( 2 ), "second data point" ) ); s.save( container ); t.commit(); s.close(); s = openSession(); t = s.beginTransaction(); Container c = ( Container ) s.load( Container.class, container.getId() ); assertFalse( Hibernate.isInitialized( c ) ); s.evict( c ); try { c.getName(); fail( "expecting LazyInitializationException" ); } catch( LazyInitializationException e ) { // expected result } c = ( Container ) s.load( Container.class, container.getId() ); assertFalse( Hibernate.isInitialized( c ) ); Info i = c.getInfo(); assertTrue( Hibernate.isInitialized( c ) ); assertFalse( Hibernate.isInitialized( i ) ); s.evict( c ); try { i.getDetails(); fail( "expecting LazyInitializationException" ); } catch( LazyInitializationException e ) { // expected result } s.delete( c ); t.commit(); s.close(); } @Test public void testFullyLoadedPCSerialization() { Session s = openSession(); Transaction t = s.beginTransaction(); Long lastContainerId = null; int containerCount = 10; int nestedDataPointCount = 5; for ( int c_indx = 0; c_indx < containerCount; c_indx++ ) { Owner owner = new Owner( "Owner #" + c_indx ); Container container = new Container( "Container #" + c_indx ); container.setOwner( owner ); for ( int dp_indx = 0; dp_indx < nestedDataPointCount; dp_indx++ ) { DataPoint dp = new DataPoint(); dp.setDescription( "data-point [" + c_indx + ", " + dp_indx + "]" ); // more HSQLDB fun... // dp.setX( new BigDecimal( c_indx ) ); dp.setX( new BigDecimal( c_indx + dp_indx ) ); dp.setY( new BigDecimal( dp_indx ) ); container.getDataPoints().add( dp ); } s.save( container ); lastContainerId = container.getId(); } t.commit(); s.close(); s = openSession(); s.setFlushMode( FlushMode.MANUAL ); t = s.beginTransaction(); // load the last container as a proxy Container proxy = ( Container ) s.load( Container.class, lastContainerId ); assertFalse( Hibernate.isInitialized( proxy ) ); // load the rest back into the PC List all = s.createQuery( "from Container as c inner join fetch c.owner inner join fetch c.dataPoints where c.id <> :last" ) .setLong( "last", lastContainerId.longValue() ) .list(); Container container = ( Container ) all.get( 0 ); s.delete( container ); // force a snapshot retrieval of the proxied container SessionImpl sImpl = ( SessionImpl ) s; sImpl.getPersistenceContext().getDatabaseSnapshot( lastContainerId, sImpl.getFactory().getEntityPersister( Container.class.getName() ) ); assertFalse( Hibernate.isInitialized( proxy ) ); t.commit(); // int iterations = 50; // long cumulativeTime = 0; // long cumulativeSize = 0; // for ( int i = 0; i < iterations; i++ ) { // final long start = System.currentTimeMillis(); // byte[] bytes = SerializationHelper.serialize( s ); // SerializationHelper.deserialize( bytes ); // final long end = System.currentTimeMillis(); // cumulativeTime += ( end - start ); // int size = bytes.length; // cumulativeSize += size; //// System.out.println( "Iteration #" + i + " took " + ( end - start ) + " ms : size = " + size + " bytes" ); // } // System.out.println( "Average time : " + ( cumulativeTime / iterations ) + " ms" ); // System.out.println( "Average size : " + ( cumulativeSize / iterations ) + " bytes" ); byte[] bytes = SerializationHelper.serialize( s ); SerializationHelper.deserialize( bytes ); t = s.beginTransaction(); int count = s.createQuery( "delete DataPoint" ).executeUpdate(); assertEquals( "unexpected DP delete count", ( containerCount * nestedDataPointCount ), count ); count = s.createQuery( "delete Container" ).executeUpdate(); assertEquals( "unexpected container delete count", containerCount, count ); count = s.createQuery( "delete Owner" ).executeUpdate(); assertEquals( "unexpected owner delete count", containerCount, count ); t.commit(); s.close(); } @Test public void testRefreshLockInitializedProxy() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = newPersistentDataPoint( s ); dp = ( DataPoint ) s.load( DataPoint.class, new Long( dp.getId() ) ); dp.getX(); assertTrue( Hibernate.isInitialized( dp ) ); s.refresh( dp, LockOptions.UPGRADE ); assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); s.delete( dp ); t.commit(); s.close(); } @Test @FailureExpected( jiraKey = "HHH-1645", message = "Session.refresh with LockOptions does not work on uninitialized proxies" ) public void testRefreshLockUninitializedProxy() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = newPersistentDataPoint( s ); dp = ( DataPoint ) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized( dp ) ); s.refresh( dp, LockOptions.UPGRADE ); assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); s.delete( dp ); t.commit(); s.close(); } private static DataPoint newPersistentDataPoint(Session s) { DataPoint dp = new DataPoint(); dp.setDescription( "a data point" ); dp.setX( new BigDecimal( 1.0 ) ); dp.setY( new BigDecimal( 2.0 ) ); s.persist( dp ); s.flush(); s.clear(); return dp; } @Test @FailureExpected( jiraKey = "HHH-1645", message = "Session.refresh with LockOptions does not work on uninitialized proxies" ) public void testRefreshLockUninitializedProxyThenRead() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = newPersistentDataPoint( s ); dp = ( DataPoint ) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized( dp ) ); s.refresh( dp, LockOptions.UPGRADE ); dp.getX(); assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); s.delete( dp ); t.commit(); s.close(); } @Test public void testLockUninitializedProxy() { Session s = openSession(); Transaction t = s.beginTransaction(); DataPoint dp = newPersistentDataPoint( s ); dp = ( DataPoint) s.load( DataPoint.class, new Long( dp.getId() ) ); assertFalse( Hibernate.isInitialized( dp ) ); s.buildLockRequest( LockOptions.UPGRADE ).lock( dp ); assertSame( LockOptions.UPGRADE.getLockMode(), s.getCurrentLockMode( dp ) ); s.delete( dp ); t.commit(); s.close(); } }