HHH-17049 Add more test for issue
This commit is contained in:
parent
85b08de3d2
commit
de3008e712
|
@ -0,0 +1,702 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.PropertyValueException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.TransientPropertyValueException;
|
||||
import org.hibernate.orm.test.cascade.circle.Node;
|
||||
import org.hibernate.orm.test.cascade.circle.Route;
|
||||
import org.hibernate.orm.test.cascade.circle.Tour;
|
||||
import org.hibernate.orm.test.cascade.circle.Transport;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public abstract class AbstractMultiPathCircleCascadeTest extends BaseCoreFunctionalTestCase {
|
||||
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() {
|
||||
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 );
|
||||
|
||||
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() {
|
||||
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 );
|
||||
|
||||
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() {
|
||||
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 );
|
||||
|
||||
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() {
|
||||
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 r = getUpdatedDetachedEntity();
|
||||
|
||||
clearCounts();
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
operation.doEntityOperation( r, session )
|
||||
);
|
||||
|
||||
assertInsertCount( 4 );
|
||||
assertUpdateCount( 1 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Route route = session.get( Route.class, r.getRouteID() );
|
||||
checkResults( route, true );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@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 r = getUpdatedDetachedEntity();
|
||||
|
||||
clearCounts();
|
||||
|
||||
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( 4 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Route route = session.get( Route.class, r.getRouteID() );
|
||||
checkResults( route, false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@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 r = getUpdatedDetachedEntity();
|
||||
|
||||
clearCounts();
|
||||
|
||||
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( 4 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Route route = session.get( Route.class, r.getRouteID() );
|
||||
checkResults( route, false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@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 r = getUpdatedDetachedEntity();
|
||||
|
||||
clearCounts();
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
operation.doEntityOperation( ( (Node) r.getNodes().toArray()[0] ).getTour(), session )
|
||||
);
|
||||
|
||||
assertInsertCount( 4 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Route route = session.get( Route.class, r.getRouteID() );
|
||||
checkResults( route, false );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@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 r = getUpdatedDetachedEntity();
|
||||
|
||||
clearCounts();
|
||||
|
||||
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( 4 );
|
||||
assertUpdateCount( 0 );
|
||||
|
||||
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() {
|
||||
|
||||
Route route = new Route();
|
||||
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;
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
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() {
|
||||
testData3Nodes( MERGE_OPERATION );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveData3Nodes() {
|
||||
testData3Nodes( SAVE_OPERATION );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveUpdateData3Nodes() {
|
||||
testData3Nodes( SAVE_UPDATE_OPERATION );
|
||||
}
|
||||
|
||||
private void testData3Nodes( EntityOperation operation) {
|
||||
|
||||
Route r = new Route();
|
||||
inTransaction(
|
||||
session -> {
|
||||
r.setName( "routeA" );
|
||||
|
||||
session.save( r );
|
||||
}
|
||||
);
|
||||
|
||||
clearCounts();
|
||||
|
||||
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( 6 );
|
||||
assertUpdateCount( 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() {
|
||||
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 );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeCheckNullFalseDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
configuration.setProperty( Environment.CHECK_NULLABILITY, "false" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeCheckNullTrueDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
configuration.setProperty( Environment.CHECK_NULLABILITY, "true" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeCheckNullibilityFalseTest extends AbstractMultiPathCircleCascadeTest {
|
||||
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
configuration.setProperty( Environment.CHECK_NULLABILITY, "false" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeCheckNullibilityTrueTest extends AbstractMultiPathCircleCascadeTest {
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
configuration.setProperty( Environment.CHECK_NULLABILITY, "true" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeDelayedInsertTest extends AbstractMultiPathCircleCascadeTest {
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascadeDelayedInsert.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.cascade.circle;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCircleCascadeTest extends AbstractMultiPathCircleCascadeTest {
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/circle/MultiPathCircleCascade.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
configuration.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
configuration.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.deletetransient;
|
||||
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
import org.hibernate.orm.test.deletetransient.Address;
|
||||
import org.hibernate.orm.test.deletetransient.Note;
|
||||
import org.hibernate.orm.test.deletetransient.Person;
|
||||
import org.hibernate.orm.test.deletetransient.Suite;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class DeleteTransientEntityTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected String getBaseForMappings() {
|
||||
return "org/hibernate/orm/test/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getMappings() {
|
||||
return new String[] { "deletetransient/Person.hbm.xml" };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCleanupTestDataRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientEntityDeletionNoCascades() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.remove( new Address() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientEntityDeletionCascadingToTransientAssociation() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Person p = new Person();
|
||||
p.getAddresses().add( new Address() );
|
||||
session.persist( p );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientEntityDeleteCascadingToCircularity() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Person p1 = new Person();
|
||||
Person p2 = new Person();
|
||||
p1.getFriends().add( p2 );
|
||||
p2.getFriends().add( p1 );
|
||||
session.persist( p1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientEntityDeletionCascadingToDetachedAssociation() {
|
||||
Address address = new Address();
|
||||
inTransaction(
|
||||
session -> {
|
||||
address.setInfo( "123 Main St." );
|
||||
session.persist( address );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Person p = new Person();
|
||||
p.getAddresses().add( address );
|
||||
session.delete( p );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Long count = (Long) session.createQuery( "select count(*) from Address" ).list().get( 0 );
|
||||
assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransientEntityDeletionCascadingToPersistentAssociation() {
|
||||
Long id = fromTransaction(
|
||||
session -> {
|
||||
Address address = new Address();
|
||||
address.setInfo( "123 Main St." );
|
||||
session.persist( address );
|
||||
return address.getId();
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Address address = session.get( Address.class, id );
|
||||
Person p = new Person();
|
||||
p.getAddresses().add( address );
|
||||
session.delete( p );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Long count = (Long) session.createQuery( "select count(*) from Address" ).list().get( 0 );
|
||||
assertEquals( "delete not cascaded properly across transient entity", 0, count.longValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public void testCascadeAllFromClearedPersistentAssnToTransientEntity() {
|
||||
final Person p = new Person();
|
||||
Address address = new Address();
|
||||
inTransaction(
|
||||
session -> {
|
||||
address.setInfo( "123 Main St." );
|
||||
p.getAddresses().add( address );
|
||||
session.save( p );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Suite suite = new Suite();
|
||||
address.getSuites().add( suite );
|
||||
p.getAddresses().clear();
|
||||
session.saveOrUpdate( p );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Person person = session.get( p.getClass(), p.getId() );
|
||||
assertEquals( "persistent collection not cleared", 0, person.getAddresses().size() );
|
||||
Long count = (Long) session.createQuery( "select count(*) from Address" ).list().get( 0 );
|
||||
assertEquals( 1, count.longValue() );
|
||||
count = (Long) session.createQuery( "select count(*) from Suite" ).list().get( 0 );
|
||||
assertEquals( 0, count.longValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public void testCascadeAllDeleteOrphanFromClearedPersistentAssnToTransientEntity() {
|
||||
Address address = new Address();
|
||||
address.setInfo( "123 Main St." );
|
||||
Suite suite = new Suite();
|
||||
address.getSuites().add( suite );
|
||||
inTransaction(
|
||||
session -> {
|
||||
|
||||
session.save( address );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Note note = new Note();
|
||||
note.setDescription( "a description" );
|
||||
suite.getNotes().add( note );
|
||||
address.getSuites().clear();
|
||||
session.saveOrUpdate( address );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Long count = (Long) session.createQuery( "select count(*) from Suite" ).list().get( 0 );
|
||||
assertEquals(
|
||||
"all-delete-orphan not cascaded properly to cleared persistent collection entities",
|
||||
0,
|
||||
count.longValue()
|
||||
);
|
||||
count = (Long) session.createQuery( "select count(*) from Note" ).list().get( 0 );
|
||||
assertEquals( 0, count.longValue() );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
package org.hibernate.orm.test.bytecode.enhancement.lazy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.annotations.DynamicUpdate;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToOne;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
@JiraKey("HHH-17049")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class JpaConstructorInitializationAndDynamicUpdateTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class,
|
||||
LoginAccount.class,
|
||||
AccountPreferences.class
|
||||
};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = new Person( 1l, "Henry" );
|
||||
LoginAccount loginAccount = new LoginAccount();
|
||||
loginAccount.setOwner( person );
|
||||
person.setLoginAccount( loginAccount );
|
||||
em.persist( person );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.createQuery( "delete from Person" ).executeUpdate();
|
||||
em.createQuery( "delete from LoginAccount" ).executeUpdate();
|
||||
em.createQuery( "delete from AccountPreferences" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findTest() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.find( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReferenceTest() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.getReference( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findTest2() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.find( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
loginAccount.setName( "abc" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = em.find( Person.class, 1L );
|
||||
assertThat( person.getFirstName() ).isEqualTo( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
assertThat( loginAccount ).isNotNull();
|
||||
assertThat( loginAccount.getName() ).isEqualTo( "abc" );
|
||||
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReferenceTest2() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.getReference( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
loginAccount.setName( "abc" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = em.find( Person.class, 1L );
|
||||
assertThat( person.getFirstName() ).isEqualTo( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
assertThat( loginAccount ).isNotNull();
|
||||
assertThat( loginAccount.getName() ).isEqualTo( "abc" );
|
||||
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@DynamicUpdate
|
||||
public static class Person {
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
private String firstName;
|
||||
|
||||
@OneToOne(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
|
||||
private LoginAccount loginAccount = new LoginAccount();
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(long id, String firstName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public LoginAccount getLoginAccount() {
|
||||
return loginAccount;
|
||||
}
|
||||
|
||||
public void setLoginAccount(LoginAccount loginAccount) {
|
||||
this.loginAccount = loginAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "LoginAccount")
|
||||
@DynamicUpdate
|
||||
public static class LoginAccount {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
private AccountPreferences accountPreferences = new AccountPreferences();
|
||||
|
||||
@OneToOne(mappedBy = "loginAccount")
|
||||
private Person owner;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public AccountPreferences getAccountPreferences() {
|
||||
return accountPreferences;
|
||||
}
|
||||
|
||||
public void setAccountPreferences(AccountPreferences accountPreferences) {
|
||||
this.accountPreferences = accountPreferences;
|
||||
}
|
||||
|
||||
public Person getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(Person owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "AccountPreferences")
|
||||
@DynamicUpdate
|
||||
public static class AccountPreferences {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Column(name = "open_col")
|
||||
private boolean open = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package org.hibernate.orm.test.bytecode.enhancement.lazy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToOne;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
@JiraKey("HHH-17049")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class JpaConstructorInitializationTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class,
|
||||
LoginAccount.class,
|
||||
AccountPreferences.class
|
||||
};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = new Person( 1l, "Henry" );
|
||||
LoginAccount loginAccount = new LoginAccount();
|
||||
loginAccount.setOwner( person );
|
||||
person.setLoginAccount( loginAccount );
|
||||
em.persist( person );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.createQuery( "delete from Person" ).executeUpdate();
|
||||
em.createQuery( "delete from LoginAccount" ).executeUpdate();
|
||||
em.createQuery( "delete from AccountPreferences" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findTest() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.find( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReferenceTest() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.getReference( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findTest2() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.find( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
loginAccount.setName( "abc" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = em.find( Person.class, 1L );
|
||||
assertThat( person.getFirstName() ).isEqualTo( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
assertThat( loginAccount ).isNotNull();
|
||||
assertThat( loginAccount.getName() ).isEqualTo( "abc" );
|
||||
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getReferenceTest2() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
em.clear();
|
||||
Person person = em.getReference( Person.class, 1L );
|
||||
person.setFirstName( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
loginAccount.setName( "abc" );
|
||||
}
|
||||
);
|
||||
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person person = em.find( Person.class, 1L );
|
||||
assertThat( person.getFirstName() ).isEqualTo( "Liza" );
|
||||
|
||||
LoginAccount loginAccount = person.getLoginAccount();
|
||||
assertThat( loginAccount ).isNotNull();
|
||||
assertThat( loginAccount.getName() ).isEqualTo( "abc" );
|
||||
|
||||
List<LoginAccount> accounts = em.createQuery(
|
||||
"select la from LoginAccount la",
|
||||
LoginAccount.class
|
||||
).getResultList();
|
||||
assertThat( accounts.size() ).isEqualTo( 1 );
|
||||
|
||||
List<AccountPreferences> preferences = em.createQuery(
|
||||
"select ap from AccountPreferences ap",
|
||||
AccountPreferences.class
|
||||
).getResultList();
|
||||
assertThat( preferences.size() ).isEqualTo( 1 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
@Id
|
||||
private long id;
|
||||
|
||||
private String firstName;
|
||||
|
||||
@OneToOne(orphanRemoval = true, cascade = { CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
|
||||
private LoginAccount loginAccount = new LoginAccount();
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(long id, String firstName) {
|
||||
this.id = id;
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public LoginAccount getLoginAccount() {
|
||||
return loginAccount;
|
||||
}
|
||||
|
||||
public void setLoginAccount(LoginAccount loginAccount) {
|
||||
this.loginAccount = loginAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "LoginAccount")
|
||||
public static class LoginAccount {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
private AccountPreferences accountPreferences = new AccountPreferences();
|
||||
|
||||
@OneToOne(mappedBy = "loginAccount")
|
||||
private Person owner;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public AccountPreferences getAccountPreferences() {
|
||||
return accountPreferences;
|
||||
}
|
||||
|
||||
public void setAccountPreferences(AccountPreferences accountPreferences) {
|
||||
this.accountPreferences = accountPreferences;
|
||||
}
|
||||
|
||||
public Person getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(Person owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "AccountPreferences")
|
||||
public static class AccountPreferences {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Column(name = "open_col")
|
||||
private boolean open = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy;
|
||||
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
import org.hibernate.orm.test.cascade.A;
|
||||
import org.hibernate.orm.test.cascade.G;
|
||||
import org.hibernate.orm.test.cascade.H;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Ovidiu Feodorov
|
||||
* @author Gail Badner
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class MultiPathCascadeTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/cascade/MultiPathCascade.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupTest() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from A" );
|
||||
session.createQuery( "delete from G" );
|
||||
session.createQuery( "delete from H" );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMergeModifiedDetached() {
|
||||
// persist a simple A in the database
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
inTransaction(
|
||||
session -> session.merge( a )
|
||||
);
|
||||
|
||||
verifyModifications( a.getId() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMergeModifiedDetachedIntoProxy() {
|
||||
// persist a simple A in the database
|
||||
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
A aLoaded = session.load( A.class, new Long( a.getId() ) );
|
||||
assertTrue( aLoaded instanceof HibernateProxy );
|
||||
assertSame( aLoaded, session.merge( a ) );
|
||||
}
|
||||
);
|
||||
|
||||
verifyModifications( a.getId() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathUpdateModifiedDetached() {
|
||||
// persist a simple A in the database
|
||||
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.update( a )
|
||||
);
|
||||
|
||||
verifyModifications( a.getId() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathGetAndModify() {
|
||||
// persist a simple A in the database
|
||||
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
A result = session.get( A.class, new Long( a.getId() ) );
|
||||
modifyEntity( result );
|
||||
}
|
||||
);
|
||||
// retrieve the previously saved instance from the database, and update it
|
||||
|
||||
verifyModifications( a.getId() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMergeNonCascadedTransientEntityInCollection() {
|
||||
// persist a simple A in the database
|
||||
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
A merged = fromTransaction(
|
||||
session ->
|
||||
(A) session.merge( a )
|
||||
);
|
||||
|
||||
verifyModifications( merged.getId() );
|
||||
|
||||
// add a new (transient) G to collection in h
|
||||
// there is no cascade from H to the collection, so this should fail when merged
|
||||
assertEquals( 1, merged.getHs().size() );
|
||||
|
||||
H h = (H) merged.getHs().iterator().next();
|
||||
|
||||
G gNew = new G();
|
||||
gNew.setData( "Gail" );
|
||||
gNew.getHs().add( h );
|
||||
h.getGs().add( gNew );
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
session.beginTransaction();
|
||||
try {
|
||||
session.merge( merged );
|
||||
session.merge( h );
|
||||
session.getTransaction().commit();
|
||||
fail( "should have thrown IllegalStateException" );
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
// expected
|
||||
}
|
||||
finally {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMergeNonCascadedTransientEntityInOneToOne() {
|
||||
// persist a simple A in the database
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
A merged = fromTransaction(
|
||||
session ->
|
||||
(A) session.merge( a )
|
||||
);
|
||||
|
||||
verifyModifications( merged.getId() );
|
||||
|
||||
// change the one-to-one association from g to be a new (transient) A
|
||||
// there is no cascade from G to A, so this should fail when merged
|
||||
G g = merged.getG();
|
||||
a.setG( null );
|
||||
A aNew = new A();
|
||||
aNew.setData( "Alice" );
|
||||
g.setA( aNew );
|
||||
aNew.setG( g );
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
session.beginTransaction();
|
||||
try {
|
||||
session.merge( merged );
|
||||
session.merge( g );
|
||||
session.getTransaction().commit();
|
||||
fail( "should have thrown IllegalStateException" );
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
// expected
|
||||
}
|
||||
finally {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMergeNonCascadedTransientEntityInManyToOne() {
|
||||
// persist a simple A in the database
|
||||
|
||||
A a = new A();
|
||||
a.setData( "Anna" );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.save( a )
|
||||
);
|
||||
|
||||
// modify detached entity
|
||||
modifyEntity( a );
|
||||
|
||||
A merged = fromTransaction(
|
||||
session ->
|
||||
(A) session.merge( a )
|
||||
);
|
||||
|
||||
verifyModifications( a.getId() );
|
||||
|
||||
// change the many-to-one association from h to be a new (transient) A
|
||||
// there is no cascade from H to A, so this should fail when merged
|
||||
assertEquals( 1, merged.getHs().size() );
|
||||
H h = (H) merged.getHs().iterator().next();
|
||||
merged.getHs().remove( h );
|
||||
A aNew = new A();
|
||||
aNew.setData( "Alice" );
|
||||
aNew.addH( h );
|
||||
|
||||
inSession(
|
||||
session -> {
|
||||
session.beginTransaction();
|
||||
try {
|
||||
session.merge( merged );
|
||||
session.merge( h );
|
||||
session.getTransaction().commit();
|
||||
fail( "should have thrown IllegalStateException" );
|
||||
}
|
||||
catch (IllegalStateException expected) {
|
||||
// expected
|
||||
}
|
||||
finally {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void modifyEntity(A a) {
|
||||
// create a *circular* graph in detached entity
|
||||
a.setData( "Anthony" );
|
||||
|
||||
G g = new G();
|
||||
g.setData( "Giovanni" );
|
||||
|
||||
H h = new H();
|
||||
h.setData( "Hellen" );
|
||||
|
||||
a.setG( g );
|
||||
g.setA( a );
|
||||
|
||||
a.getHs().add( h );
|
||||
h.setA( a );
|
||||
|
||||
g.getHs().add( h );
|
||||
h.getGs().add( g );
|
||||
}
|
||||
|
||||
private void verifyModifications(long aId) {
|
||||
inTransaction(
|
||||
session -> {
|
||||
// retrieve the A object and check it
|
||||
A a = session.get( A.class, new Long( aId ) );
|
||||
assertEquals( aId, a.getId() );
|
||||
assertEquals( "Anthony", a.getData() );
|
||||
assertNotNull( a.getG() );
|
||||
assertNotNull( a.getHs() );
|
||||
assertEquals( 1, a.getHs().size() );
|
||||
|
||||
G gFromA = a.getG();
|
||||
H hFromA = (H) a.getHs().iterator().next();
|
||||
|
||||
// check the G object
|
||||
assertEquals( "Giovanni", gFromA.getData() );
|
||||
assertSame( a, gFromA.getA() );
|
||||
assertNotNull( gFromA.getHs() );
|
||||
assertEquals( a.getHs(), gFromA.getHs() );
|
||||
assertSame( hFromA, gFromA.getHs().iterator().next() );
|
||||
|
||||
// check the H object
|
||||
assertEquals( "Hellen", hFromA.getData() );
|
||||
assertSame( a, hFromA.getA() );
|
||||
assertNotNull( hFromA.getGs() );
|
||||
assertEquals( 1, hFromA.getGs().size() );
|
||||
assertSame( gFromA, hFromA.getGs().iterator().next() );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy.backref;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.NoDirtyCheckingContext;
|
||||
import org.hibernate.orm.test.bytecode.enhancement.lazy.proxy.inlinedirtychecking.DirtyCheckEnhancementContext;
|
||||
import org.hibernate.orm.test.collection.backref.map.compkey.MapKey;
|
||||
import org.hibernate.orm.test.collection.backref.map.compkey.Part;
|
||||
import org.hibernate.orm.test.collection.backref.map.compkey.Product;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
* BackrefCompositeMapKeyTest implementation. Test access to a composite map-key
|
||||
* backref via a number of different access methods.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@RunWith( BytecodeEnhancerRunner.class )
|
||||
@CustomEnhancementContext({ NoDirtyCheckingContext.class, DirtyCheckEnhancementContext.class })
|
||||
public class BackrefCompositeMapKeyTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[] {
|
||||
"org/hibernate/orm/test/collection/backref/map/compkey/Mappings.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteOnDelete() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product( "Widget" );
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
session.flush();
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
|
||||
session.delete( prod );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( "Orphan 'Widge' was not deleted", session.get( Part.class, "Widge" ) );
|
||||
assertNull( "Orphan 'Get' was not deleted", session.get( Part.class, "Get" ) );
|
||||
assertNull( "Orphan 'Widget' was not deleted", session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteAfterPersist() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product( "Widget" );
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.delete( session.get( Product.class, "Widget" ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteAfterPersistAndFlush() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product( "Widget" );
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
session.flush();
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteAfterLock() {
|
||||
Product prod = new Product( "Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
inTransaction(
|
||||
session -> {
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.lock( prod, LockMode.READ );
|
||||
prod.getParts().remove( mapKey );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteOnSaveOrUpdate() {
|
||||
Product prod = new Product( "Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
inTransaction(
|
||||
session -> {
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.saveOrUpdate( prod )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteOnSaveOrUpdateAfterSerialization() {
|
||||
Product prod = new Product( "Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
inTransaction(
|
||||
session -> {
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
|
||||
Product cloned = (Product) SerializationHelper.clone( prod );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.saveOrUpdate( cloned )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDelete() {
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product( "Widget" );
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
SessionFactoryImplementor sessionFactory = sessionFactory();
|
||||
sessionFactory.getCache().evictEntityData( Product.class );
|
||||
sessionFactory.getCache().evictEntityData( Part.class );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = session.get( Product.class, "Widget" );
|
||||
assertTrue( Hibernate.isInitialized( prod.getParts() ) );
|
||||
Part part = session.get( Part.class, "Widge" );
|
||||
prod.getParts().remove( mapKey );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
sessionFactory.getCache().evictEntityData( Product.class );
|
||||
sessionFactory.getCache().evictEntityData( Part.class );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = session.get( Product.class, "Widget" );
|
||||
assertTrue( Hibernate.isInitialized( prod.getParts() ) );
|
||||
assertNull( prod.getParts().get( new MapKey( "Top" ) ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrphanDeleteOnMerge() {
|
||||
Product prod = new Product( "Widget" );
|
||||
MapKey mapKey = new MapKey( "Top" );
|
||||
inTransaction(
|
||||
session -> {
|
||||
Part part = new Part( "Widge", "part if a Widget" );
|
||||
prod.getParts().put( mapKey, part );
|
||||
Part part2 = new Part( "Get", "another part if a Widget" );
|
||||
prod.getParts().put( new MapKey( "Bottom" ), part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
prod.getParts().remove( mapKey );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.merge( prod )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.orphan;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.orm.test.orphan.Part;
|
||||
import org.hibernate.orm.test.orphan.Product;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.bytecode.enhancement.CustomEnhancementContext;
|
||||
import org.hibernate.testing.bytecode.enhancement.EnhancerTestContext;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
@CustomEnhancementContext({
|
||||
EnhancerTestContext.class, // supports laziness and dirty-checking
|
||||
DefaultEnhancementContext.class
|
||||
})
|
||||
public class OrphanTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected String[] getOrmXmlFiles() {
|
||||
return new String[]{
|
||||
"org/hibernate/orm/test/orphan/Product.hbm.xml"
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from Part" ).executeUpdate();
|
||||
session.createQuery( "delete from Product" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteOnDelete() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product();
|
||||
prod.setName( "Widget" );
|
||||
Part part = new Part();
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
session.flush();
|
||||
|
||||
prod.getParts().remove( part );
|
||||
|
||||
session.delete( prod );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNull( session.get( Part.class, "Get" ) );
|
||||
assertNull( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteAfterPersist() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product();
|
||||
prod.setName( "Widget" );
|
||||
Part part = new Part();
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
|
||||
prod.getParts().remove( part );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteAfterPersistAndFlush() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product();
|
||||
prod.setName( "Widget" );
|
||||
Part part = new Part();
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
session.flush();
|
||||
|
||||
prod.getParts().remove( part );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteAfterLock() {
|
||||
Product prod = new Product();
|
||||
Part part = new Part();
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.lock( prod, LockMode.READ );
|
||||
prod.getParts().remove( part );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteOnSaveOrUpdate() {
|
||||
Product prod = new Product();
|
||||
Part part = new Part();
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
prod.getParts().remove( part );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.saveOrUpdate( prod )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteOnSaveOrUpdateAfterSerialization() {
|
||||
Product prod = new Product();
|
||||
Part part = new Part();
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
prod.getParts().remove( part );
|
||||
|
||||
Product cloned = (Product) SerializationHelper.clone( prod );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.saveOrUpdate( cloned )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDelete() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = new Product();
|
||||
prod.setName( "Widget" );
|
||||
Part part = new Part();
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
sessionFactory().getCache().evictEntityData( Product.class );
|
||||
sessionFactory().getCache().evictEntityData( Part.class );
|
||||
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
Product prod = session.get( Product.class, "Widget" );
|
||||
assertTrue( Hibernate.isInitialized( prod.getParts() ) );
|
||||
Part part = session.get( Part.class, "Widge" );
|
||||
prod.getParts().remove( part );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteOnMerge() {
|
||||
Product prod = new Product();
|
||||
Part part = new Part();
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
Part part2 = new Part();
|
||||
part2.setName( "Get" );
|
||||
part2.setDescription( "another part if a Widget" );
|
||||
prod.getParts().add( part2 );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
prod.getParts().remove( part );
|
||||
|
||||
inTransaction(
|
||||
session ->
|
||||
session.merge( prod )
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
assertNotNull( session.get( Part.class, "Get" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOrphanDeleteOnMergeRemoveElementMerge() {
|
||||
Product prod = new Product();
|
||||
Part part = new Part();
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.merge( prod );
|
||||
prod.getParts().remove( part );
|
||||
session.merge( prod );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNull( session.get( Part.class, "Widge" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
@TestForIssue(jiraKey = "HHH-9171")
|
||||
public void testOrphanDeleteOnAddElementMergeRemoveElementMerge() {
|
||||
Product prod = new Product();
|
||||
inTransaction(
|
||||
session -> {
|
||||
prod.setName( "Widget" );
|
||||
session.persist( prod );
|
||||
}
|
||||
);
|
||||
|
||||
Part part = new Part();
|
||||
part.setName( "Widge" );
|
||||
part.setDescription( "part if a Widget" );
|
||||
prod.getParts().add( part );
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.merge( prod );
|
||||
// In Section 2.9, Entity Relationships, the JPA 2.1 spec says:
|
||||
// "If the entity being orphaned is a detached, new, or removed entity,
|
||||
// the semantics of orphanRemoval do not apply."
|
||||
// In other words, since part is a new entity, it will not be deleted when removed
|
||||
// from prod.parts, even though cascade for the association includes "delete-orphan".
|
||||
prod.getParts().remove( part );
|
||||
session.merge( prod );
|
||||
}
|
||||
);
|
||||
|
||||
inTransaction(
|
||||
session -> {
|
||||
assertNotNull( session.get( Part.class, "Widge" ) );
|
||||
session.delete( session.get( Product.class, "Widget" ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ public class Route {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
protected Set getNodes() {
|
||||
public Set getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue