Move more tests to o.h.orm.test

This commit is contained in:
Andrea Boriero 2020-03-03 12:28:20 +00:00
parent 28f4d62f6a
commit a96096bc28
26 changed files with 1349 additions and 1279 deletions

View File

@ -0,0 +1,697 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.cascade.circle;
import java.util.Iterator;
import javax.persistence.PersistenceException;
import org.hibernate.JDBCException;
import org.hibernate.PropertyValueException;
import org.hibernate.Session;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Andrea Boriero
*/
public abstract class AbstractMultiPathCircleCascadeTest {
private interface EntityOperation {
boolean isLegacy();
Object doEntityOperation(Object entity, Session s);
}
private static EntityOperation MERGE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return false;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
return s.merge( entity );
}
};
private static EntityOperation SAVE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return true;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
s.save( entity );
return entity;
}
};
private static EntityOperation SAVE_UPDATE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return true;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
s.saveOrUpdate( entity );
return entity;
}
};
@Test
public void testMergeEntityWithNonNullableTransientEntity(SessionFactoryScope scope) {
testEntityWithNonNullableTransientEntity( scope, MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullableTransientEntity(SessionFactoryScope scope) {
testEntityWithNonNullableTransientEntity( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullableTransientEntity(SessionFactoryScope scope) {
testEntityWithNonNullableTransientEntity( scope, SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullableTransientEntity(SessionFactoryScope scope, EntityOperation operation) {
Route route = getUpdatedDetachedEntity( scope );
Node node = (Node) route.getNodes().iterator().next();
route.getNodes().remove( node );
Route routeNew = new Route();
routeNew.setName( "new route" );
routeNew.getNodes().add( node );
node.setRoute( routeNew );
scope.inSession(
session -> {
session.beginTransaction();
try {
operation.doEntityOperation( node, session );
session.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
session.getFactory().getSessionFactoryOptions().isCheckNullability(),
false,
operation.isLegacy()
);
}
finally {
session.getTransaction().rollback();
}
}
);
}
@Test
public void testMergeEntityWithNonNullableEntityNull(SessionFactoryScope scope) {
testEntityWithNonNullableEntityNull( scope, MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullableEntityNull(SessionFactoryScope scope) {
testEntityWithNonNullableEntityNull( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullableEntityNull(SessionFactoryScope scope) {
testEntityWithNonNullableEntityNull( scope, SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullableEntityNull(SessionFactoryScope scope, EntityOperation operation) {
Route route = getUpdatedDetachedEntity( scope );
Node node = (Node) route.getNodes().iterator().next();
route.getNodes().remove( node );
node.setRoute( null );
scope.inSession(
session -> {
session.beginTransaction();
try {
operation.doEntityOperation( node, session );
session.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
session.getFactory().getSessionFactoryOptions().isCheckNullability(),
true,
operation.isLegacy()
);
}
finally {
session.getTransaction().rollback();
}
}
);
}
@Test
public void testMergeEntityWithNonNullablePropSetToNull(SessionFactoryScope scope) {
testEntityWithNonNullablePropSetToNull( scope, MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullablePropSetToNull(SessionFactoryScope scope) {
testEntityWithNonNullablePropSetToNull( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullablePropSetToNull(SessionFactoryScope scope) {
testEntityWithNonNullablePropSetToNull( scope, SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullablePropSetToNull(SessionFactoryScope scope, EntityOperation operation) {
Route route = getUpdatedDetachedEntity( scope );
Node node = (Node) route.getNodes().iterator().next();
node.setName( null );
scope.inSession(
session -> {
session.beginTransaction();
try {
operation.doEntityOperation( route, session );
session.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
session.getFactory().getSessionFactoryOptions().isCheckNullability(),
true,
operation.isLegacy()
);
}
finally {
session.getTransaction().rollback();
}
}
);
}
@Test
public void testMergeRoute(SessionFactoryScope scope) {
testRoute( scope, MERGE_OPERATION );
}
// skip SAVE_OPERATION since Route is not transient
@Test
public void testSaveUpdateRoute(SessionFactoryScope scope) {
testRoute( scope, SAVE_UPDATE_OPERATION );
}
private void testRoute(SessionFactoryScope scope, EntityOperation operation) {
Route r = getUpdatedDetachedEntity( scope );
clearCounts( scope );
scope.inTransaction(
session ->
operation.doEntityOperation( r, session )
);
assertInsertCount( scope, 4 );
assertUpdateCount( scope, 1 );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
checkResults( route, true );
}
);
}
@Test
public void testMergePickupNode(SessionFactoryScope scope) {
testPickupNode( scope, MERGE_OPERATION );
}
@Test
public void testSavePickupNode(SessionFactoryScope scope) {
testPickupNode( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdatePickupNode(SessionFactoryScope scope) {
testPickupNode( scope, SAVE_UPDATE_OPERATION );
}
private void testPickupNode(SessionFactoryScope scope, EntityOperation operation) {
Route r = getUpdatedDetachedEntity( scope );
clearCounts( scope );
scope.inTransaction(
session -> {
Iterator it = r.getNodes().iterator();
Node node = (Node) it.next();
Node pickupNode;
if ( node.getName().equals( "pickupNodeB" ) ) {
pickupNode = node;
}
else {
node = (Node) it.next();
assertEquals( "pickupNodeB", node.getName() );
pickupNode = node;
}
operation.doEntityOperation( pickupNode, session );
}
);
assertInsertCount( scope, 4 );
assertUpdateCount( scope, 0 );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
checkResults( route, false );
}
);
}
@Test
public void testMergeDeliveryNode(SessionFactoryScope scope) {
testDeliveryNode( scope, MERGE_OPERATION );
}
@Test
public void testSaveDeliveryNode(SessionFactoryScope scope) {
testDeliveryNode( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateDeliveryNode(SessionFactoryScope scope) {
testDeliveryNode( scope, SAVE_UPDATE_OPERATION );
}
private void testDeliveryNode(SessionFactoryScope scope, EntityOperation operation) {
Route r = getUpdatedDetachedEntity( scope );
clearCounts( scope );
scope.inTransaction(
session -> {
Iterator it = r.getNodes().iterator();
Node node = (Node) it.next();
Node deliveryNode;
if ( node.getName().equals( "deliveryNodeB" ) ) {
deliveryNode = node;
}
else {
node = (Node) it.next();
assertEquals( "deliveryNodeB", node.getName() );
deliveryNode = node;
}
operation.doEntityOperation( deliveryNode, session );
}
);
assertInsertCount( scope, 4 );
assertUpdateCount( scope, 0 );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
checkResults( route, false );
}
);
}
@Test
public void testMergeTour(SessionFactoryScope scope) {
testTour( scope, MERGE_OPERATION );
}
@Test
public void testSaveTour(SessionFactoryScope scope) {
testTour( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateTour(SessionFactoryScope scope) {
testTour( scope, SAVE_UPDATE_OPERATION );
}
private void testTour(SessionFactoryScope scope, EntityOperation operation) {
Route r = getUpdatedDetachedEntity( scope );
clearCounts( scope );
scope.inTransaction(
session ->
operation.doEntityOperation( ( (Node) r.getNodes().toArray()[0] ).getTour(), session )
);
assertInsertCount( scope, 4 );
assertUpdateCount( scope, 0 );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
checkResults( route, false );
}
);
}
@Test
public void testMergeTransport(SessionFactoryScope scope) {
testTransport( scope, MERGE_OPERATION );
}
@Test
public void testSaveTransport(SessionFactoryScope scope) {
testTransport( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateTransport(SessionFactoryScope scope) {
testTransport( scope, SAVE_UPDATE_OPERATION );
}
private void testTransport(SessionFactoryScope scope, EntityOperation operation) {
Route r = getUpdatedDetachedEntity( scope );
clearCounts( scope );
scope.inTransaction(
session -> {
Transport transport;
Node node = ( (Node) r.getNodes().toArray()[0] );
if ( node.getPickupTransports().size() == 1 ) {
transport = (Transport) node.getPickupTransports().toArray()[0];
}
else {
transport = (Transport) node.getDeliveryTransports().toArray()[0];
}
operation.doEntityOperation( transport, session );
}
);
assertInsertCount( scope, 4 );
assertUpdateCount( scope, 0 );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
checkResults( route, false );
}
);
}
private Node getSimpleUpdatedDetachedEntity() {
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
return deliveryNode;
}
private Route getUpdatedDetachedEntity(SessionFactoryScope scope) {
Route route = new Route();
scope.inTransaction(
session -> {
route.setName( "routeA" );
session.save( route );
}
);
route.setName( "new routeA" );
route.setTransientField( "sfnaouisrbn" );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport = new Transport();
transport.setName( "transportB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.getPickupTransports().add( transport );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.getDeliveryTransports().add( transport );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
transport.setPickupNode( pickupNode );
transport.setDeliveryNode( deliveryNode );
transport.setTransientField( "aaaaaaaaaaaaaa" );
return route;
}
@AfterEach
public void cleanup(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from Transport" );
session.createQuery( "delete from Tour" );
session.createQuery( "delete from Node" );
session.createQuery( "delete from Route" );
}
);
}
private void checkResults(Route route, boolean isRouteUpdated) {
// since no cascaded to route, this method needs to
// know whether route is expected to be updated
if ( isRouteUpdated ) {
assertEquals( "new routeA", route.getName() );
}
assertEquals( 2, route.getNodes().size() );
Node deliveryNode = null;
Node pickupNode = null;
for ( Iterator it = route.getNodes().iterator(); it.hasNext(); ) {
Node node = (Node) it.next();
if ( "deliveryNodeB".equals( node.getName() ) ) {
deliveryNode = node;
}
else if ( "pickupNodeB".equals( node.getName() ) ) {
pickupNode = node;
}
else {
fail( "unknown node" );
}
}
assertNotNull( deliveryNode );
assertSame( route, deliveryNode.getRoute() );
assertEquals( 1, deliveryNode.getDeliveryTransports().size() );
assertEquals( 0, deliveryNode.getPickupTransports().size() );
assertNotNull( deliveryNode.getTour() );
assertEquals( "node original value", deliveryNode.getTransientField() );
assertNotNull( pickupNode );
assertSame( route, pickupNode.getRoute() );
assertEquals( 0, pickupNode.getDeliveryTransports().size() );
assertEquals( 1, pickupNode.getPickupTransports().size() );
assertNotNull( pickupNode.getTour() );
assertEquals( "node original value", pickupNode.getTransientField() );
assertTrue( !deliveryNode.getNodeID().equals( pickupNode.getNodeID() ) );
assertSame( deliveryNode.getTour(), pickupNode.getTour() );
assertSame(
deliveryNode.getDeliveryTransports().iterator().next(),
pickupNode.getPickupTransports().iterator().next()
);
Tour tour = deliveryNode.getTour();
Transport transport = (Transport) deliveryNode.getDeliveryTransports().iterator().next();
assertEquals( "tourB", tour.getName() );
assertEquals( 2, tour.getNodes().size() );
assertTrue( tour.getNodes().contains( deliveryNode ) );
assertTrue( tour.getNodes().contains( pickupNode ) );
assertEquals( "transportB", transport.getName() );
assertSame( deliveryNode, transport.getDeliveryNode() );
assertSame( pickupNode, transport.getPickupNode() );
assertEquals( "transport original value", transport.getTransientField() );
}
@Test
public void testMergeData3Nodes(SessionFactoryScope scope) {
testData3Nodes( scope, MERGE_OPERATION );
}
@Test
public void testSaveData3Nodes(SessionFactoryScope scope) {
testData3Nodes( scope, SAVE_OPERATION );
}
@Test
public void testSaveUpdateData3Nodes(SessionFactoryScope scope) {
testData3Nodes( scope, SAVE_UPDATE_OPERATION );
}
private void testData3Nodes(SessionFactoryScope scope, EntityOperation operation) {
Route r = new Route();
scope.inTransaction(
session -> {
r.setName( "routeA" );
session.save( r );
}
);
clearCounts( scope );
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
route.setName( "new routA" );
route.setTransientField( "sfnaouisrbn" );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport1 = new Transport();
transport1.setName( "TRANSPORT1" );
Transport transport2 = new Transport();
transport2.setName( "TRANSPORT2" );
Node node1 = new Node();
node1.setName( "NODE1" );
Node node2 = new Node();
node2.setName( "NODE2" );
Node node3 = new Node();
node3.setName( "NODE3" );
node1.setRoute( route );
node1.setTour( tour );
node1.getPickupTransports().add( transport1 );
node1.setTransientField( "node 1" );
node2.setRoute( route );
node2.setTour( tour );
node2.getDeliveryTransports().add( transport1 );
node2.getPickupTransports().add( transport2 );
node2.setTransientField( "node 2" );
node3.setRoute( route );
node3.setTour( tour );
node3.getDeliveryTransports().add( transport2 );
node3.setTransientField( "node 3" );
tour.getNodes().add( node1 );
tour.getNodes().add( node2 );
tour.getNodes().add( node3 );
route.getNodes().add( node1 );
route.getNodes().add( node2 );
route.getNodes().add( node3 );
transport1.setPickupNode( node1 );
transport1.setDeliveryNode( node2 );
transport1.setTransientField( "aaaaaaaaaaaaaa" );
transport2.setPickupNode( node2 );
transport2.setDeliveryNode( node3 );
transport2.setTransientField( "bbbbbbbbbbbbb" );
operation.doEntityOperation( route, session );
}
);
assertInsertCount( scope, 6 );
assertUpdateCount( scope, 1 );
}
protected void checkExceptionFromNullValueForNonNullable(
Exception ex, boolean checkNullability, boolean isNullValue, boolean isLegacy
) {
if ( isNullValue ) {
if ( checkNullability ) {
if ( isLegacy ) {
assertTyping( PropertyValueException.class, ex );
}
else {
assertTyping( PersistenceException.class, ex );
}
}
else {
assertTrue( ( ex instanceof JDBCException ) || ( ex.getCause() instanceof JDBCException ) );
}
}
else {
if ( isLegacy ) {
assertTyping( TransientPropertyValueException.class, ex );
}
else {
assertTyping( IllegalStateException.class, ex );
}
}
}
protected void clearCounts(SessionFactoryScope scope) {
scope.getSessionFactory().getStatistics().clear();
}
protected void assertInsertCount(SessionFactoryScope scope, int expected) {
int inserts = (int) scope.getSessionFactory().getStatistics().getEntityInsertCount();
assertEquals( expected, inserts, "unexpected insert count" );
}
protected void assertUpdateCount(SessionFactoryScope scope, int expected) {
int updates = (int) scope.getSessionFactory().getStatistics().getEntityUpdateCount();
assertEquals( expected, updates, "unexpected update counts" );
}
protected void assertDeleteCount(SessionFactoryScope scope, int expected) {
int deletes = (int) scope.getSessionFactory().getStatistics().getEntityDeleteCount();
assertEquals( expected, deletes, "unexpected delete counts" );
}
}

View File

@ -7,7 +7,7 @@
-->
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.hibernate.test.cascade.circle">
<hibernate-mapping package="org.hibernate.orm.test.cascade.circle">
<class name="Route" table="HB_Route">

View File

@ -0,0 +1,170 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* The test case uses the following model:
* <p>
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* <- -> | | |
* Route -- (1 : N) -- Node Transport
* | <- -> |
* -- (1 : N) -- (delivery) --
* <p>
* Arrows indicate the direction of cascade-merge, cascade-save, cascade-refresh and cascade-save-or-update
* <p>
* It reproduces the following issues:
* https://hibernate.atlassian.net/browse/HHH-9512
* <p/>
* This tests that cascades are done properly from each entity.
*
* @author Alex Belyaev (based on code by Pavol Zibrita and Gail Badner)
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/CascadeManagedAndTransient.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = @ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "true")
)
public class CascadeManagedAndTransientTest {
@AfterEach
public void cleanupTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from Transport" );
session.createQuery( "delete from Tour" );
session.createQuery( "delete from Node" );
session.createQuery( "delete from Route" );
session.createQuery( "delete from Vehicle" );
}
);
}
@Test
public void testAttachedChildInMerge(SessionFactoryScope scope) {
fillInitialData( scope );
scope.inTransaction(
session -> {
Route route = (Route) session.createQuery( "FROM Route WHERE name = :name" )
.setParameter( "name", "Route 1" )
.uniqueResult();
Node n2 = (Node) session.createQuery( "FROM Node WHERE name = :name" )
.setParameter( "name", "Node 2" )
.uniqueResult();
Node n3 = (Node) session.createQuery( "FROM Node WHERE name = :name" )
.setParameter( "name", "Node 3" )
.uniqueResult();
Vehicle vehicle = new Vehicle();
vehicle.setName( "Bus" );
vehicle.setRoute( route );
Transport $2to3 = new Transport();
$2to3.setName( "Transport 2 -> 3" );
$2to3.setPickupNode( n2 );
n2.getPickupTransports().add( $2to3 );
$2to3.setDeliveryNode( n3 );
n3.getDeliveryTransports().add( $2to3 );
$2to3.setVehicle( vehicle );
vehicle.setTransports( new HashSet<Transport>( Arrays.asList( $2to3 ) ) );
// Try to save graph of transient entities (vehicle, transport) which contains attached entities (node2, node3)
Vehicle managedVehicle = (Vehicle) session.merge( vehicle );
checkNewVehicle( managedVehicle );
session.flush();
session.clear();
assertEquals( 3, session.createQuery( "FROM Transport" ).list().size() );
assertEquals( 2, session.createQuery( "FROM Vehicle" ).list().size() );
assertEquals( 4, session.createQuery( "FROM Node" ).list().size() );
Vehicle newVehicle = (Vehicle) session.createQuery( "FROM Vehicle WHERE name = :name" )
.setParameter( "name", "Bus" )
.uniqueResult();
checkNewVehicle( newVehicle );
}
);
}
private void checkNewVehicle(Vehicle newVehicle) {
assertEquals( "Bus", newVehicle.getName() );
assertEquals( 1, newVehicle.getTransports().size() );
Transport t = (Transport) newVehicle.getTransports().iterator().next();
assertEquals( "Transport 2 -> 3", t.getName() );
assertEquals( "Node 2", t.getPickupNode().getName() );
assertEquals( "Node 3", t.getDeliveryNode().getName() );
}
private void fillInitialData(SessionFactoryScope scope) {
Tour tour = new Tour();
tour.setName( "Tour 1" );
Route route = new Route();
route.setName( "Route 1" );
ArrayList<Node> nodes = new ArrayList<Node>();
for ( int i = 0; i < 4; i++ ) {
Node n = new Node();
n.setName( "Node " + i );
n.setTour( tour );
n.setRoute( route );
nodes.add( n );
}
tour.setNodes( new HashSet<>( nodes ) );
route.setNodes( new HashSet<>( Arrays.asList( nodes.get( 0 ), nodes.get( 1 ), nodes.get( 2 ) ) ) );
Vehicle vehicle = new Vehicle();
vehicle.setName( "Car" );
route.setVehicles( new HashSet<>( Arrays.asList( vehicle ) ) );
vehicle.setRoute( route );
Transport $0to1 = new Transport();
$0to1.setName( "Transport 0 -> 1" );
$0to1.setPickupNode( nodes.get( 0 ) );
$0to1.setDeliveryNode( nodes.get( 1 ) );
$0to1.setVehicle( vehicle );
Transport $1to2 = new Transport();
$1to2.setName( "Transport 1 -> 2" );
$1to2.setPickupNode( nodes.get( 1 ) );
$1to2.setDeliveryNode( nodes.get( 2 ) );
$1to2.setVehicle( vehicle );
vehicle.setTransports( new HashSet<>( Arrays.asList( $0to1, $1to2 ) ) );
scope.inTransaction(
session ->
session.persist( tour )
);
}
}

View File

@ -7,7 +7,7 @@
-->
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.hibernate.test.cascade.circle">
<hibernate-mapping package="org.hibernate.orm.test.cascade.circle">
<class name="Route" table="HB_Route">

View File

@ -0,0 +1,261 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
/**
* The test case uses the following model:
* <p>
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* -> | | |
* Route -- (1 : N) - Node Transport
* | | <- -> | |
* | -- (1 : N) -- (delivery) -- |
* | |
* | -> -> |
* -------- (1 : N) ---- Vehicle--(1 : N)------------
* <p>
* Arrows indicate the direction of cascade-merge.
* <p/>
* I believe it reproduces the following issue:
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3544
*
* @author Gail Badner (based on original model provided by Pavol Zibrita)
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = @ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "true")
)
public class CascadeMergeToChildBeforeParentTest {
@AfterEach
public void cleanupTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "delete from Transport" );
session.createQuery( "delete from Tour" );
session.createQuery( "delete from Node" );
session.createQuery( "delete from Route" );
session.createQuery( "delete from Vehicle" );
}
);
}
@Test
public void testMerge(SessionFactoryScope scope) {
Route r = new Route();
scope.inTransaction(
session -> {
r.setName( "routeA" );
session.save( r );
}
);
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
route.setTransientField( "sfnaouisrbn" );
Tour tour = new Tour();
tour.setName( "tourB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
session.merge( route );
}
);
}
@Test
public void testMergeTransientChildBeforeTransientParent(SessionFactoryScope scope) {
// This test fails because the merge algorithm tries to save a
// transient child (transport) before cascade-merge gets its
// transient parent (vehicle); merge does not cascade from the
// child to the parent.
Route r = new Route();
scope.inTransaction(
session -> {
r.setName( "routeA" );
session.save( r );
}
);
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
route.setTransientField( "sfnaouisrbn" );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport = new Transport();
transport.setName( "transportB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
Vehicle vehicle = new Vehicle();
vehicle.setName( "vehicleB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.getPickupTransports().add( transport );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.getDeliveryTransports().add( transport );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
route.getVehicles().add( vehicle );
transport.setPickupNode( pickupNode );
transport.setDeliveryNode( deliveryNode );
transport.setVehicle( vehicle );
transport.setTransientField( "aaaaaaaaaaaaaa" );
vehicle.getTransports().add( transport );
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
session.merge( route );
}
);
}
@Test
public void testMergeData3Nodes(SessionFactoryScope scope) {
Route r = new Route();
scope.inTransaction(
session -> {
r.setName( "routeA" );
session.save( r );
}
);
scope.inTransaction(
session -> {
Route route = session.get( Route.class, r.getRouteID() );
route.setTransientField( "sfnaouisrbn" );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport1 = new Transport();
transport1.setName( "TRANSPORT1" );
Transport transport2 = new Transport();
transport2.setName( "TRANSPORT2" );
Node node1 = new Node();
node1.setName( "NODE1" );
Node node2 = new Node();
node2.setName( "NODE2" );
Node node3 = new Node();
node3.setName( "NODE3" );
Vehicle vehicle = new Vehicle();
vehicle.setName( "vehicleB" );
node1.setRoute( route );
node1.setTour( tour );
node1.getPickupTransports().add( transport1 );
node1.setTransientField( "node 1" );
node2.setRoute( route );
node2.setTour( tour );
node2.getDeliveryTransports().add( transport1 );
node2.getPickupTransports().add( transport2 );
node2.setTransientField( "node 2" );
node3.setRoute( route );
node3.setTour( tour );
node3.getDeliveryTransports().add( transport2 );
node3.setTransientField( "node 3" );
tour.getNodes().add( node1 );
tour.getNodes().add( node2 );
tour.getNodes().add( node3 );
route.getNodes().add( node1 );
route.getNodes().add( node2 );
route.getNodes().add( node3 );
route.getVehicles().add( vehicle );
transport1.setPickupNode( node1 );
transport1.setDeliveryNode( node2 );
transport1.setVehicle( vehicle );
transport1.setTransientField( "aaaaaaaaaaaaaa" );
transport2.setPickupNode( node2 );
transport2.setDeliveryNode( node3 );
transport2.setVehicle( vehicle );
transport2.setTransientField( "bbbbbbbbbbbbb" );
vehicle.getTransports().add( transport1 );
vehicle.getTransports().add( transport2 );
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
session.merge( route );
}
);
}
}

View File

@ -7,7 +7,7 @@
-->
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.hibernate.test.cascade.circle">
<hibernate-mapping package="org.hibernate.orm.test.cascade.circle">
<class name="Route" table="HB_Route">

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* @author Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
@ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "false")
}
)
public class MultiPathCircleCascadeCheckNullFalseDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* @author Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
@ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "true")
}
)
public class MultiPathCircleCascadeCheckNullTrueDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* @author Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
@ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "false")
}
)
public class MultiPathCircleCascadeCheckNullibilityFalseTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* @author Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
@ServiceRegistry.Setting(name = Environment.CHECK_NULLABILITY, value = "true")
}
)
public class MultiPathCircleCascadeCheckNullibilityTrueTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -7,7 +7,7 @@
-->
<!DOCTYPE hibernate-mapping SYSTEM "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.hibernate.test.cascade.circle">
<hibernate-mapping package="org.hibernate.orm.test.cascade.circle">
<class name="Route" table="HB_Route">

View File

@ -0,0 +1,31 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* @author Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
}
)
public class MultiPathCircleCascadeDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cascade.circle;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
/**
* The test case uses the following model:
* <p>
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* -> | | |
* Route -- (1 : N) -- Node Transport
* | <- -> |
* -- (1 : N) -- (delivery) --
* <p>
* Arrows indicate the direction of cascade-merge, cascade-save, and cascade-save-or-update
* <p>
* It reproduced the following issues:
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3046
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3810
* <p/>
* This tests that cascades are done properly from each entity.
*
* @author Pavol Zibrita, Gail Badner
*/
@DomainModel(
xmlMappings = {
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
}
)
@SessionFactory
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting(name = Environment.GENERATE_STATISTICS, value = "true"),
@ServiceRegistry.Setting(name = Environment.STATEMENT_BATCH_SIZE, value = "0"),
}
)
public class MultiPathCircleCascadeTest extends AbstractMultiPathCircleCascadeTest {
}

View File

@ -4,11 +4,9 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
//$Id: $
package org.hibernate.orm.test.cascade.circle;
package org.hibernate.test.cascade.circle;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@ -41,7 +39,7 @@ public class Node {
// @ManyToOne(targetEntity=Tour.class, cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, optional=true, fetch=FetchType.LAZY)
// @JoinColumn(name="TOURID", nullable=true, insertable=true, updatable=true)
private Tour tour;
// @Transient
private String transientField = "node original value";

View File

@ -6,9 +6,8 @@
*/
//$Id: $
package org.hibernate.orm.test.cascade.circle;
package org.hibernate.test.cascade.circle;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

View File

@ -7,7 +7,8 @@
//$Id: $
package org.hibernate.test.cascade.circle;
package org.hibernate.orm.test.cascade.circle;
import java.util.HashSet;
import java.util.Set;

View File

@ -6,11 +6,7 @@
*/
//$Id: $
package org.hibernate.test.cascade.circle;
package org.hibernate.orm.test.cascade.circle;
public class Transport {
@ -34,7 +30,7 @@ public class Transport {
private Node deliveryNode = null;
private Vehicle vehicle;
// @Transient
private String transientField = "transport original value";

View File

@ -7,8 +7,8 @@
//$Id: $
package org.hibernate.orm.test.cascade.circle;
package org.hibernate.test.cascade.circle;
import java.util.HashSet;
import java.util.Set;

View File

@ -1,164 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* The test case uses the following model:
*
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* <- -> | | |
* Route -- (1 : N) -- Node Transport
* | <- -> |
* -- (1 : N) -- (delivery) --
*
* Arrows indicate the direction of cascade-merge, cascade-save, cascade-refresh and cascade-save-or-update
*
* It reproduces the following issues:
* https://hibernate.atlassian.net/browse/HHH-9512
* <p/>
* This tests that cascades are done properly from each entity.
*
* @author Alex Belyaev (based on code by Pavol Zibrita and Gail Badner)
*/
public class CascadeManagedAndTransientTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {
return new String[] {
"cascade/circle/CascadeManagedAndTransient.hbm.xml"
};
}
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "true" );
}
@Override
protected void cleanupTest() {
Session s = openSession();
s.beginTransaction();
s.createQuery( "delete from Transport" );
s.createQuery( "delete from Tour" );
s.createQuery( "delete from Node" );
s.createQuery( "delete from Route" );
s.createQuery( "delete from Vehicle" );
}
private void checkNewVehicle(Vehicle newVehicle) {
assertEquals("Bus", newVehicle.getName());
assertEquals(1, newVehicle.getTransports().size());
Transport t = (Transport) newVehicle.getTransports().iterator().next();
assertEquals("Transport 2 -> 3", t.getName());
assertEquals("Node 2", t.getPickupNode().getName());
assertEquals("Node 3", t.getDeliveryNode().getName());
}
@Test
public void testAttachedChildInMerge() {
fillInitialData();
Session s = openSession();
s.beginTransaction();
Route route = (Route) s.createQuery("FROM Route WHERE name = :name").setParameter("name", "Route 1").uniqueResult();
Node n2 = (Node) s.createQuery("FROM Node WHERE name = :name").setParameter("name", "Node 2").uniqueResult();
Node n3 = (Node) s.createQuery("FROM Node WHERE name = :name").setParameter("name", "Node 3").uniqueResult();
Vehicle vehicle = new Vehicle();
vehicle.setName("Bus");
vehicle.setRoute(route);
Transport $2to3 = new Transport();
$2to3.setName("Transport 2 -> 3");
$2to3.setPickupNode(n2); n2.getPickupTransports().add($2to3);
$2to3.setDeliveryNode(n3); n3.getDeliveryTransports().add($2to3);
$2to3.setVehicle(vehicle);
vehicle.setTransports(new HashSet<Transport>(Arrays.asList($2to3)));
// Try to save graph of transient entities (vehicle, transport) which contains attached entities (node2, node3)
Vehicle managedVehicle = (Vehicle) s.merge(vehicle);
checkNewVehicle(managedVehicle);
s.flush();
s.clear();
assertEquals(3, s.createQuery("FROM Transport").list().size());
assertEquals(2, s.createQuery("FROM Vehicle").list().size());
assertEquals(4, s.createQuery("FROM Node").list().size());
Vehicle newVehicle = (Vehicle) s.createQuery("FROM Vehicle WHERE name = :name").setParameter("name", "Bus").uniqueResult();
checkNewVehicle(newVehicle);
s.getTransaction().commit();
s.close();
}
private void fillInitialData() {
Tour tour = new Tour();
tour.setName("Tour 1");
Route route = new Route();
route.setName("Route 1");
ArrayList<Node> nodes = new ArrayList<Node>();
for (int i = 0; i < 4; i++) {
Node n = new Node();
n.setName("Node " + i);
n.setTour(tour);
n.setRoute(route);
nodes.add(n);
}
tour.setNodes(new HashSet<Node>(nodes));
route.setNodes(new HashSet<Node>(Arrays.asList(nodes.get(0), nodes.get(1), nodes.get(2))));
Vehicle vehicle = new Vehicle();
vehicle.setName("Car");
route.setVehicles(new HashSet<Vehicle>(Arrays.asList(vehicle)));
vehicle.setRoute(route);
Transport $0to1 = new Transport();
$0to1.setName("Transport 0 -> 1");
$0to1.setPickupNode(nodes.get(0));
$0to1.setDeliveryNode(nodes.get(1));
$0to1.setVehicle(vehicle);
Transport $1to2 = new Transport();
$1to2.setName("Transport 1 -> 2");
$1to2.setPickupNode(nodes.get(1));
$1to2.setDeliveryNode(nodes.get(2));
$1to2.setVehicle(vehicle);
vehicle.setTransports(new HashSet<Transport>(Arrays.asList($0to1, $1to2)));
Session s = openSession();
s.beginTransaction();
s.persist(tour);
s.getTransaction().commit();
s.close();
}
}

View File

@ -1,268 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
/**
* The test case uses the following model:
*
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* -> | | |
* Route -- (1 : N) - Node Transport
* | | <- -> | |
* | -- (1 : N) -- (delivery) -- |
* | |
* | -> -> |
* -------- (1 : N) ---- Vehicle--(1 : N)------------
*
* Arrows indicate the direction of cascade-merge.
* <p/>
* I believe it reproduces the following issue:
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3544
*
* @author Gail Badner (based on original model provided by Pavol Zibrita)
*/
public class CascadeMergeToChildBeforeParentTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {
return new String[] {
"cascade/circle/CascadeMergeToChildBeforeParent.hbm.xml"
};
}
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "true" );
}
@Override
protected void cleanupTest() {
Session s = openSession();
s.beginTransaction();
s.createQuery( "delete from Transport" );
s.createQuery( "delete from Tour" );
s.createQuery( "delete from Node" );
s.createQuery( "delete from Route" );
s.createQuery( "delete from Vehicle" );
}
@Test
public void testMerge() {
Session s = openSession();
s.beginTransaction();
Route route = new Route();
route.setName( "routeA" );
s.save( route );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
route.setTransientField( new String( "sfnaouisrbn" ) );
Tour tour = new Tour();
tour.setName( "tourB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
Route mergedRoute = (Route) s.merge( route );
s.getTransaction().commit();
s.close();
}
@Test
public void testMergeTransientChildBeforeTransientParent() {
// This test fails because the merge algorithm tries to save a
// transient child (transport) before cascade-merge gets its
// transient parent (vehicle); merge does not cascade from the
// child to the parent.
Session s = openSession();
s.beginTransaction();
Route route = new Route();
route.setName( "routeA" );
s.save( route );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
route.setTransientField( new String( "sfnaouisrbn" ) );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport = new Transport();
transport.setName( "transportB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
Vehicle vehicle = new Vehicle();
vehicle.setName( "vehicleB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.getPickupTransports().add( transport );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.getDeliveryTransports().add( transport );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
route.getVehicles().add( vehicle );
transport.setPickupNode( pickupNode );
transport.setDeliveryNode( deliveryNode );
transport.setVehicle( vehicle );
transport.setTransientField( "aaaaaaaaaaaaaa" );
vehicle.getTransports().add( transport );
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
Route mergedRoute = (Route) s.merge( route );
s.getTransaction().commit();
s.close();
}
@Test
public void testMergeData3Nodes() {
Session s = openSession();
s.beginTransaction();
Route route = new Route();
route.setName( "routeA" );
s.save( route );
s.getTransaction().commit();
s.close();
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
route.setTransientField( new String( "sfnaouisrbn" ) );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport1 = new Transport();
transport1.setName( "TRANSPORT1" );
Transport transport2 = new Transport();
transport2.setName( "TRANSPORT2" );
Node node1 = new Node();
node1.setName( "NODE1" );
Node node2 = new Node();
node2.setName( "NODE2" );
Node node3 = new Node();
node3.setName( "NODE3" );
Vehicle vehicle = new Vehicle();
vehicle.setName( "vehicleB" );
node1.setRoute( route );
node1.setTour( tour );
node1.getPickupTransports().add( transport1 );
node1.setTransientField( "node 1" );
node2.setRoute( route );
node2.setTour( tour );
node2.getDeliveryTransports().add( transport1 );
node2.getPickupTransports().add( transport2 );
node2.setTransientField( "node 2" );
node3.setRoute( route );
node3.setTour( tour );
node3.getDeliveryTransports().add( transport2 );
node3.setTransientField( "node 3" );
tour.getNodes().add( node1 );
tour.getNodes().add( node2 );
tour.getNodes().add( node3 );
route.getNodes().add( node1 );
route.getNodes().add( node2 );
route.getNodes().add( node3 );
route.getVehicles().add( vehicle );
transport1.setPickupNode( node1 );
transport1.setDeliveryNode( node2 );
transport1.setVehicle( vehicle );
transport1.setTransientField( "aaaaaaaaaaaaaa" );
transport2.setPickupNode( node2 );
transport2.setDeliveryNode( node3 );
transport2.setVehicle( vehicle );
transport2.setTransientField( "bbbbbbbbbbbbb" );
vehicle.getTransports().add( transport1 );
vehicle.getTransports().add( transport2 );
vehicle.setTransientField( "anewvalue" );
vehicle.setRoute( route );
Route mergedRoute = (Route) s.merge( route );
s.getTransaction().commit();
s.close();
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
/**
* @author Gail Badner
*/
public class MultiPathCircleCascadeCheckNullFalseDelayedInsertTest extends MultiPathCircleCascadeDelayedInsertTest {
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "false" );
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
/**
* @author Gail Badner
*/
public class MultiPathCircleCascadeCheckNullTrueDelayedInsertTest extends MultiPathCircleCascadeDelayedInsertTest {
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "true" );
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
/**
* @author Gail Badner
*/
public class MultiPathCircleCascadeCheckNullibilityFalseTest extends MultiPathCircleCascadeTest {
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "false" );
}
}

View File

@ -1,21 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
/**
* @author Gail Badner
*/
public class MultiPathCircleCascadeCheckNullibilityTrueTest extends MultiPathCircleCascadeTest {
@Override
public void configure(Configuration cfg) {
super.configure( cfg );
cfg.setProperty( Environment.CHECK_NULLABILITY, "true" );
}
}

View File

@ -1,20 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
/**
* @author Gail Badner
*/
public class MultiPathCircleCascadeDelayedInsertTest extends MultiPathCircleCascadeTest {
@Override
public String[] getMappings() {
return new String[] {
"cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
};
}
}

View File

@ -1,725 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.cascade.circle;
import java.util.Iterator;
import javax.persistence.PersistenceException;
import org.junit.Test;
import org.hibernate.JDBCException;
import org.hibernate.PropertyValueException;
import org.hibernate.Session;
import org.hibernate.TransientPropertyValueException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* The test case uses the following model:
*
* <- ->
* -- (N : 0,1) -- Tour
* | <- ->
* | -- (1 : N) -- (pickup) ----
* -> | | |
* Route -- (1 : N) -- Node Transport
* | <- -> |
* -- (1 : N) -- (delivery) --
*
* Arrows indicate the direction of cascade-merge, cascade-save, and cascade-save-or-update
*
* It reproduced the following issues:
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3046
* http://opensource.atlassian.com/projects/hibernate/browse/HHH-3810
* <p/>
* This tests that cascades are done properly from each entity.
*
* @author Pavol Zibrita, Gail Badner
*/
public class MultiPathCircleCascadeTest extends BaseCoreFunctionalTestCase {
private static interface EntityOperation {
boolean isLegacy();
Object doEntityOperation(Object entity, Session s);
}
private static EntityOperation MERGE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return false;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
return s.merge( entity );
}
};
private static EntityOperation SAVE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return true;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
s.save( entity );
return entity;
}
};
private static EntityOperation SAVE_UPDATE_OPERATION =
new EntityOperation() {
@Override
public boolean isLegacy() {
return true;
}
@Override
public Object doEntityOperation(Object entity, Session s) {
s.saveOrUpdate( entity );
return entity;
}
};
@Override
public void configure(Configuration cfg) {
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
}
@Override
public String[] getMappings() {
return new String[] {
"cascade/circle/MultiPathCircleCascade.hbm.xml"
};
}
@Test
public void testMergeEntityWithNonNullableTransientEntity() {
testEntityWithNonNullableTransientEntity( MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullableTransientEntity() {
testEntityWithNonNullableTransientEntity( SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullableTransientEntity() {
testEntityWithNonNullableTransientEntity( SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullableTransientEntity(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
Node node = (Node) route.getNodes().iterator().next();
route.getNodes().remove( node );
Route routeNew = new Route();
routeNew.setName( "new route" );
routeNew.getNodes().add( node );
node.setRoute( routeNew );
Session s = openSession();
s.beginTransaction();
try {
operation.doEntityOperation( node, s );
s.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
((SessionImplementor) s).getFactory().getSettings().isCheckNullability(),
false,
operation.isLegacy()
);
}
finally {
s.getTransaction().rollback();
s.close();
cleanup();
}
}
@Test
public void testMergeEntityWithNonNullableEntityNull() {
testEntityWithNonNullableEntityNull( MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullableEntityNull() {
testEntityWithNonNullableEntityNull( SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullableEntityNull() {
testEntityWithNonNullableEntityNull( SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullableEntityNull(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
Node node = (Node) route.getNodes().iterator().next();
route.getNodes().remove( node );
node.setRoute( null );
Session s = openSession();
s.beginTransaction();
try {
operation.doEntityOperation( node, s );
s.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
((SessionImplementor) s).getFactory().getSettings().isCheckNullability(),
true,
operation.isLegacy()
);
}
finally {
s.getTransaction().rollback();
s.close();
cleanup();
}
}
@Test
public void testMergeEntityWithNonNullablePropSetToNull() {
testEntityWithNonNullablePropSetToNull( MERGE_OPERATION );
}
@Test
public void testSaveEntityWithNonNullablePropSetToNull() {
testEntityWithNonNullablePropSetToNull( SAVE_OPERATION );
}
@Test
public void testSaveUpdateEntityWithNonNullablePropSetToNull() {
testEntityWithNonNullablePropSetToNull( SAVE_UPDATE_OPERATION );
}
private void testEntityWithNonNullablePropSetToNull(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
Node node = (Node) route.getNodes().iterator().next();
node.setName( null );
Session s = openSession();
s.beginTransaction();
try {
operation.doEntityOperation( route, s );
s.getTransaction().commit();
fail( "should have thrown an exception" );
}
catch (Exception ex) {
checkExceptionFromNullValueForNonNullable(
ex,
((SessionImplementor) s).getFactory().getSettings().isCheckNullability(),
true,
operation.isLegacy()
);
}
finally {
s.getTransaction().rollback();
s.close();
cleanup();
}
}
@Test
public void testMergeRoute() {
testRoute( MERGE_OPERATION );
}
// skip SAVE_OPERATION since Route is not transient
@Test
public void testSaveUpdateRoute() {
testRoute( SAVE_UPDATE_OPERATION );
}
private void testRoute(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
clearCounts();
Session s = openSession();
s.beginTransaction();
operation.doEntityOperation( route, s );
s.getTransaction().commit();
s.close();
assertInsertCount( 4 );
assertUpdateCount( 1 );
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
checkResults( route, true );
s.getTransaction().commit();
s.close();
cleanup();
}
@Test
public void testMergePickupNode() {
testPickupNode( MERGE_OPERATION );
}
@Test
public void testSavePickupNode() {
testPickupNode( SAVE_OPERATION );
}
@Test
public void testSaveUpdatePickupNode() {
testPickupNode( SAVE_UPDATE_OPERATION );
}
private void testPickupNode(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
clearCounts();
Session s = openSession();
s.beginTransaction();
Iterator it = route.getNodes().iterator();
Node node = (Node) it.next();
Node pickupNode;
if ( node.getName().equals( "pickupNodeB" ) ) {
pickupNode = node;
}
else {
node = (Node) it.next();
assertEquals( "pickupNodeB", node.getName() );
pickupNode = node;
}
pickupNode = (Node) operation.doEntityOperation( pickupNode, s );
s.getTransaction().commit();
s.close();
assertInsertCount( 4 );
assertUpdateCount( 0 );
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
checkResults( route, false );
s.getTransaction().commit();
s.close();
cleanup();
}
@Test
public void testMergeDeliveryNode() {
testDeliveryNode( MERGE_OPERATION );
}
@Test
public void testSaveDeliveryNode() {
testDeliveryNode( SAVE_OPERATION );
}
@Test
public void testSaveUpdateDeliveryNode() {
testDeliveryNode( SAVE_UPDATE_OPERATION );
}
private void testDeliveryNode(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
clearCounts();
Session s = openSession();
s.beginTransaction();
Iterator it = route.getNodes().iterator();
Node node = (Node) it.next();
Node deliveryNode;
if ( node.getName().equals( "deliveryNodeB" ) ) {
deliveryNode = node;
}
else {
node = (Node) it.next();
assertEquals( "deliveryNodeB", node.getName() );
deliveryNode = node;
}
deliveryNode = (Node) operation.doEntityOperation( deliveryNode, s );
s.getTransaction().commit();
s.close();
assertInsertCount( 4 );
assertUpdateCount( 0 );
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
checkResults( route, false );
s.getTransaction().commit();
s.close();
cleanup();
}
@Test
public void testMergeTour() {
testTour( MERGE_OPERATION );
}
@Test
public void testSaveTour() {
testTour( SAVE_OPERATION );
}
@Test
public void testSaveUpdateTour() {
testTour( SAVE_UPDATE_OPERATION );
}
private void testTour(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
clearCounts();
Session s = openSession();
s.beginTransaction();
Tour tour = (Tour) operation.doEntityOperation( ((Node) route.getNodes().toArray()[0]).getTour(), s );
s.getTransaction().commit();
s.close();
assertInsertCount( 4 );
assertUpdateCount( 0 );
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
checkResults( route, false );
s.getTransaction().commit();
s.close();
cleanup();
}
@Test
public void testMergeTransport() {
testTransport( MERGE_OPERATION );
}
@Test
public void testSaveTransport() {
testTransport( SAVE_OPERATION );
}
@Test
public void testSaveUpdateTransport() {
testTransport( SAVE_UPDATE_OPERATION );
}
private void testTransport(EntityOperation operation) {
Route route = getUpdatedDetachedEntity();
clearCounts();
Session s = openSession();
s.beginTransaction();
Node node = ((Node) route.getNodes().toArray()[0]);
Transport transport;
if ( node.getPickupTransports().size() == 1 ) {
transport = (Transport) node.getPickupTransports().toArray()[0];
}
else {
transport = (Transport) node.getDeliveryTransports().toArray()[0];
}
transport = (Transport) operation.doEntityOperation( transport, s );
s.getTransaction().commit();
s.close();
assertInsertCount( 4 );
assertUpdateCount( 0 );
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
checkResults( route, false );
s.getTransaction().commit();
s.close();
cleanup();
}
private Node getSimpleUpdatedDetachedEntity(){
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
return deliveryNode;
}
private Route getUpdatedDetachedEntity() {
Session s = openSession();
s.beginTransaction();
Route route = new Route();
route.setName( "routeA" );
s.save( route );
s.getTransaction().commit();
s.close();
route.setName( "new routeA" );
route.setTransientField( new String( "sfnaouisrbn" ) );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport = new Transport();
transport.setName( "transportB" );
Node pickupNode = new Node();
pickupNode.setName( "pickupNodeB" );
Node deliveryNode = new Node();
deliveryNode.setName( "deliveryNodeB" );
pickupNode.setRoute( route );
pickupNode.setTour( tour );
pickupNode.getPickupTransports().add( transport );
pickupNode.setTransientField( "pickup node aaaaaaaaaaa" );
deliveryNode.setRoute( route );
deliveryNode.setTour( tour );
deliveryNode.getDeliveryTransports().add( transport );
deliveryNode.setTransientField( "delivery node aaaaaaaaa" );
tour.getNodes().add( pickupNode );
tour.getNodes().add( deliveryNode );
route.getNodes().add( pickupNode );
route.getNodes().add( deliveryNode );
transport.setPickupNode( pickupNode );
transport.setDeliveryNode( deliveryNode );
transport.setTransientField( "aaaaaaaaaaaaaa" );
return route;
}
private void cleanup() {
Session s = openSession();
s.beginTransaction();
s.createQuery( "delete from Transport" );
s.createQuery( "delete from Tour" );
s.createQuery( "delete from Node" );
s.createQuery( "delete from Route" );
s.getTransaction().commit();
s.close();
}
private void checkResults(Route route, boolean isRouteUpdated) {
// since no cascaded to route, this method needs to
// know whether route is expected to be updated
if ( isRouteUpdated ) {
assertEquals( "new routeA", route.getName() );
}
assertEquals( 2, route.getNodes().size() );
Node deliveryNode = null;
Node pickupNode = null;
for ( Iterator it = route.getNodes().iterator(); it.hasNext(); ) {
Node node = (Node) it.next();
if ( "deliveryNodeB".equals( node.getName() ) ) {
deliveryNode = node;
}
else if ( "pickupNodeB".equals( node.getName() ) ) {
pickupNode = node;
}
else {
fail( "unknown node" );
}
}
assertNotNull( deliveryNode );
assertSame( route, deliveryNode.getRoute() );
assertEquals( 1, deliveryNode.getDeliveryTransports().size() );
assertEquals( 0, deliveryNode.getPickupTransports().size() );
assertNotNull( deliveryNode.getTour() );
assertEquals( "node original value", deliveryNode.getTransientField() );
assertNotNull( pickupNode );
assertSame( route, pickupNode.getRoute() );
assertEquals( 0, pickupNode.getDeliveryTransports().size() );
assertEquals( 1, pickupNode.getPickupTransports().size() );
assertNotNull( pickupNode.getTour() );
assertEquals( "node original value", pickupNode.getTransientField() );
assertTrue( !deliveryNode.getNodeID().equals( pickupNode.getNodeID() ) );
assertSame( deliveryNode.getTour(), pickupNode.getTour() );
assertSame(
deliveryNode.getDeliveryTransports().iterator().next(),
pickupNode.getPickupTransports().iterator().next()
);
Tour tour = deliveryNode.getTour();
Transport transport = (Transport) deliveryNode.getDeliveryTransports().iterator().next();
assertEquals( "tourB", tour.getName() );
assertEquals( 2, tour.getNodes().size() );
assertTrue( tour.getNodes().contains( deliveryNode ) );
assertTrue( tour.getNodes().contains( pickupNode ) );
assertEquals( "transportB", transport.getName() );
assertSame( deliveryNode, transport.getDeliveryNode() );
assertSame( pickupNode, transport.getPickupNode() );
assertEquals( "transport original value", transport.getTransientField() );
}
@Test
public void testMergeData3Nodes() {
testData3Nodes( MERGE_OPERATION );
}
@Test
public void testSaveData3Nodes() {
testData3Nodes( SAVE_OPERATION );
}
@Test
public void testSaveUpdateData3Nodes() {
testData3Nodes( SAVE_UPDATE_OPERATION );
}
private void testData3Nodes(EntityOperation operation) {
Session s = openSession();
s.beginTransaction();
Route route = new Route();
route.setName( "routeA" );
s.save( route );
s.getTransaction().commit();
s.close();
clearCounts();
s = openSession();
s.beginTransaction();
route = (Route) s.get( Route.class, route.getRouteID() );
//System.out.println(route);
route.setName( "new routA" );
route.setTransientField( new String( "sfnaouisrbn" ) );
Tour tour = new Tour();
tour.setName( "tourB" );
Transport transport1 = new Transport();
transport1.setName( "TRANSPORT1" );
Transport transport2 = new Transport();
transport2.setName( "TRANSPORT2" );
Node node1 = new Node();
node1.setName( "NODE1" );
Node node2 = new Node();
node2.setName( "NODE2" );
Node node3 = new Node();
node3.setName( "NODE3" );
node1.setRoute( route );
node1.setTour( tour );
node1.getPickupTransports().add( transport1 );
node1.setTransientField( "node 1" );
node2.setRoute( route );
node2.setTour( tour );
node2.getDeliveryTransports().add( transport1 );
node2.getPickupTransports().add( transport2 );
node2.setTransientField( "node 2" );
node3.setRoute( route );
node3.setTour( tour );
node3.getDeliveryTransports().add( transport2 );
node3.setTransientField( "node 3" );
tour.getNodes().add( node1 );
tour.getNodes().add( node2 );
tour.getNodes().add( node3 );
route.getNodes().add( node1 );
route.getNodes().add( node2 );
route.getNodes().add( node3 );
transport1.setPickupNode( node1 );
transport1.setDeliveryNode( node2 );
transport1.setTransientField( "aaaaaaaaaaaaaa" );
transport2.setPickupNode( node2 );
transport2.setDeliveryNode( node3 );
transport2.setTransientField( "bbbbbbbbbbbbb" );
operation.doEntityOperation( route, s );
s.getTransaction().commit();
s.close();
assertInsertCount( 6 );
assertUpdateCount( 1 );
cleanup();
}
protected void checkExceptionFromNullValueForNonNullable(
Exception ex, boolean checkNullability, boolean isNullValue, boolean isLegacy
) {
if ( isNullValue ) {
if ( checkNullability ) {
if ( isLegacy ) {
assertTyping( PropertyValueException.class, ex );
}
else {
assertTyping( PersistenceException.class, ex );
}
}
else {
assertTrue( (ex instanceof JDBCException) || (ex.getCause() instanceof JDBCException) );
}
}
else {
if ( isLegacy ) {
assertTyping( TransientPropertyValueException.class, ex );
}
else {
assertTyping( IllegalStateException.class, ex );
}
}
}
protected void clearCounts() {
sessionFactory().getStatistics().clear();
}
protected void assertInsertCount(int expected) {
int inserts = (int) sessionFactory().getStatistics().getEntityInsertCount();
assertEquals( "unexpected insert count", expected, inserts );
}
protected void assertUpdateCount(int expected) {
int updates = (int) sessionFactory().getStatistics().getEntityUpdateCount();
assertEquals( "unexpected update counts", expected, updates );
}
protected void assertDeleteCount(int expected) {
int deletes = (int) sessionFactory().getStatistics().getEntityDeleteCount();
assertEquals( "unexpected delete counts", expected, deletes );
}
}