HHH-2762 : new unit tests for SessionImplementor.getNonFlushedChanges()/applyNonFlushedChanges() implementation
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@17998 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
36100c8c3a
commit
01a1a251d7
|
@ -0,0 +1,130 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.ConnectionReleaseMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.engine.NonFlushedChanges;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.StatefulPersistenceContext;
|
||||
import org.hibernate.junit.functional.FunctionalTestCase;
|
||||
import org.hibernate.test.tm.ConnectionProviderImpl;
|
||||
import org.hibernate.test.tm.TransactionManagerLookupImpl;
|
||||
import org.hibernate.transaction.CMTTransactionFactory;
|
||||
import org.hibernate.util.SerializationHelper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public abstract class AbstractOperationTestCase extends FunctionalTestCase {
|
||||
private Map oldToNewEntityRefs = new HashMap();
|
||||
|
||||
public AbstractOperationTestCase(String name) {
|
||||
super( name );
|
||||
}
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
cfg.setProperty( Environment.CONNECTION_PROVIDER, ConnectionProviderImpl.class.getName() );
|
||||
cfg.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, TransactionManagerLookupImpl.class.getName() );
|
||||
cfg.setProperty( Environment.TRANSACTION_STRATEGY, CMTTransactionFactory.class.getName() );
|
||||
cfg.setProperty( Environment.AUTO_CLOSE_SESSION, "true" );
|
||||
cfg.setProperty( Environment.FLUSH_BEFORE_COMPLETION, "true" );
|
||||
cfg.setProperty( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.AFTER_STATEMENT.toString() );
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
}
|
||||
|
||||
public String[] getMappings() {
|
||||
return new String[] {
|
||||
"nonflushedchanges/Node.hbm.xml",
|
||||
"nonflushedchanges/Employer.hbm.xml",
|
||||
"nonflushedchanges/OptLockEntity.hbm.xml",
|
||||
"nonflushedchanges/OneToOne.hbm.xml",
|
||||
"nonflushedchanges/Competition.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
public String getCacheConcurrencyStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void clearCounts() {
|
||||
getSessions().getStatistics().clear();
|
||||
}
|
||||
|
||||
protected void assertInsertCount(int expected) {
|
||||
int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
|
||||
assertEquals( "unexpected insert count", expected, inserts );
|
||||
}
|
||||
|
||||
protected void assertUpdateCount(int expected) {
|
||||
int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
|
||||
assertEquals( "unexpected update counts", expected, updates );
|
||||
}
|
||||
|
||||
protected void assertDeleteCount(int expected) {
|
||||
int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
|
||||
assertEquals( "unexpected delete counts", expected, deletes );
|
||||
}
|
||||
|
||||
protected void assertFetchCount(int count) {
|
||||
int fetches = ( int ) getSessions().getStatistics().getEntityFetchCount();
|
||||
assertEquals( count, fetches );
|
||||
}
|
||||
|
||||
protected Session applyNonFlushedChangesToNewSessionCloseOldSession(Session oldSession) {
|
||||
NonFlushedChanges nfc = ( ( SessionImplementor ) oldSession ).getNonFlushedChanges();
|
||||
byte[] bytes = SerializationHelper.serialize( nfc );
|
||||
NonFlushedChanges nfc2 = ( NonFlushedChanges ) SerializationHelper.deserialize( bytes );
|
||||
Session newSession = openSession();
|
||||
( ( SessionImplementor ) newSession ).applyNonFlushedChanges( nfc2 );
|
||||
oldToNewEntityRefs.clear();
|
||||
for ( Iterator it = ( ( SessionImplementor ) oldSession ).getPersistenceContext()
|
||||
.getEntitiesByKey()
|
||||
.entrySet()
|
||||
.iterator(); it.hasNext(); ) {
|
||||
Map.Entry entry = ( Map.Entry ) it.next();
|
||||
EntityKey entityKey = ( EntityKey ) entry.getKey();
|
||||
Object oldEntityRef = entry.getValue();
|
||||
oldToNewEntityRefs.put(
|
||||
oldEntityRef, ( ( SessionImplementor ) newSession ).getPersistenceContext().getEntity( entityKey )
|
||||
);
|
||||
}
|
||||
for ( Iterator it = ( ( StatefulPersistenceContext ) ( ( SessionImplementor ) oldSession ).getPersistenceContext() )
|
||||
.getProxiesByKey()
|
||||
.entrySet()
|
||||
.iterator(); it.hasNext(); ) {
|
||||
Map.Entry entry = ( Map.Entry ) it.next();
|
||||
EntityKey entityKey = ( EntityKey ) entry.getKey();
|
||||
Object oldProxyRef = entry.getValue();
|
||||
oldToNewEntityRefs.put(
|
||||
oldProxyRef, ( ( SessionImplementor ) newSession ).getPersistenceContext().getProxy( entityKey )
|
||||
);
|
||||
}
|
||||
|
||||
oldSession.clear();
|
||||
oldSession.close();
|
||||
return newSession;
|
||||
}
|
||||
|
||||
protected void applyNonFlushedChangesToClearedSession(Session s) {
|
||||
NonFlushedChanges nfc = ( ( SessionImplementor ) s ).getNonFlushedChanges();
|
||||
byte[] bytes = SerializationHelper.serialize( nfc );
|
||||
NonFlushedChanges nfc2 = ( NonFlushedChanges ) SerializationHelper.deserialize( bytes );
|
||||
s.clear();
|
||||
( ( SessionImplementor ) s ).applyNonFlushedChanges( nfc2 );
|
||||
}
|
||||
|
||||
protected Map getOldToNewEntityRefMap() {
|
||||
return Collections.unmodifiableMap( oldToNewEntityRefs );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class Address implements Serializable {
|
||||
private Long id;
|
||||
private String streetAddress;
|
||||
private String city;
|
||||
private String country;
|
||||
private Person resident;
|
||||
|
||||
public Address() {
|
||||
}
|
||||
|
||||
public Address(String streetAddress, String city, String country, Person resident) {
|
||||
this.streetAddress = streetAddress;
|
||||
this.city = city;
|
||||
this.country = country;
|
||||
this.resident = resident;
|
||||
resident.setAddress( this );
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getStreetAddress() {
|
||||
return streetAddress;
|
||||
}
|
||||
|
||||
public void setStreetAddress(String streetAddress) {
|
||||
this.streetAddress = streetAddress;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public Person getResident() {
|
||||
return resident;
|
||||
}
|
||||
|
||||
public void setResident(Person resident) {
|
||||
this.resident = resident;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?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.nonflushedchanges">
|
||||
|
||||
<class name="Competition">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<list name="competitors" table="COMPET_ION_OR" cascade="persist,merge,delete">
|
||||
<key column="TION_ID"/>
|
||||
<list-index column="INDEX_COL"/>
|
||||
<many-to-many class="Competitor" column="TOR_ID"/>
|
||||
</list>
|
||||
</class>
|
||||
|
||||
<class name="Competitor">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="name"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
//$Id: $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class Competition implements Serializable {
|
||||
private Integer id;
|
||||
|
||||
private List competitors = new ArrayList();
|
||||
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List getCompetitors() {
|
||||
return competitors;
|
||||
}
|
||||
|
||||
public void setCompetitors(List competitors) {
|
||||
this.competitors = competitors;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//$Id: $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class Competitor implements Serializable {
|
||||
public Integer id;
|
||||
private String name;
|
||||
|
||||
|
||||
public Competitor() {
|
||||
}
|
||||
|
||||
public Competitor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
//$Id: CreateTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.PersistentObjectException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
|
||||
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class CreateTest extends AbstractOperationTestCase {
|
||||
|
||||
public CreateTest(String str) {
|
||||
super( str );
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new FunctionalTestClassTestSuite( CreateTest.class );
|
||||
}
|
||||
|
||||
public void testNoUpdatesOnCreateVersionedWithCollection() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
VersionedEntity root = new VersionedEntity( "root", "root" );
|
||||
VersionedEntity child = new VersionedEntity( "c1", "child-1" );
|
||||
root.getChildren().add( child );
|
||||
child.setParent( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.save( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( VersionedEntity ) getOldToNewEntityRefMap().get( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( VersionedEntity ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
assertDeleteCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( VersionedEntity ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertDeleteCount( 2 );
|
||||
}
|
||||
|
||||
public void testCreateTree() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
Node child = new Node( "child" );
|
||||
root.addChild( child );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
System.out.println( "getting" );
|
||||
root = ( Node ) s.get( Node.class, "root" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
Node child2 = new Node( "child2" );
|
||||
root.addChild( child2 );
|
||||
System.out.println( "committing" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
}
|
||||
|
||||
public void testCreateTreeWithGeneratedId() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root.addChild( child );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) s.get( NumberedNode.class, new Long( root.getId() ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
NumberedNode child2 = new NumberedNode( "child2" );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
root.addChild( child2 );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
}
|
||||
|
||||
public void testCreateException() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node dupe = new Node( "dupe" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
dupe = ( Node ) getOldToNewEntityRefMap().get( dupe );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
try {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertFalse( true );
|
||||
}
|
||||
catch ( ConstraintViolationException cve ) {
|
||||
//verify that an exception is thrown!
|
||||
}
|
||||
SimpleJtaTransactionManagerImpl.getInstance().rollback();
|
||||
|
||||
Node nondupe = new Node( "nondupe" );
|
||||
nondupe.addChild( dupe );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( nondupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
try {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertFalse( true );
|
||||
}
|
||||
catch ( ConstraintViolationException cve ) {
|
||||
//verify that an exception is thrown!
|
||||
}
|
||||
SimpleJtaTransactionManagerImpl.getInstance().rollback();
|
||||
}
|
||||
|
||||
public void testCreateExceptionWithGeneratedId() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode dupe = new NumberedNode( "dupe" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
dupe = ( NumberedNode ) getOldToNewEntityRefMap().get( dupe );
|
||||
s.persist( dupe );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
try {
|
||||
s.persist( dupe );
|
||||
assertFalse( true );
|
||||
}
|
||||
catch ( PersistentObjectException poe ) {
|
||||
//verify that an exception is thrown!
|
||||
}
|
||||
SimpleJtaTransactionManagerImpl.getInstance().rollback();
|
||||
|
||||
NumberedNode nondupe = new NumberedNode( "nondupe" );
|
||||
nondupe.addChild( dupe );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
try {
|
||||
s.persist( nondupe );
|
||||
assertFalse( true );
|
||||
}
|
||||
catch ( PersistentObjectException poe ) {
|
||||
//verify that an exception is thrown!
|
||||
}
|
||||
SimpleJtaTransactionManagerImpl.getInstance().rollback();
|
||||
}
|
||||
|
||||
public void testBasic() throws Exception {
|
||||
Session s;
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
Employer er = new Employer();
|
||||
Employee ee = new Employee();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.persist( ee );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
ee = ( Employee ) getOldToNewEntityRefMap().get( ee );
|
||||
Collection erColl = new ArrayList();
|
||||
Collection eeColl = new ArrayList();
|
||||
erColl.add( ee );
|
||||
eeColl.add( er );
|
||||
er.setEmployees( erColl );
|
||||
ee.setEmployers( eeColl );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
ee = ( Employee ) getOldToNewEntityRefMap().get( ee );
|
||||
er = ( Employer ) ee.getEmployers().iterator().next();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
er = ( Employer ) s.load( Employer.class, er.getId() );
|
||||
assertNotNull( er );
|
||||
assertFalse( Hibernate.isInitialized( er ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
er = ( Employer ) getOldToNewEntityRefMap().get( er );
|
||||
assertNotNull( er );
|
||||
assertFalse( Hibernate.isInitialized( er ) );
|
||||
assertNotNull( er.getEmployees() );
|
||||
assertEquals( 1, er.getEmployees().size() );
|
||||
Employee eeFromDb = ( Employee ) er.getEmployees().iterator().next();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
eeFromDb = ( Employee ) getOldToNewEntityRefMap().get( eeFromDb );
|
||||
assertEquals( ee.getId(), eeFromDb.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
|
||||
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class DeleteTest extends AbstractOperationTestCase {
|
||||
public DeleteTest(String name) {
|
||||
super( name );
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new FunctionalTestClassTestSuite( DeleteTest.class );
|
||||
}
|
||||
|
||||
public void testDeleteVersionedWithCollectionNoUpdate() throws Exception {
|
||||
// test adapted from HHH-1564...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity c = new VersionedEntity( "c1", "child-1" );
|
||||
VersionedEntity p = new VersionedEntity( "root", "root" );
|
||||
p.getChildren().add( c );
|
||||
c.setParent( p );
|
||||
s.save( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
VersionedEntity loadedParent = ( VersionedEntity ) s.get( VersionedEntity.class, "root" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
loadedParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( loadedParent );
|
||||
s.delete( loadedParent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 0 );
|
||||
assertDeleteCount( 2 );
|
||||
}
|
||||
|
||||
public void testNoUpdateOnDelete() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node node = new Node( "test" );
|
||||
s.persist( node );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.delete( node );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
}
|
||||
|
||||
public void testNoUpdateOnDeleteWithCollection() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node parent = new Node( "parent" );
|
||||
Node child = new Node( "child" );
|
||||
parent.getCascadingChildren().add( child );
|
||||
s.persist( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
parent = ( Node ) s.get( Node.class, "parent" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
parent = ( Node ) getOldToNewEntityRefMap().get( parent );
|
||||
s.delete( parent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
assertDeleteCount( 2 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//$Id: Employee.java 5686 2005-02-12 07:27:32Z steveebersole $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Employee in an Employer-Employee relationship
|
||||
*
|
||||
* @author Emmanuel Bernard, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
|
||||
public class Employee implements Serializable {
|
||||
private Integer id;
|
||||
private Collection employers;
|
||||
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer integer) {
|
||||
id = integer;
|
||||
}
|
||||
|
||||
|
||||
public Collection getEmployers() {
|
||||
return employers;
|
||||
}
|
||||
|
||||
public void setEmployers(Collection employers) {
|
||||
this.employers = employers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?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.nonflushedchanges">
|
||||
|
||||
<class name="Employer" polymorphism="explicit">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<version column="vers" name="vers"/>
|
||||
<bag name="employees"
|
||||
cascade="persist,merge"
|
||||
table="EMPLOYER_EMPLOYEE">
|
||||
<key column="EMPER_ID"/>
|
||||
<many-to-many class="Employee" column="EMPEE_ID"/>
|
||||
</bag>
|
||||
</class>
|
||||
|
||||
<class name="Employee" polymorphism="explicit">
|
||||
<id name="id">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<bag name="employers"
|
||||
inverse="true"
|
||||
cascade="persist,merge,save-update"
|
||||
table="EMPLOYER_EMPLOYEE">
|
||||
<key column="EMPEE_ID"/>
|
||||
<many-to-many class="Employer" column="EMPER_ID"/>
|
||||
</bag>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//$Id: Employer.java 8670 2005-11-25 17:36:29Z epbernard $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* Employer in a employer-Employee relationship
|
||||
*
|
||||
* @author Emmanuel Bernard, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
|
||||
public class Employer implements Serializable {
|
||||
private Integer id;
|
||||
private Collection employees;
|
||||
private Integer vers;
|
||||
|
||||
public Integer getVers() {
|
||||
return vers;
|
||||
}
|
||||
|
||||
public void setVers(Integer vers) {
|
||||
this.vers = vers;
|
||||
}
|
||||
|
||||
|
||||
public Collection getEmployees() {
|
||||
return employees;
|
||||
}
|
||||
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setEmployees(Collection set) {
|
||||
employees = set;
|
||||
}
|
||||
|
||||
public void setId(Integer integer) {
|
||||
id = integer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
//$Id: GetLoadTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
|
||||
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
|
||||
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class GetLoadTest extends AbstractOperationTestCase {
|
||||
|
||||
public GetLoadTest(String str) {
|
||||
super( str );
|
||||
}
|
||||
|
||||
public void testGetLoad() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Employer emp = new Employer();
|
||||
s.persist( emp );
|
||||
Node node = new Node( "foo" );
|
||||
Node parent = new Node( "bar" );
|
||||
parent.addChild( node );
|
||||
s.persist( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
emp = ( Employer ) s.get( Employer.class, emp.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
assertTrue( Hibernate.isInitialized( emp ) );
|
||||
assertFalse( Hibernate.isInitialized( emp.getEmployees() ) );
|
||||
node = ( Node ) s.get( Node.class, node.getName() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
node = ( Node ) getOldToNewEntityRefMap().get( node );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
assertTrue( Hibernate.isInitialized( node ) );
|
||||
assertFalse( Hibernate.isInitialized( node.getChildren() ) );
|
||||
assertFalse( Hibernate.isInitialized( node.getParent() ) );
|
||||
assertNull( s.get( Node.class, "xyz" ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
emp = ( Employer ) s.load( Employer.class, emp.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
emp.getId();
|
||||
assertFalse( Hibernate.isInitialized( emp ) );
|
||||
node = ( Node ) s.load( Node.class, node.getName() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
node = ( Node ) getOldToNewEntityRefMap().get( node );
|
||||
assertEquals( node.getName(), "foo" );
|
||||
assertFalse( Hibernate.isInitialized( node ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
emp = ( Employer ) s.get( "org.hibernate.test.nonflushedchanges.Employer", emp.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
assertTrue( Hibernate.isInitialized( emp ) );
|
||||
node = ( Node ) s.get( "org.hibernate.test.nonflushedchanges.Node", node.getName() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
node = ( Node ) getOldToNewEntityRefMap().get( node );
|
||||
assertTrue( Hibernate.isInitialized( node ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
emp = ( Employer ) s.load( "org.hibernate.test.nonflushedchanges.Employer", emp.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
emp.getId();
|
||||
assertFalse( Hibernate.isInitialized( emp ) );
|
||||
node = ( Node ) s.load( "org.hibernate.test.nonflushedchanges.Node", node.getName() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
|
||||
node = ( Node ) getOldToNewEntityRefMap().get( node );
|
||||
assertEquals( node.getName(), "foo" );
|
||||
assertFalse( Hibernate.isInitialized( node ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertFetchCount( 0 );
|
||||
}
|
||||
|
||||
public void testGetAfterDelete() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Employer emp = new Employer();
|
||||
s.persist( emp );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( emp );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
emp = ( Employer ) s.get( Employee.class, emp.getId() );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertNull( "get did not return null after delete", emp );
|
||||
}
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new FunctionalTestClassTestSuite( GetLoadTest.class );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,844 @@
|
|||
//$Id: MergeTest.java 11037 2007-01-09 16:04:16Z steve.ebersole@jboss.com $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.NonUniqueObjectException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
|
||||
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class MergeTest extends AbstractOperationTestCase {
|
||||
|
||||
public MergeTest(String str) {
|
||||
super( str );
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new FunctionalTestClassTestSuite( MergeTest.class );
|
||||
}
|
||||
|
||||
public void testMergeStaleVersionFails() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity entity = new VersionedEntity( "entity", "entity" );
|
||||
s.persist( entity );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
// make the detached 'entity' reference stale...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
VersionedEntity entity2 = ( VersionedEntity ) s.get( VersionedEntity.class, entity.getId() );
|
||||
entity2.setName( "entity-name" );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
// now try to reattch it
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
try {
|
||||
s.merge( entity );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
entity = ( VersionedEntity ) getOldToNewEntityRefMap().get( entity );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
fail( "was expecting staleness error" );
|
||||
}
|
||||
catch ( StaleObjectStateException expected ) {
|
||||
// expected outcome...
|
||||
}
|
||||
finally {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().rollback();
|
||||
}
|
||||
}
|
||||
|
||||
public void testMergeBidiPrimayKeyOneToOne() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Person p = new Person( "steve" );
|
||||
new PersonalDetails( "I have big feet", p );
|
||||
s.persist( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
p.getDetails().setSomePersonalDetail( p.getDetails().getSomePersonalDetail() + " and big hands too" );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
p = ( Person ) s.merge( p );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
p = ( Person ) getOldToNewEntityRefMap().get( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 1 );
|
||||
assertDeleteCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testMergeBidiForeignKeyOneToOne() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Person p = new Person( "steve" );
|
||||
Address a = new Address( "123 Main", "Austin", "US", p );
|
||||
s.persist( a );
|
||||
s.persist( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
p.getAddress().setStreetAddress( "321 Main" );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
p = ( Person ) s.merge( p );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 0 ); // no cascade
|
||||
assertDeleteCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( a );
|
||||
s.delete( p );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnMerge() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node node = new Node( "test" );
|
||||
s.persist( node );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// node is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
node = ( Node ) s.merge( node );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node while it is detached and
|
||||
// make sure we get an update as a result...
|
||||
node.setDescription( "new description" );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
node = ( Node ) s.merge( node );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 0 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnMergeWithCollection() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node parent = new Node( "parent" );
|
||||
Node child = new Node( "child" );
|
||||
parent.getChildren().add( child );
|
||||
child.setParent( parent );
|
||||
s.persist( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// parent is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
parent = ( Node ) s.merge( parent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node while it is detached and
|
||||
// make sure we get an update as a result...
|
||||
( ( Node ) parent.getChildren().iterator().next() ).setDescription( "child's new description" );
|
||||
parent.addChild( new Node( "second child" ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
parent = ( Node ) s.merge( parent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 1 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnMergeVersioned() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity entity = new VersionedEntity( "entity", "entity" );
|
||||
s.persist( entity );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// entity is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
VersionedEntity mergedEntity = ( VersionedEntity ) s.merge( entity );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
mergedEntity = ( VersionedEntity ) getOldToNewEntityRefMap().get( mergedEntity );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
assertEquals( "unexpected version increment", entity.getVersion(), mergedEntity.getVersion() );
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node while it is detached and
|
||||
// make sure we get an update as a result...
|
||||
entity.setName( "new name" );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
entity = ( VersionedEntity ) s.merge( entity );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 0 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnMergeVersionedWithCollection() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity parent = new VersionedEntity( "parent", "parent" );
|
||||
VersionedEntity child = new VersionedEntity( "child", "child" );
|
||||
parent.getChildren().add( child );
|
||||
child.setParent( parent );
|
||||
s.persist( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// parent is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
VersionedEntity mergedParent = ( VersionedEntity ) s.merge( parent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
mergedParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( mergedParent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
assertEquals( "unexpected parent version increment", parent.getVersion(), mergedParent.getVersion() );
|
||||
VersionedEntity mergedChild = ( VersionedEntity ) mergedParent.getChildren().iterator().next();
|
||||
assertEquals( "unexpected child version increment", child.getVersion(), mergedChild.getVersion() );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node while it is detached and
|
||||
// make sure we get an update as a result...
|
||||
mergedParent.setName( "new name" );
|
||||
mergedParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
parent = ( VersionedEntity ) s.merge( mergedParent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
parent = ( VersionedEntity ) getOldToNewEntityRefMap().get( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 1 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnPersistentMergeVersionedWithCollection() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity parent = new VersionedEntity( "parent", "parent" );
|
||||
VersionedEntity child = new VersionedEntity( "child", "child" );
|
||||
parent.getChildren().add( child );
|
||||
child.setParent( parent );
|
||||
s.persist( parent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// parent is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
// load parent so that merge will follow entityIsPersistent path
|
||||
VersionedEntity persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
persistentParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( persistentParent );
|
||||
// load children
|
||||
VersionedEntity persistentChild = ( VersionedEntity ) persistentParent.getChildren().iterator().next();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
persistentParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( persistentParent );
|
||||
VersionedEntity mergedParent = ( VersionedEntity ) s.merge( persistentParent ); // <-- This merge leads to failure
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
mergedParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( mergedParent );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
assertEquals( "unexpected parent version increment", parent.getVersion(), mergedParent.getVersion() );
|
||||
VersionedEntity mergedChild = ( VersionedEntity ) mergedParent.getChildren().iterator().next();
|
||||
assertEquals( "unexpected child version increment", child.getVersion(), mergedChild.getVersion() );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node once it is loaded and
|
||||
// make sure we get an update as a result...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
|
||||
persistentParent.setName( "new name" );
|
||||
persistentParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
persistentParent = ( VersionedEntity ) getOldToNewEntityRefMap().get( persistentParent );
|
||||
persistentParent = ( VersionedEntity ) s.merge( persistentParent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 1 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
// cleanup();
|
||||
}
|
||||
|
||||
public void testPersistThenMergeInSameTxnWithVersion() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
VersionedEntity entity = new VersionedEntity( "test", "test" );
|
||||
s.persist( entity );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.merge( new VersionedEntity( "test", "test-2" ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
|
||||
try {
|
||||
// control operation...
|
||||
s.saveOrUpdate( new VersionedEntity( "test", "test-3" ) );
|
||||
fail( "saveOrUpdate() should fail here" );
|
||||
}
|
||||
catch ( NonUniqueObjectException expected ) {
|
||||
// expected behavior
|
||||
}
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testPersistThenMergeInSameTxnWithTimestamp() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
TimestampedEntity entity = new TimestampedEntity( "test", "test" );
|
||||
s.persist( entity );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.merge( new TimestampedEntity( "test", "test-2" ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
|
||||
try {
|
||||
// control operation...
|
||||
s.saveOrUpdate( new TimestampedEntity( "test", "test-3" ) );
|
||||
fail( "saveOrUpdate() should fail here" );
|
||||
}
|
||||
catch ( NonUniqueObjectException expected ) {
|
||||
// expected behavior
|
||||
}
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeDeepTree() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
Node child = new Node( "child" );
|
||||
Node grandchild = new Node( "grandchild" );
|
||||
root.addChild( child );
|
||||
child.addChild( grandchild );
|
||||
s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
grandchild.setDescription( "the grand child" );
|
||||
Node grandchild2 = new Node( "grandchild2" );
|
||||
child.addChild( grandchild2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 1 );
|
||||
clearCounts();
|
||||
|
||||
Node child2 = new Node( "child2" );
|
||||
Node grandchild3 = new Node( "grandchild3" );
|
||||
child2.addChild( grandchild3 );
|
||||
root.addChild( child2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( grandchild );
|
||||
s.delete( grandchild2 );
|
||||
s.delete( grandchild3 );
|
||||
s.delete( child );
|
||||
s.delete( child2 );
|
||||
s.delete( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
}
|
||||
|
||||
public void testMergeDeepTreeWithGeneratedId() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
NumberedNode grandchild = new NumberedNode( "grandchild" );
|
||||
root.addChild( child );
|
||||
child.addChild( grandchild );
|
||||
root = ( NumberedNode ) s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
child = ( NumberedNode ) root.getChildren().iterator().next();
|
||||
grandchild = ( NumberedNode ) child.getChildren().iterator().next();
|
||||
grandchild.setDescription( "the grand child" );
|
||||
NumberedNode grandchild2 = new NumberedNode( "grandchild2" );
|
||||
child.addChild( grandchild2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 1 );
|
||||
clearCounts();
|
||||
|
||||
getSessions().evict( NumberedNode.class );
|
||||
|
||||
NumberedNode child2 = new NumberedNode( "child2" );
|
||||
NumberedNode grandchild3 = new NumberedNode( "grandchild3" );
|
||||
child2.addChild( grandchild3 );
|
||||
root.addChild( child2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.createQuery( "delete from NumberedNode where name like 'grand%'" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode where name like 'child%'" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode" ).executeUpdate();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
}
|
||||
|
||||
public void testMergeTree() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
Node child = new Node( "child" );
|
||||
root.addChild( child );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( Node ) root.getChildren().iterator().next();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
clearCounts();
|
||||
|
||||
root.setDescription( "The root node" );
|
||||
child.setDescription( "The child node" );
|
||||
|
||||
Node secondChild = new Node( "second child" );
|
||||
|
||||
root.addChild( secondChild );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.merge( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 2 );
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeTreeWithGeneratedId() throws Exception {
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root.addChild( child );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( NumberedNode ) root.getChildren().iterator().next();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
clearCounts();
|
||||
|
||||
root.setDescription( "The root node" );
|
||||
child.setDescription( "The child node" );
|
||||
|
||||
NumberedNode secondChild = new NumberedNode( "second child" );
|
||||
|
||||
root.addChild( secondChild );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.merge( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 2 );
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeManaged() throws Exception {
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
s.persist( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root = ( NumberedNode ) s.merge( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
root.addChild( child );
|
||||
assertSame( root, s.merge( root ) );
|
||||
Object mergedChild = root.getChildren().iterator().next();
|
||||
assertNotSame( mergedChild, child );
|
||||
assertTrue( s.contains( mergedChild ) );
|
||||
assertFalse( s.contains( child ) );
|
||||
assertEquals( root.getChildren().size(), 1 );
|
||||
assertTrue( root.getChildren().contains( mergedChild ) );
|
||||
//assertNotSame( mergedChild, s.merge(child) ); //yucky :(
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
mergedChild = root.getChildren().iterator().next();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
assertEquals( root.getChildren().size(), 1 );
|
||||
assertTrue( root.getChildren().contains( mergedChild ) );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( NumberedNode.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeManagedUninitializedCollection() throws Exception {
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
root.addChild( new NumberedNode( "child" ) );
|
||||
s.persist( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
NumberedNode newRoot = new NumberedNode( "root" );
|
||||
newRoot.setId( root.getId() );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.get( NumberedNode.class, root.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
Set managedChildren = root.getChildren();
|
||||
assertFalse( Hibernate.isInitialized( managedChildren ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
managedChildren = root.getChildren();
|
||||
newRoot.setChildren( managedChildren );
|
||||
assertSame( root, s.merge( newRoot ) );
|
||||
assertSame( managedChildren, root.getChildren() );
|
||||
assertFalse( Hibernate.isInitialized( managedChildren ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 0 );
|
||||
assertDeleteCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( NumberedNode.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeManagedInitializedCollection() throws Exception {
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
root.addChild( new NumberedNode( "child" ) );
|
||||
s.persist( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
NumberedNode newRoot = new NumberedNode( "root" );
|
||||
newRoot.setId( root.getId() );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.get( NumberedNode.class, root.getId() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
Set managedChildren = root.getChildren();
|
||||
Hibernate.initialize( managedChildren );
|
||||
assertTrue( Hibernate.isInitialized( managedChildren ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
managedChildren = root.getChildren();
|
||||
newRoot.setChildren( managedChildren );
|
||||
assertSame( root, s.merge( newRoot ) );
|
||||
assertSame( managedChildren, root.getChildren() );
|
||||
assertTrue( Hibernate.isInitialized( managedChildren ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 0 );
|
||||
assertDeleteCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( NumberedNode.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testRecursiveMergeTransient() throws Exception {
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Employer jboss = new Employer();
|
||||
Employee gavin = new Employee();
|
||||
jboss.setEmployees( new ArrayList() );
|
||||
jboss.getEmployees().add( gavin );
|
||||
s.merge( jboss );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
s.flush();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
jboss = ( Employer ) s.createQuery( "from Employer e join fetch e.employees" ).uniqueResult();
|
||||
assertTrue( Hibernate.isInitialized( jboss.getEmployees() ) );
|
||||
assertEquals( 1, jboss.getEmployees().size() );
|
||||
s.clear();
|
||||
s.merge( jboss.getEmployees().iterator().next() );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testDeleteAndMerge() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Employer jboss = new Employer();
|
||||
s.persist( jboss );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
Employer otherJboss;
|
||||
otherJboss = ( Employer ) s.get( Employer.class, jboss.getId() );
|
||||
s.delete( otherJboss );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
jboss.setVers( new Integer( 1 ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.merge( jboss );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void testMergeManyToManyWithCollectionDeference() throws Exception {
|
||||
// setup base data...
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Competition competition = new Competition();
|
||||
competition.getCompetitors().add( new Competitor( "Name" ) );
|
||||
competition.getCompetitors().add( new Competitor() );
|
||||
competition.getCompetitors().add( new Competitor() );
|
||||
s.persist( competition );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
// the competition graph is now detached:
|
||||
// 1) create a new List reference to represent the competitors
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
List newComp = new ArrayList();
|
||||
Competitor originalCompetitor = ( Competitor ) competition.getCompetitors().get( 0 );
|
||||
originalCompetitor.setName( "Name2" );
|
||||
newComp.add( originalCompetitor );
|
||||
newComp.add( new Competitor() );
|
||||
// 2) set that new List reference unto the Competition reference
|
||||
competition.setCompetitors( newComp );
|
||||
// 3) attempt the merge
|
||||
Competition competition2 = ( Competition ) s.merge( competition );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
Competition competition2copy = ( Competition ) getOldToNewEntityRefMap().get( competition2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertFalse( competition == competition2 );
|
||||
assertFalse( competition2 == competition2copy );
|
||||
assertFalse( competition.getCompetitors() == competition2.getCompetitors() );
|
||||
assertEquals( 2, competition2.getCompetitors().size() );
|
||||
assertEquals( 2, competition2copy.getCompetitors().size() );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
competition = ( Competition ) s.get( Competition.class, competition.getId() );
|
||||
assertEquals( 2, competition.getCompetitors().size() );
|
||||
s.delete( competition );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private void cleanup() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
s.createQuery( "delete from NumberedNode where parent is not null" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode" ).executeUpdate();
|
||||
|
||||
s.createQuery( "delete from Node where parent is not null" ).executeUpdate();
|
||||
s.createQuery( "delete from Node" ).executeUpdate();
|
||||
|
||||
s.createQuery( "delete from VersionedEntity where parent is not null" ).executeUpdate();
|
||||
s.createQuery( "delete from VersionedEntity" ).executeUpdate();
|
||||
s.createQuery( "delete from TimestampedEntity" ).executeUpdate();
|
||||
|
||||
s.createQuery( "delete from Competitor" ).executeUpdate();
|
||||
s.createQuery( "delete from Competition" ).executeUpdate();
|
||||
|
||||
Iterator itr = s.createQuery( "from Employer" ).list().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final Employer employer = ( Employer ) itr.next();
|
||||
s.delete( employer );
|
||||
}
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?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.nonflushedchanges">
|
||||
|
||||
<class name="Node" polymorphism="explicit">
|
||||
<id name="name">
|
||||
<generator class="assigned"/>
|
||||
</id>
|
||||
<property name="description"/>
|
||||
<many-to-one name="parent"/>
|
||||
<property name="created" not-null="true"/>
|
||||
<set name="children"
|
||||
inverse="true"
|
||||
cascade="persist,merge,save-update,evict">
|
||||
<key column="parent"/>
|
||||
<one-to-many class="Node"/>
|
||||
</set>
|
||||
<set name="cascadingChildren" inverse="false" cascade="persist,merge,save-update,evict,delete">
|
||||
<key column="CASC_PARENT"/>
|
||||
<one-to-many class="Node"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="NumberedNode" polymorphism="explicit">
|
||||
<id name="id" unsaved-value="0">
|
||||
<generator class="native"/>
|
||||
</id>
|
||||
<property name="name">
|
||||
<column name="name" index="iname" not-null="true"/>
|
||||
</property>
|
||||
<property name="description"/>
|
||||
<property name="created" not-null="true"
|
||||
type="imm_date"/>
|
||||
<many-to-one name="parent" class="NumberedNode"/>
|
||||
<set name="children"
|
||||
inverse="true"
|
||||
cascade="persist,merge,save-update">
|
||||
<key column="parent"/>
|
||||
<one-to-many class="NumberedNode"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
//$Id: Node.java 10759 2006-11-08 00:00:53Z steve.ebersole@jboss.com $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class Node implements Serializable {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private Date created;
|
||||
private Node parent;
|
||||
private Set children = new HashSet();
|
||||
private Set cascadingChildren = new HashSet();
|
||||
|
||||
public Node() {
|
||||
}
|
||||
|
||||
public Node(String name) {
|
||||
this.name = name;
|
||||
created = generateCurrentDate();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Node getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(Node parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Set getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public Node addChild(Node child) {
|
||||
children.add( child );
|
||||
child.setParent( this );
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set getCascadingChildren() {
|
||||
return cascadingChildren;
|
||||
}
|
||||
|
||||
public void setCascadingChildren(Set cascadingChildren) {
|
||||
this.cascadingChildren = cascadingChildren;
|
||||
}
|
||||
|
||||
private Date generateCurrentDate() {
|
||||
// Note : done as java.sql.Date mainly to work around issue with
|
||||
// MySQL and its lack of milli-second precision on its DATETIME
|
||||
// and TIMESTAMP datatypes.
|
||||
return new Date( new java.util.Date().getTime() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//$Id: NumberedNode.java 7236 2005-06-20 03:19:34Z oneovthafew $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class NumberedNode implements Serializable {
|
||||
|
||||
private long id;
|
||||
private String name;
|
||||
private NumberedNode parent;
|
||||
private Set children = new HashSet();
|
||||
private String description;
|
||||
private Date created;
|
||||
|
||||
public NumberedNode() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NumberedNode(String name) {
|
||||
this.name = name;
|
||||
created = new Date();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Set getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NumberedNode getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(NumberedNode parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public NumberedNode addChild(NumberedNode child) {
|
||||
children.add( child );
|
||||
child.setParent( this );
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC
|
||||
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
|
||||
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<!--
|
||||
Mappings demonstrating bidirectional one-to-one mappings for testing
|
||||
with various operations.
|
||||
|
||||
Person -> Address is modeled as a bidirectional one to one based on FK.
|
||||
Person -> Details is modeled as a bidirectional one to one based on PK.
|
||||
-->
|
||||
|
||||
<hibernate-mapping package="org.hibernate.test.nonflushedchanges">
|
||||
|
||||
<class name="Person" table="OPS_PERSON">
|
||||
<id name="id" column="ID" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
<one-to-one name="address" class="Address" property-ref="resident"/>
|
||||
<one-to-one name="details" class="PersonalDetails" cascade="all"/>
|
||||
</class>
|
||||
|
||||
<class name="Address" table="OPS_ADDRESS">
|
||||
<id name="id" column="ID" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="streetAddress" column="STREET" type="string"/>
|
||||
<property name="city" column="CITY" type="string"/>
|
||||
<property name="country" column="CTRY" type="string"/>
|
||||
<many-to-one name="resident" column="RESIDENT" class="Person"/>
|
||||
</class>
|
||||
|
||||
<class name="PersonalDetails" table="OPS_PERS_DETAIL">
|
||||
<id name="id" column="ID" type="long">
|
||||
<generator class="increment"/>
|
||||
</id>
|
||||
<property name="somePersonalDetail" column="SOME_DETAIL" type="string"/>
|
||||
<one-to-one name="person" class="Person" cascade="none" constrained="true"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
|
@ -0,0 +1,36 @@
|
|||
<?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.nonflushedchanges">
|
||||
|
||||
<class name="VersionedEntity" table="V_ENTITY">
|
||||
<id name="id" column="ID" type="string">
|
||||
<generator class="assigned"/>
|
||||
</id>
|
||||
<version name="version" column="VERS" type="long"/>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
<many-to-one name="parent" class="VersionedEntity"/>
|
||||
<set name="children"
|
||||
inverse="true"
|
||||
cascade="persist,merge,save-update,evict,delete">
|
||||
<key column="parent"/>
|
||||
<one-to-many class="VersionedEntity"/>
|
||||
</set>
|
||||
</class>
|
||||
|
||||
<class name="TimestampedEntity" table="T_ENTITY">
|
||||
<id name="id" column="ID" type="string">
|
||||
<generator class="assigned"/>
|
||||
</id>
|
||||
<timestamp name="timestamp" column="TS"/>
|
||||
<property name="name" column="NAME" type="string"/>
|
||||
</class>
|
||||
|
||||
</hibernate-mapping>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class Person implements Serializable {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Address address;
|
||||
private PersonalDetails details;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(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 Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public PersonalDetails getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(PersonalDetails details) {
|
||||
this.details = details;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class PersonalDetails implements Serializable {
|
||||
private Long id;
|
||||
private String somePersonalDetail;
|
||||
private Person person;
|
||||
|
||||
public PersonalDetails() {
|
||||
}
|
||||
|
||||
public PersonalDetails(String somePersonalDetail, Person person) {
|
||||
this.somePersonalDetail = somePersonalDetail;
|
||||
this.person = person;
|
||||
person.setDetails( this );
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSomePersonalDetail() {
|
||||
return somePersonalDetail;
|
||||
}
|
||||
|
||||
public void setSomePersonalDetail(String somePersonalDetail) {
|
||||
this.somePersonalDetail = somePersonalDetail;
|
||||
}
|
||||
|
||||
public Person getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public void setPerson(Person person) {
|
||||
this.person = person;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,492 @@
|
|||
//$Id: SaveOrUpdateTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $
|
||||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.intercept.FieldInterceptionHelper;
|
||||
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
|
||||
|
||||
/**
|
||||
* @author Gavin King, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class SaveOrUpdateTest extends AbstractOperationTestCase {
|
||||
|
||||
public SaveOrUpdateTest(String str) {
|
||||
super( str );
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateDeepTree() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
Node child = new Node( "child" );
|
||||
Node grandchild = new Node( "grandchild" );
|
||||
root.addChild( child );
|
||||
child.addChild( grandchild );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( Node ) getOldToNewEntityRefMap().get( child );
|
||||
grandchild = ( Node ) getOldToNewEntityRefMap().get( grandchild );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
grandchild.setDescription( "the grand child" );
|
||||
Node grandchild2 = new Node( "grandchild2" );
|
||||
child.addChild( grandchild2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 1 );
|
||||
clearCounts();
|
||||
|
||||
Node child2 = new Node( "child2" );
|
||||
Node grandchild3 = new Node( "grandchild3" );
|
||||
child2.addChild( grandchild3 );
|
||||
root.addChild( child2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( grandchild );
|
||||
s.delete( grandchild2 );
|
||||
s.delete( grandchild3 );
|
||||
s.delete( child );
|
||||
s.delete( child2 );
|
||||
s.delete( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateDeepTreeWithGeneratedId() throws Exception {
|
||||
boolean instrumented = FieldInterceptionHelper.isInstrumented( new NumberedNode() );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
NumberedNode grandchild = new NumberedNode( "grandchild" );
|
||||
root.addChild( child );
|
||||
child.addChild( grandchild );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( NumberedNode ) getOldToNewEntityRefMap().get( child );
|
||||
grandchild = ( NumberedNode ) getOldToNewEntityRefMap().get( grandchild );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 3 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
child = ( NumberedNode ) root.getChildren().iterator().next();
|
||||
grandchild = ( NumberedNode ) child.getChildren().iterator().next();
|
||||
grandchild.setDescription( "the grand child" );
|
||||
NumberedNode grandchild2 = new NumberedNode( "grandchild2" );
|
||||
child.addChild( grandchild2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( instrumented ? 1 : 3 );
|
||||
clearCounts();
|
||||
|
||||
NumberedNode child2 = new NumberedNode( "child2" );
|
||||
NumberedNode grandchild3 = new NumberedNode( "grandchild3" );
|
||||
child2.addChild( grandchild3 );
|
||||
root.addChild( child2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
assertUpdateCount( instrumented ? 0 : 4 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.createQuery( "delete from NumberedNode where name like 'grand%'" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode where name like 'child%'" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode" ).executeUpdate();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateTree() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
Node child = new Node( "child" );
|
||||
root.addChild( child );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( Node ) getOldToNewEntityRefMap().get( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
clearCounts();
|
||||
|
||||
root.setDescription( "The root node" );
|
||||
child.setDescription( "The child node" );
|
||||
|
||||
Node secondChild = new Node( "second child" );
|
||||
|
||||
root.addChild( secondChild );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.createQuery( "delete from Node where parent is not null" ).executeUpdate();
|
||||
s.createQuery( "delete from Node" ).executeUpdate();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateTreeWithGeneratedId() throws Exception {
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root.addChild( child );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( NumberedNode ) getOldToNewEntityRefMap().get( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 2 );
|
||||
clearCounts();
|
||||
|
||||
root.setDescription( "The root node" );
|
||||
child.setDescription( "The child node" );
|
||||
|
||||
NumberedNode secondChild = new NumberedNode( "second child" );
|
||||
|
||||
root.addChild( secondChild );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 2 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.createQuery( "delete from NumberedNode where parent is not null" ).executeUpdate();
|
||||
s.createQuery( "delete from NumberedNode" ).executeUpdate();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateManaged() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.get( NumberedNode.class, root.getId() );
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root.addChild( child );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
assertNull( getOldToNewEntityRefMap().get( child ) );
|
||||
s.flush();
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( NumberedNode ) getOldToNewEntityRefMap().get( child );
|
||||
child = ( NumberedNode ) root.getChildren().iterator().next();
|
||||
assertTrue( s.contains( child ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( NumberedNode ) getOldToNewEntityRefMap().get( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertTrue( root.getChildren().contains( child ) );
|
||||
assertEquals( root.getChildren().size(), 1 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( NumberedNode.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
s.delete( root );
|
||||
s.delete( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
|
||||
public void testSaveOrUpdateGot() throws Exception {
|
||||
boolean instrumented = FieldInterceptionHelper.isInstrumented( new NumberedNode() );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
NumberedNode root = new NumberedNode( "root" );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( instrumented ? 0 : 1 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( NumberedNode ) s.get( NumberedNode.class, new Long( root.getId() ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
Hibernate.initialize( root.getChildren() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
NumberedNode child = new NumberedNode( "child" );
|
||||
root.addChild( child );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( NumberedNode ) getOldToNewEntityRefMap().get( root );
|
||||
assertTrue( Hibernate.isInitialized( root.getChildren() ) );
|
||||
child = ( NumberedNode ) root.getChildren().iterator().next();
|
||||
assertTrue( s.contains( child ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( instrumented ? 0 : 1 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( NumberedNode.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
s.delete( root );
|
||||
s.delete( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testSaveOrUpdateGotWithMutableProp() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node root = new Node( "root" );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
assertUpdateCount( 0 );
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 0 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
root = ( Node ) s.get( Node.class, "root" );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
Hibernate.initialize( root.getChildren() );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
clearCounts();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
Node child = new Node( "child" );
|
||||
root.addChild( child );
|
||||
s.saveOrUpdate( root );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( Node ) root.getChildren().iterator().next();
|
||||
assertTrue( s.contains( child ) );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
root = ( Node ) getOldToNewEntityRefMap().get( root );
|
||||
child = ( Node ) getOldToNewEntityRefMap().get( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
assertInsertCount( 1 );
|
||||
//assertUpdateCount( 1 ); //note: will fail here if no second-level cache
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
assertEquals(
|
||||
s.createCriteria( Node.class )
|
||||
.setProjection( Projections.rowCount() )
|
||||
.uniqueResult(),
|
||||
new Long( 2 )
|
||||
);
|
||||
s.delete( root );
|
||||
s.delete( child );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void testEvictThenSaveOrUpdate() throws Exception {
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s = openSession();
|
||||
Node parent = new Node( "1:parent" );
|
||||
Node child = new Node( "2:child" );
|
||||
Node grandchild = new Node( "3:grandchild" );
|
||||
parent.addChild( child );
|
||||
child.addChild( grandchild );
|
||||
s.saveOrUpdate( parent );
|
||||
s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s1 = openSession();
|
||||
child = ( Node ) s1.load( Node.class, "2:child" );
|
||||
s1 = applyNonFlushedChangesToNewSessionCloseOldSession( s1 );
|
||||
child = ( Node ) getOldToNewEntityRefMap().get( child );
|
||||
assertTrue( s1.contains( child ) );
|
||||
assertFalse( Hibernate.isInitialized( child ) );
|
||||
assertTrue( s1.contains( child.getParent() ) );
|
||||
assertTrue( Hibernate.isInitialized( child ) );
|
||||
assertFalse( Hibernate.isInitialized( child.getChildren() ) );
|
||||
assertFalse( Hibernate.isInitialized( child.getParent() ) );
|
||||
assertTrue( s1.contains( child ) );
|
||||
s1 = applyNonFlushedChangesToNewSessionCloseOldSession( s1 );
|
||||
// child is an initialized proxy; after serialization, it is
|
||||
// the proxy is replaced by its implementation
|
||||
// TODO: find out if this is how this should work...
|
||||
child = ( Node ) getOldToNewEntityRefMap().get(
|
||||
( ( HibernateProxy ) child ).getHibernateLazyInitializer().getImplementation()
|
||||
);
|
||||
s1.evict( child );
|
||||
assertFalse( s1.contains( child ) );
|
||||
assertTrue( s1.contains( child.getParent() ) );
|
||||
|
||||
javax.transaction.Transaction tx1 = SimpleJtaTransactionManagerImpl.getInstance().suspend();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
Session s2 = openSession();
|
||||
s2.saveOrUpdate( child );
|
||||
s2 = applyNonFlushedChangesToNewSessionCloseOldSession( s2 );
|
||||
child = ( Node ) getOldToNewEntityRefMap().get( child );
|
||||
assertTrue( s2.contains( child ) );
|
||||
assertFalse( s1.contains( child ) );
|
||||
assertTrue( s2.contains( child.getParent() ) );
|
||||
assertFalse( s1.contains( child.getParent() ) );
|
||||
assertFalse( Hibernate.isInitialized( child.getChildren() ) );
|
||||
assertFalse( Hibernate.isInitialized( child.getParent() ) );
|
||||
assertEquals( 1, child.getChildren().size() );
|
||||
assertEquals( "1:parent", child.getParent().getName() );
|
||||
assertTrue( Hibernate.isInitialized( child.getChildren() ) );
|
||||
assertFalse( Hibernate.isInitialized( child.getParent() ) );
|
||||
assertNull( child.getParent().getDescription() );
|
||||
assertTrue( Hibernate.isInitialized( child.getParent() ) );
|
||||
s1 = applyNonFlushedChangesToNewSessionCloseOldSession( s1 );
|
||||
s2 = applyNonFlushedChangesToNewSessionCloseOldSession( s2 );
|
||||
|
||||
javax.transaction.Transaction tx2 = SimpleJtaTransactionManagerImpl.getInstance().suspend();
|
||||
SimpleJtaTransactionManagerImpl.getInstance().resume( tx1 );
|
||||
tx1.commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().resume( tx2 );
|
||||
tx2.commit();
|
||||
|
||||
SimpleJtaTransactionManagerImpl.getInstance().begin();
|
||||
s = openSession();
|
||||
s.delete( s.get( Node.class, "3:grandchild" ) );
|
||||
s.delete( s.get( Node.class, "2:child" ) );
|
||||
s.delete( s.get( Node.class, "1:parent" ) );
|
||||
SimpleJtaTransactionManagerImpl.getInstance().commit();
|
||||
}
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
super.configure( cfg );
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
}
|
||||
|
||||
public String[] getMappings() {
|
||||
return new String[] { "nonflushedchanges/Node.hbm.xml" };
|
||||
}
|
||||
|
||||
public static Test suite() {
|
||||
return new FunctionalTestClassTestSuite( SaveOrUpdateTest.class );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* todo: describe TimestampedEntity
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class TimestampedEntity implements Serializable {
|
||||
private String id;
|
||||
private String name;
|
||||
private Date timestamp;
|
||||
|
||||
public TimestampedEntity() {
|
||||
}
|
||||
|
||||
public TimestampedEntity(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Date getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Date timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package org.hibernate.test.nonflushedchanges;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* VersionedEntity
|
||||
*
|
||||
* @author Steve Ebersole, Gail Badner (adapted this from "ops" tests version)
|
||||
*/
|
||||
public class VersionedEntity implements Serializable {
|
||||
private String id;
|
||||
private String name;
|
||||
private long version;
|
||||
|
||||
private VersionedEntity parent;
|
||||
private Set children = new HashSet();
|
||||
|
||||
public VersionedEntity() {
|
||||
}
|
||||
|
||||
public VersionedEntity(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String 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 VersionedEntity getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(VersionedEntity parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Set getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(Set children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue