From: <hib...@li...> - 2006-06-07 15:30:07
|
Author: ste...@jb... Date: 2006-06-07 11:30:02 -0400 (Wed, 07 Jun 2006) New Revision: 9996 Added: trunk/Hibernate3/test/org/hibernate/test/jpa/ trunk/Hibernate3/test/org/hibernate/test/jpa/AbstractJPATest.java trunk/Hibernate3/test/org/hibernate/test/jpa/Item.hbm.xml trunk/Hibernate3/test/org/hibernate/test/jpa/Item.java trunk/Hibernate3/test/org/hibernate/test/jpa/JPAComplianceSuite.java trunk/Hibernate3/test/org/hibernate/test/jpa/Part.hbm.xml trunk/Hibernate3/test/org/hibernate/test/jpa/Part.java trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/ trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/FetchingTest.java trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.hbm.xml trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.java trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Stay.java trunk/Hibernate3/test/org/hibernate/test/jpa/lock/ trunk/Hibernate3/test/org/hibernate/test/jpa/lock/EJB3LockTest.java trunk/Hibernate3/test/org/hibernate/test/jpa/lock/RepeatableReadTest.java trunk/Hibernate3/test/org/hibernate/test/jpa/package.html trunk/Hibernate3/test/org/hibernate/test/jpa/proxy/ trunk/Hibernate3/test/org/hibernate/test/jpa/proxy/JPAProxyTest.java trunk/Hibernate3/test/org/hibernate/test/jpa/ql/ trunk/Hibernate3/test/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java trunk/Hibernate3/test/org/hibernate/test/jpa/removed/ trunk/Hibernate3/test/org/hibernate/test/jpa/removed/RemovedEntityTest.java Log: ejb3 -> jpa Added: trunk/Hibernate3/test/org/hibernate/test/jpa/AbstractJPATest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/AbstractJPATest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/AbstractJPATest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,67 @@ +package org.hibernate.test.jpa; + +import org.hibernate.test.TestCase; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.proxy.EntityNotFoundDelegate; + +import java.io.Serializable; + +/** + * todo: describe AbstractJPATest + * + * @author Steve Ebersole + */ +public class AbstractJPATest extends TestCase { + public AbstractJPATest(String name) { + super( name ); + } + + protected String[] getMappings() { + return new String[] { "jpa/Part.hbm.xml", "jpa/Item.hbm.xml" }; + } + + protected void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( Environment.JPAQL_STRICT_COMPLIANCE, "true" ); + cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" ); + cfg.setEntityNotFoundDelegate( new JPAEntityNotFoundDelegate() ); + } + + public String getCacheConcurrencyStrategy() { + // no second level caching + return null; + } + + private static class JPAEntityNotFoundDelegate implements EntityNotFoundDelegate { + public void handleEntityNotFound(String entityName, Serializable id) { + throw new EntityNotFoundException( entityName, id ); + } + } + + /** + * Mimic the JPA EntityNotFoundException. + */ + public static class EntityNotFoundException extends RuntimeException { + private final String entityName; + private final Serializable id; + + public EntityNotFoundException(String entityName, Serializable id) { + this( "unable to locate specified entity", entityName, id ); + } + + public EntityNotFoundException(String message, String entityName, Serializable id) { + super( message ); + this.entityName = entityName; + this.id = id; + } + + public String getEntityName() { + return entityName; + } + + public Serializable getId() { + return id; + } + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/Item.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/Item.hbm.xml 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/Item.hbm.xml 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,22 @@ +<?xml version="1.0"?> + +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD 3.0//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> + +<hibernate-mapping package="org.hibernate.test.jpa"> + + <class name="Item" table="EJB3_ITEM"> + <id name="id" column="ITEM_ID" type="long"> + <generator class="increment"/> + </id> + <version name="version" column="VERS" type="long"/> + <property name="name" column="NAME" not-null="true"/> + <!-- modeled as many-to-one even though, yes, in real life would normally be many-to-many --> + <set name="parts" cascade="all" fetch="subselect" inverse="true"> + <key column="ITEM_ID"/> + <one-to-many class="Part"/> + </set> + </class> + +</hibernate-mapping> Added: trunk/Hibernate3/test/org/hibernate/test/jpa/Item.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/Item.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/Item.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,53 @@ +package org.hibernate.test.jpa; + +import java.util.Set; +import java.util.HashSet; + +/** + * @author Steve Ebersole + */ +public class Item { + private Long id; + private String name; + private long version; + private Set parts = new HashSet(); + + public Item() { + } + + public Item(String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } + + public Set getParts() { + return parts; + } + + public void setParts(Set parts) { + this.parts = parts; + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/JPAComplianceSuite.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/JPAComplianceSuite.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/JPAComplianceSuite.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,26 @@ +package org.hibernate.test.jpa; + +import junit.framework.Test; +import junit.framework.TestSuite; +import org.hibernate.test.jpa.lock.EJB3LockTest; +import org.hibernate.test.jpa.lock.RepeatableReadTest; +import org.hibernate.test.jpa.removed.RemovedEntityTest; +import org.hibernate.test.jpa.proxy.JPAProxyTest; +import org.hibernate.test.jpa.fetch.FetchingTest; +import org.hibernate.test.jpa.ql.JPAQLComplianceTest; + +/** + * @author Steve Ebersole + */ +public class JPAComplianceSuite { + public static Test suite() { + TestSuite suite = new TestSuite( "EJB3-compliance tests"); + suite.addTest( EJB3LockTest.suite() ); + suite.addTest( RepeatableReadTest.suite() ); + suite.addTest( JPAProxyTest.suite() ); + suite.addTest( FetchingTest.suite() ); + suite.addTest( JPAQLComplianceTest.suite() ); + suite.addTest( RemovedEntityTest.suite() ); + return suite; + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/Part.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/Part.hbm.xml 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/Part.hbm.xml 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,19 @@ +<?xml version="1.0"?> + +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD 3.0//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> + +<hibernate-mapping package="org.hibernate.test.jpa"> + + <class name="Part" table="EJB3_PART"> + <id name="id" column="PART_ID" type="long"> + <generator class="increment"/> + </id> + <many-to-one name="item" class="Item" column="ITEM_ID" cascade="save-update, lock" not-null="true"/> + <property name="name" column="NAME" not-null="true" type="string"/> + <property name="stockNumber" column="STOCK_NUM" not-null="true" type="string"/> + <property name="unitPrice" column="UNIT_PRICE" not-null="true" type="big_decimal"/> + </class> + +</hibernate-mapping> Added: trunk/Hibernate3/test/org/hibernate/test/jpa/Part.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/Part.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/Part.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,66 @@ +package org.hibernate.test.jpa; + +import java.math.BigDecimal; + +/** + * @author Steve Ebersole + */ +public class Part { + private Long id; + private Item item; + private String name; + private String stockNumber; + private BigDecimal unitPrice; + + public Part() { + } + + public Part(Item item, String name, String stockNumber, BigDecimal unitPrice) { + this.item = item; + this.name = name; + this.stockNumber = stockNumber; + this.unitPrice = unitPrice; + + this.item.getParts().add( this ); + } + + public Long getId() { + return id; + } + + private void setId(Long id) { + this.id = id; + } + + public Item getItem() { + return item; + } + + private void setItem(Item item) { + this.item = item; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStockNumber() { + return stockNumber; + } + + public void setStockNumber(String stockNumber) { + this.stockNumber = stockNumber; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/FetchingTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/FetchingTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/FetchingTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,84 @@ +package org.hibernate.test.jpa.fetch; + +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.Hibernate; + +import java.util.Date; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author Emmanuel Bernard + */ +public class FetchingTest extends AbstractJPATest { + + public void testLazy() throws Exception { + Session s; + Transaction tx; + s = openSession(); + tx = s.beginTransaction(); + Person p = new Person( "Gavin", "King", "JBoss Inc" ); + Stay stay = new Stay( p, new Date(), new Date(), "A380", "Blah", "Blah" ); + p.addStay( stay ); + s.persist( p ); + tx.commit(); + s.clear(); + tx = s.beginTransaction(); + p = (Person) s.createQuery( "select p from Person p where p.firstName = :name" ) + .setParameter( "name", "Gavin" ).uniqueResult(); + assertFalse( Hibernate.isInitialized( p.getStays() ) ); + s.delete( p ); + tx.commit(); + s.close(); + } + + public void testHibernateFetchingLazy() throws Exception { + Session s; + Transaction tx; + s = openSession(); + tx = s.beginTransaction(); + Person p = new Person( "Gavin", "King", "JBoss Inc" ); + Stay stay = new Stay( null, new Date(), new Date(), "A380", "Blah", "Blah" ); + Stay stay2 = new Stay( null, new Date(), new Date(), "A320", "Blah", "Blah" ); + Stay stay3 = new Stay( null, new Date(), new Date(), "A340", "Blah", "Blah" ); + stay.setOldPerson( p ); + stay2.setVeryOldPerson( p ); + stay3.setVeryOldPerson( p ); + p.addOldStay( stay ); + p.addVeryOldStay( stay2 ); + p.addVeryOldStay( stay3 ); + s.persist( p ); + tx.commit(); + s.clear(); + tx = s.beginTransaction(); + p = (Person) s.createQuery( "select p from Person p where p.firstName = :name" ) + .setParameter( "name", "Gavin" ).uniqueResult(); + assertFalse( Hibernate.isInitialized( p.getOldStays() ) ); + assertEquals( 1, p.getOldStays().size() ); + assertFalse( "lazy extra is failing", Hibernate.isInitialized( p.getOldStays() ) ); + s.clear(); + stay = (Stay) s.get( Stay.class, stay.getId() ); + assertTrue( ! Hibernate.isInitialized( stay.getOldPerson() ) ); + s.clear(); + stay3 = (Stay) s.get( Stay.class, stay3.getId() ); + assertTrue( "FetchMode.JOIN should overrides lazy options", Hibernate.isInitialized( stay3.getVeryOldPerson() ) ); + s.delete( stay3.getVeryOldPerson() ); + tx.commit(); + s.close(); + } + + public FetchingTest(String x) { + super( x ); + } + + protected String[] getMappings() { + return new String[] { "jpa/fetch/Person.hbm.xml" }; + } + + public static Test suite() { + return new TestSuite( FetchingTest.class ); + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.hbm.xml 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.hbm.xml 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,48 @@ +<!DOCTYPE hibernate-mapping PUBLIC + "-//Hibernate/Hibernate Mapping DTD 3.0//EN" + "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> + +<hibernate-mapping package="org.hibernate.test.jpa.fetch"> + + <class name="Person" table="PERSON"> + <id name="id" column="ID" type="long"> + <generator class="increment"/> + </id> + <property name="firstName"/> + <property name="lastName"/> + <property name="companyName"/> + + <bag name="stays" cascade="all" lazy="true" inverse="true"> + <key column="PERS_ID"/> + <one-to-many class="Stay"/> + </bag> + + <bag name="oldStays" cascade="all" lazy="extra" fetch="subselect" inverse="true"> + <key column="OLD_PERS_ID"/> + <one-to-many class="Stay"/> + </bag> + + <bag name="veryOldStays" cascade="all" lazy="true" fetch="select" inverse="true"> + <key column="VERY_OLD_PERS_ID"/> + <one-to-many class="Stay"/> + </bag> + </class> + + <class name="Stay" table="STAY"> + <id name="id" column="ID" type="long"> + <generator class="increment"/> + </id> + + <property name="startDate"/> + <property name="endDate"/> + <property name="vessel"/> + <property name="authoriser"/> + <property name="comments"/> + + <many-to-one name="person" column="PERS_ID" class="Person" cascade="all"/> + <many-to-one name="oldPerson" column="OLD_PERS_ID" class="Person" cascade="all" fetch="select"/> + <many-to-one name="veryOldPerson" column="VERY_OLD_PERS_ID" class="Person" cascade="all" fetch="join"/> + + </class> + +</hibernate-mapping> \ No newline at end of file Added: trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Person.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,136 @@ +package org.hibernate.test.jpa.fetch; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.ArrayList; + +/** + * Copied over from annotations test suite... + * + * @author Emmanuel Bernard + */ +public class Person implements Serializable { + + // member declaration + private Long id; + private String firstName; + private String lastName; + private String companyName; + private Collection stays; + private Collection oldStays; + private Collection veryOldStays; + + // constructors + public Person() { + } + + public Person(String firstName, String lastName, String companyName) { + this.firstName = firstName; + this.lastName = lastName; + this.companyName = companyName; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + public Collection getStays() { + return stays; + } + + public void setStays(Collection stays) { + this.stays = stays; + } + + public Collection getOldStays() { + return oldStays; + } + + public void setOldStays(Collection oldStays) { + this.oldStays = oldStays; + } + + public Collection getVeryOldStays() { + return veryOldStays; + } + + public void setVeryOldStays(Collection veryOldStays) { + this.veryOldStays = veryOldStays; + } + + + // business logic + public void addStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) { + Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments ); + addStay( stay ); + } + + public void addStay(Stay stay) { + Collection stays = getStays(); + if ( stays == null ) { + stays = new ArrayList(); + } + stays.add( stay ); + + this.stays = stays; + } + + public void addOldStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) { + Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments ); + addOldStay( stay ); + } + + public void addOldStay(Stay stay) { + Collection stays = getOldStays(); + if ( stays == null ) { + stays = new ArrayList(); + } + stays.add( stay ); + + this.oldStays = stays; + } + + public void addVeryOldStay(Date startDate, Date endDate, String vessel, String authoriser, String comments) { + Stay stay = new Stay( this, startDate, endDate, vessel, authoriser, comments ); + addVeryOldStay( stay ); + } + + public void addVeryOldStay(Stay stay) { + Collection stays = getVeryOldStays(); + if ( stays == null ) { + stays = new ArrayList(); + } + stays.add( stay ); + + this.veryOldStays = stays; + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Stay.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Stay.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/fetch/Stay.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,107 @@ +package org.hibernate.test.jpa.fetch; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author Emmanuel Bernard + */ +public class Stay implements Serializable { + + // member declaration + private Long id; + private Person person; + private Person oldPerson; + private Person veryOldPerson; + private Date startDate; + private Date endDate; + private String vessel; + private String authoriser; + private String comments; + + + // constructors + public Stay() { + } + + public Stay(Person person, Date startDate, Date endDate, String vessel, String authoriser, String comments) { + this.authoriser = authoriser; + this.endDate = endDate; + this.person = person; + this.startDate = startDate; + this.vessel = vessel; + this.comments = comments; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + + public Person getOldPerson() { + return oldPerson; + } + + public void setOldPerson(Person oldPerson) { + this.oldPerson = oldPerson; + } + + public Person getVeryOldPerson() { + return veryOldPerson; + } + + public void setVeryOldPerson(Person veryOldPerson) { + this.veryOldPerson = veryOldPerson; + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public String getVessel() { + return vessel; + } + + public void setVessel(String vessel) { + this.vessel = vessel; + } + + public String getAuthoriser() { + return authoriser; + } + + public void setAuthoriser(String authoriser) { + this.authoriser = authoriser; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/lock/EJB3LockTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/lock/EJB3LockTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/lock/EJB3LockTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,179 @@ +package org.hibernate.test.jpa.lock; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.LockMode; +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.test.jpa.Item; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Tests specifically relating to section 3.3.5.3 [Lock Modes] of the + * JPA persistence specification (as of the <i>Proposed Final Draft</i>). + * + * @author Steve Ebersole + */ +public class EJB3LockTest extends AbstractJPATest { + public EJB3LockTest(String name) { + super( name ); + } + + public static Test suite() { + return new TestSuite( EJB3LockTest.class ); + } + + /** + * Test the equivalent of EJB3 LockModeType.READ + * <p/> + * From the spec: + * <p/> + * If transaction T1 calls lock(entity, LockModeType.READ) on a versioned object, the entity + * manager must ensure that neither of the following phenomena can occur:<ul> + * <li>P1 (Dirty read): Transaction T1 modifies a row. Another transaction T2 then reads that row and + * obtains the modified value, before T1 has committed or rolled back. Transaction T2 eventually + * commits successfully; it does not matter whether T1 commits or rolls back and whether it does + * so before or after T2 commits. + * <li>P2 (Non-repeatable read): Transaction T1 reads a row. Another transaction T2 then modifies or + * deletes that row, before T1 has committed. Both transactions eventually commit successfully. + * <p/> + * This will generally be achieved by the entity manager acquiring a lock on the underlying database row. + * Any such lock may be obtained immediately (so long as it is retained until commit completes), or the + * lock may be deferred until commit time (although even then it must be retained until the commit completes). + * Any implementation that supports repeatable reads in a way that prevents the above phenomena + * is permissible. + * <p/> + * The persistence implementation is not required to support calling lock(entity, LockMode-Type.READ) + * on a non-versioned object. When it cannot support such a lock call, it must throw the + * PersistenceException. When supported, whether for versioned or non-versioned objects, LockMode-Type.READ + * must always prevent the phenomena P1 and P2. Applications that call lock(entity, LockModeType.READ) + * on non-versioned objects will not be portable. + * <p/> + * Odd as it may sound, EJB3 LockModeType.READ actually maps to the Hibernate LockMode.UPGRADE + */ + public void testLockModeTypeRead() { + if ( ! readCommittedIsolationMaintained( "ejb3 lock tests" ) ) { + return; + } + + final String initialName = "lock test"; + // set up some test data + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Item item = new Item(); + item.setName( initialName ); + s1.save( item ); + t1.commit(); + s1.close(); + + Long itemId = item.getId(); + + // perform the isolated update + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + item = ( Item ) s1.get( Item.class, itemId ); + s1.lock( item, LockMode.UPGRADE ); + item.setName( "updated" ); + s1.flush(); + + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Item item2 = ( Item ) s2.get( Item.class, itemId ); + assertEquals( "isolation not maintained", initialName, item2.getName() ); + + t1.commit(); + s1.close(); + + item2 = ( Item ) s2.get( Item.class, itemId ); + assertEquals( "repeatable read not maintained", initialName, item2.getName() ); + t2.commit(); + s2.close(); + + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + s1.delete( item ); + t1.commit(); + s1.close(); + } + + /** + * Test the equivalent of EJB3 LockModeType.WRITE + * <p/> + * From the spec: + * <p/> + * If transaction T1 calls lock(entity, LockModeType.WRITE) on a versioned object, the entity + * manager must avoid the phenomena P1 and P2 (as with LockModeType.READ) and must also force + * an update (increment) to the entity's version column. A forced version update may be performed immediately, + * or may be deferred until a flush or commit. If an entity is removed before a deferred version + * update was to have been applied, the forced version update is omitted, since the underlying database + * row no longer exists. + * <p/> + * The persistence implementation is not required to support calling lock(entity, LockMode-Type.WRITE) + * on a non-versioned object. When it cannot support a such lock call, it must throw the + * PersistenceException. When supported, whether for versioned or non-versioned objects, LockMode-Type.WRITE + * must always prevent the phenomena P1 and P2. For non-versioned objects, whether or + * not LockModeType.WRITE has any additional behaviour is vendor-specific. Applications that call + * lock(entity, LockModeType.WRITE) on non-versioned objects will not be portable. + * <p/> + * Due to the requirement that LockModeType.WRITE needs to force a version increment, + * a new Hibernate LockMode was added to support this behavior: {@link org.hibernate.LockMode#FORCE}. + */ + public void testLockModeTypeWrite() { + if ( ! readCommittedIsolationMaintained( "ejb3 lock tests" ) ) { + return; + } + final String initialName = "lock test"; + // set up some test data + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Item item = new Item(); + item.setName( initialName ); + s1.save( item ); + t1.commit(); + s1.close(); + + Long itemId = item.getId(); + long initialVersion = item.getVersion(); + + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + item = ( Item ) s1.get( Item.class, itemId ); + s1.lock( item, LockMode.FORCE ); + assertEquals( "no forced version increment", initialVersion + 1, item.getVersion() ); + + s1.lock( item, LockMode.FORCE ); + assertEquals( "subsequent LockMode.FORCE did not no-op", initialVersion + 1, item.getVersion() ); + + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Item item2 = ( Item ) s2.get( Item.class, itemId ); + assertEquals( "isolation not maintained", initialName, item2.getName() ); + + item.setName( "updated-1" ); + s1.flush(); + // currently an unfortunate side effect... + assertEquals( initialVersion + 2, item.getVersion() ); + + t1.commit(); + s1.close(); + + item2.setName( "updated" ); + try { + t2.commit(); + fail( "optimisitc lock should have failed" ); + } + catch( Throwable ignore ) { + // expected behavior + t2.rollback(); + } + finally { + s2.close(); + } + + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + s1.delete( item ); + t1.commit(); + s1.close(); + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/lock/RepeatableReadTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/lock/RepeatableReadTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/lock/RepeatableReadTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,216 @@ +package org.hibernate.test.jpa.lock; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.LockMode; +import org.hibernate.StaleObjectStateException; +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.test.jpa.Item; +import org.hibernate.test.jpa.Part; +import junit.framework.Test; +import junit.framework.TestSuite; + +import java.math.BigDecimal; + +/** + * Test that the Hibernate Session complies with REPEATABLE_READ isolation + * semantics. + * + * @author Steve Ebersole + */ +public class RepeatableReadTest extends AbstractJPATest { + + public RepeatableReadTest(String name) { + super( name ); + } + + public static Test suite() { + return new TestSuite( org.hibernate.test.jpa.lock.RepeatableReadTest.class ); + } + + + // versioned entity tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public void testStaleVersionedInstanceFoundInQueryResult() { + String check = "EJB3 Specification"; + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Item item = new Item( check ); + s1.save( item ); + t1.commit(); + s1.close(); + + Long itemId = item.getId(); + long initialVersion = item.getVersion(); + + // Now, open a new Session and re-load the item... + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + item = ( Item ) s1.get( Item.class, itemId ); + + // now that the item is associated with the persistence-context of that session, + // open a new session and modify it "behind the back" of the first session + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Item item2 = ( Item ) s2.get( Item.class, itemId ); + item2.setName( "EJB3 Persistence Spec" ); + t2.commit(); + s2.close(); + + // at this point, s1 now contains stale data, so try an hql query which + // returns said item and make sure we get the previously associated state + // (i.e., the old name and the old version) + item2 = ( Item ) s1.createQuery( "select i from Item i" ).list().get( 0 ); + assertTrue( item == item2 ); + assertEquals( "encountered non-repeatable read", check, item2.getName() ); + assertEquals( "encountered non-repeatable read", initialVersion, item2.getVersion() ); + + // clean up + s1.createQuery( "delete Item" ).executeUpdate(); + t1.commit(); + s1.close(); + } + + public void testStaleVersionedInstanceFoundOnLock() { + if ( ! readCommittedIsolationMaintained( "repeatable read tests" ) ) { + return; + } + String check = "EJB3 Specification"; + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Item item = new Item( check ); + s1.save( item ); + t1.commit(); + s1.close(); + + Long itemId = item.getId(); + long initialVersion = item.getVersion(); + + // Now, open a new Session and re-load the item... + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + item = ( Item ) s1.get( Item.class, itemId ); + + // now that the item is associated with the persistence-context of that session, + // open a new session and modify it "behind the back" of the first session + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Item item2 = ( Item ) s2.get( Item.class, itemId ); + item2.setName( "EJB3 Persistence Spec" ); + t2.commit(); + s2.close(); + + // at this point, s1 now contains stale data, so acquire a READ lock + // and make sure we get the already associated state (i.e., the old + // name and the old version) + s1.lock( item, LockMode.READ ); + item2 = ( Item ) s1.get( Item.class, itemId ); + assertTrue( item == item2 ); + assertEquals( "encountered non-repeatable read", check, item2.getName() ); + assertEquals( "encountered non-repeatable read", initialVersion, item2.getVersion() ); + + // attempt to acquire an UPGRADE lock; this should fail + try { + s1.lock( item, LockMode.UPGRADE ); + fail( "expected UPGRADE lock failure" ); + } + catch( StaleObjectStateException expected ) { + // this is the expected behavior + } + + // clean up + s1.createQuery( "delete Item" ).executeUpdate(); + t1.commit(); + s1.close(); + } + + + // non-versioned entity tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public void testStaleNonVersionedInstanceFoundInQueryResult() { + String check = "Lock Modes"; + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Part part = new Part( new Item( "EJB3 Specification" ), check, "3.3.5.3", new BigDecimal( 0.0 ) ); + s1.save( part ); + t1.commit(); + s1.close(); + + Long partId = part.getId(); + + // Now, open a new Session and re-load the part... + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + part = ( Part ) s1.get( Part.class, partId ); + + // now that the item is associated with the persistence-context of that session, + // open a new session and modify it "behind the back" of the first session + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Part part2 = ( Part ) s2.get( Part.class, partId ); + part2.setName( "Lock Mode Types" ); + t2.commit(); + s2.close(); + + // at this point, s1 now contains stale data, so try an hql query which + // returns said part and make sure we get the previously associated state + // (i.e., the old name) + part2 = ( Part ) s1.createQuery( "select p from Part p" ).list().get( 0 ); + assertTrue( part == part2 ); + assertEquals( "encountered non-repeatable read", check, part2.getName() ); + + // clean up + s1.delete( part ); + t1.commit(); + s1.close(); + } + + public void testStaleNonVersionedInstanceFoundOnLock() { + if ( ! readCommittedIsolationMaintained( "repeatable read tests" ) ) { + return; + } + String check = "Lock Modes"; + Session s1 = getSessions().openSession(); + Transaction t1 = s1.beginTransaction(); + Part part = new Part( new Item( "EJB3 Specification" ), check, "3.3.5.3", new BigDecimal( 0.0 ) ); + s1.save( part ); + t1.commit(); + s1.close(); + + Long partId = part.getId(); + + // Now, open a new Session and re-load the part... + s1 = getSessions().openSession(); + t1 = s1.beginTransaction(); + part = ( Part ) s1.get( Part.class, partId ); + + // now that the item is associated with the persistence-context of that session, + // open a new session and modify it "behind the back" of the first session + Session s2 = getSessions().openSession(); + Transaction t2 = s2.beginTransaction(); + Part part2 = ( Part ) s2.get( Part.class, partId ); + part2.setName( "Lock Mode Types" ); + t2.commit(); + s2.close(); + + // at this point, s1 now contains stale data, so acquire a READ lock + // and make sure we get the already associated state (i.e., the old + // name and the old version) + s1.lock( part, LockMode.READ ); + part2 = ( Part ) s1.get( Part.class, partId ); + assertTrue( part == part2 ); + assertEquals( "encountered non-repeatable read", check, part2.getName() ); + + // then acquire an UPGRADE lock; this should fail + s1.lock( part, LockMode.UPGRADE ); + part2 = ( Part ) s1.get( Part.class, partId ); + assertTrue( part == part2 ); + assertEquals( "encountered non-repeatable read", check, part2.getName() ); + + // clean up +// s1.refresh( item ); + s1.delete( part ); + t1.commit(); + s1.close(); + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/package.html =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/package.html 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/package.html 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,9 @@ +<html> + <head></head> + <body> + <p> + Tests for any JPA-specific behavior for which we need to ensure + compliance. + </p> + </body> +</html> Added: trunk/Hibernate3/test/org/hibernate/test/jpa/proxy/JPAProxyTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/proxy/JPAProxyTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/proxy/JPAProxyTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,98 @@ +package org.hibernate.test.jpa.proxy; + +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.test.jpa.Item; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.Hibernate; +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.framework.AssertionFailedError; + +/** + * Test relation between proxies and get()/load() processing + * and make sure the interactions match the ejb3 expectations + * + * @author Steve Ebersole + */ +public class JPAProxyTest extends AbstractJPATest { + public JPAProxyTest(String name) { + super( name ); + } + + public static Test suite() { + return new TestSuite( JPAProxyTest.class ); + } + + public void testEjb3ProxyUsage() { + Session s = openSession(); + Transaction txn = s.beginTransaction(); + + Item item = ( Item ) s.load( Item.class, new Long(-1) ); + assertFalse( Hibernate.isInitialized( item ) ); + try { + Hibernate.initialize( item ); + fail( "proxy access did not fail on non-existent proxy" ); + } + catch ( EntityNotFoundException e ) { + // expected behavior + } + catch ( Throwable t ) { + fail( "unexpected exception type on non-existent proxy access : " + t ); + } + + s.clear(); + + Item item2 = ( Item ) s.load( Item.class, new Long(-1) ); + assertFalse( Hibernate.isInitialized( item2 ) ); + assertFalse( item == item2 ); + try { + item2.getName(); + fail( "proxy access did not fail on non-existent proxy" ); + } + catch ( EntityNotFoundException e ) { + // expected behavior + } + catch ( Throwable t ) { + fail( "unexpected exception type on non-existent proxy access : " + t ); + } + + txn.commit(); + s.close(); + } + + /** + * The ejb3 find() method maps to the Hibernate get() method + */ + public void testGetSemantics() { + Long nonExistentId = new Long( -1 ); + Session s = openSession(); + Transaction txn = s.beginTransaction(); + Item item = ( Item ) s.get( Item.class, nonExistentId ); + assertNull( "get() of non-existent entity did not return null", item ); + txn.commit(); + s.close(); + + s = openSession(); + txn = s.beginTransaction(); + // first load() it to generate a proxy... + item = ( Item ) s.load( Item.class, nonExistentId ); + assertFalse( Hibernate.isInitialized( item ) ); + // then try to get() it to make sure we get an exception + try { + s.get( Item.class, nonExistentId ); + fail( "force load did not fail on non-existent entity" ); + } + catch ( EntityNotFoundException e ) { + // expected behavior + } + catch( AssertionFailedError e ) { + throw e; + } + catch ( Throwable t ) { + fail( "unexpected exception type on non-existent entity force load : " + t ); + } + txn.commit(); + s.close(); + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,34 @@ +package org.hibernate.test.jpa.ql; + +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.Session; +import org.hibernate.hql.ast.QuerySyntaxException; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Tests for various JPAQL compliance issues + * + * @author Steve Ebersole + */ +public class JPAQLComplianceTest extends AbstractJPATest { + public JPAQLComplianceTest(String name) { + super( name ); + } + + public static Test suite() { + return new TestSuite( JPAQLComplianceTest.class ); + } + + public void testNoSelectClause() { + Session s = openSession(); + try { + s.createQuery( "from Part" ).list(); + fail( "expecting parse failure" ); + } + catch( QuerySyntaxException qe ) { + // expected behavior + } + s.close(); + } +} Added: trunk/Hibernate3/test/org/hibernate/test/jpa/removed/RemovedEntityTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/jpa/removed/RemovedEntityTest.java 2006-06-06 20:51:29 UTC (rev 9995) +++ trunk/Hibernate3/test/org/hibernate/test/jpa/removed/RemovedEntityTest.java 2006-06-07 15:30:02 UTC (rev 9996) @@ -0,0 +1,61 @@ +package org.hibernate.test.jpa.removed; + +import org.hibernate.test.jpa.AbstractJPATest; +import org.hibernate.test.jpa.Item; +import org.hibernate.Session; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * + * @author Steve Ebersole + */ +public class RemovedEntityTest extends AbstractJPATest { + public RemovedEntityTest(String name) { + super( name ); + } + + public static Test suite() { + return new TestSuite( RemovedEntityTest.class ); + } + + public void testRemoveThenContains() { + Session s = openSession(); + s.beginTransaction(); + Item item = new Item(); + item.setName( "dummy" ); + s.persist( item ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.delete( item ); + boolean contains = s.contains( item ); + s.getTransaction().commit(); + s.close(); + + assertFalse( "expecting removed entity to not be contained", contains ); + } + + public void testRemoveThenGet() { + Session s = openSession(); + s.beginTransaction(); + Item item = new Item(); + item.setName( "dummy" ); + s.persist( item ); + s.getTransaction().commit(); + s.close(); + + Long id = item.getId(); + + s = openSession(); + s.beginTransaction(); + s.delete( item ); + item = ( Item ) s.get( Item.class, id ); + s.getTransaction().commit(); + s.close(); + + assertNull( "expecting removed entity to be returned as null from get()", item ); + } +} |