HHH-12666 Test the current behavior of exception conversion on persist/save/merge/flush/etc.

Original tests by Gail Badner:
09aa6fbce2
This commit is contained in:
Yoann Rodière 2018-07-18 13:18:29 +02:00 committed by Guillaume Smet
parent 8ef0ca9a13
commit 88fb16e5c9
5 changed files with 886 additions and 0 deletions

View File

@ -0,0 +1,69 @@
/*
* 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.exceptionhandling;
import java.util.Arrays;
import java.util.Map;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.junit4.CustomParameterized;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(CustomParameterized.class)
public abstract class BaseExceptionHandlingTest extends BaseJpaOrNativeBootstrapFunctionalTestCase {
private static final String EXCEPTION_HANDLING_PROPERTY =
AvailableSettings.NATIVE_EXCEPTION_HANDLING_51_COMPLIANCE;
public enum ExceptionHandlingSetting {
DEFAULT,
TRUE,
FALSE
}
@Parameterized.Parameters(name = "Bootstrap={0}, ExceptionHandlingSetting={1}")
public static Iterable<Object[]> parameters() {
return Arrays.asList( new Object[][] {
{ BootstrapMethod.JPA, ExceptionHandlingSetting.DEFAULT, ExceptionExpectations.jpa() },
{ BootstrapMethod.JPA, ExceptionHandlingSetting.TRUE, ExceptionExpectations.jpa() },
{ BootstrapMethod.JPA, ExceptionHandlingSetting.FALSE, ExceptionExpectations.jpa() },
{ BootstrapMethod.NATIVE, ExceptionHandlingSetting.DEFAULT, ExceptionExpectations.nativePost52() },
{ BootstrapMethod.NATIVE, ExceptionHandlingSetting.TRUE, ExceptionExpectations.nativePre52() },
{ BootstrapMethod.NATIVE, ExceptionHandlingSetting.FALSE, ExceptionExpectations.nativePost52() }
} );
}
private final ExceptionHandlingSetting exceptionHandlingSetting;
protected final ExceptionExpectations exceptionExpectations;
protected BaseExceptionHandlingTest(
BootstrapMethod bootstrapMethod,
ExceptionHandlingSetting exceptionHandlingSetting,
ExceptionExpectations exceptionExpectations) {
super( bootstrapMethod );
this.exceptionHandlingSetting = exceptionHandlingSetting;
this.exceptionExpectations = exceptionExpectations;
}
@Override
protected void configure(Map<Object, Object> properties) {
switch ( exceptionHandlingSetting ) {
case DEFAULT:
// Keep the default
break;
case TRUE:
properties.put( EXCEPTION_HANDLING_PROPERTY, "true" );
break;
case FALSE:
properties.put( EXCEPTION_HANDLING_PROPERTY, "false" );
break;
}
}
}

View File

@ -0,0 +1,374 @@
/*
* 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.exceptionhandling;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.testing.AfterClassOnce;
import org.hibernate.testing.BeforeClassOnce;
import org.hibernate.testing.cache.CachingRegionFactory;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.After;
import static org.junit.Assert.fail;
/**
* A base class for all functional tests.
*/
public abstract class BaseJpaOrNativeBootstrapFunctionalTestCase extends BaseUnitTestCase {
// IMPL NOTE : Here we use @Before and @After (instead of @BeforeClassOnce and @AfterClassOnce like we do in
// BaseCoreFunctionalTestCase) because the old HEM test methodology was to create an EMF for each test method.
private static final Dialect dialect = Dialect.getDialect();
public enum BootstrapMethod {
JPA,
NATIVE
}
private final BootstrapMethod bootstrapMethod;
private StandardServiceRegistryImpl serviceRegistry;
private SessionFactoryImplementor sessionFactory;
private Session session;
protected Dialect getDialect() {
return dialect;
}
protected SessionFactoryImplementor sessionFactory() {
return sessionFactory;
}
protected StandardServiceRegistryImpl serviceRegistry() {
return serviceRegistry;
}
protected Session openSession() throws HibernateException {
session = sessionFactory().openSession();
return session;
}
protected EntityManager openEntityManager() throws HibernateException {
return openSession().unwrap( EntityManager.class );
}
protected BaseJpaOrNativeBootstrapFunctionalTestCase(BootstrapMethod bootstrapMethod) {
this.bootstrapMethod = bootstrapMethod;
}
@BeforeClassOnce
@SuppressWarnings( {"UnusedDeclaration"})
public void buildSessionOrEntityManagerFactory() {
switch ( bootstrapMethod ) {
case JPA:
buildEntityManagerFactory();
break;
case NATIVE:
buildSessionFactory();
break;
}
afterSessionOrEntityManagerFactoryBuilt();
}
private void buildEntityManagerFactory() {
log.trace( "Building EntityManagerFactory" );
Properties properties = buildProperties();
ArrayList<Class> classes = new ArrayList<Class>();
classes.addAll( Arrays.asList( getAnnotatedClasses() ) );
properties.put( AvailableSettings.LOADED_CLASSES, classes );
sessionFactory = Bootstrap.getEntityManagerFactoryBuilder(
buildPersistenceUnitDescriptor(),
properties
).build().unwrap( SessionFactoryImplementor.class );
serviceRegistry = (StandardServiceRegistryImpl) sessionFactory.getServiceRegistry()
.getParentServiceRegistry();
}
private void buildSessionFactory() {
// for now, build the configuration to get all the property settings
Configuration configuration = new Configuration();
configuration.setProperties( buildProperties() );
Class<?>[] annotatedClasses = getAnnotatedClasses();
if ( annotatedClasses != null ) {
for ( Class<?> annotatedClass : annotatedClasses ) {
configuration.addAnnotatedClass( annotatedClass );
}
}
BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry();
serviceRegistry = buildServiceRegistry( bootRegistry, configuration );
sessionFactory = ( SessionFactoryImplementor ) configuration.buildSessionFactory( serviceRegistry );
afterSessionOrEntityManagerFactoryBuilt();
}
private PersistenceUnitDescriptor buildPersistenceUnitDescriptor() {
return new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() );
}
public static class TestingPersistenceUnitDescriptorImpl implements PersistenceUnitDescriptor {
private final String name;
public TestingPersistenceUnitDescriptorImpl(String name) {
this.name = name;
}
@Override
public URL getPersistenceUnitRootUrl() {
return null;
}
@Override
public String getName() {
return name;
}
@Override
public String getProviderClassName() {
return HibernatePersistenceProvider.class.getName();
}
@Override
public boolean isUseQuotedIdentifiers() {
return false;
}
@Override
public boolean isExcludeUnlistedClasses() {
return false;
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public List<String> getManagedClassNames() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return null;
}
@Override
public List<URL> getJarFileUrls() {
return null;
}
@Override
public Object getNonJtaDataSource() {
return null;
}
@Override
public Object getJtaDataSource() {
return null;
}
@Override
public Properties getProperties() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public ClassLoader getTempClassLoader() {
return null;
}
@Override
public void pushClassTransformer(EnhancementContext enhancementContext) {
}
}
private BootstrapServiceRegistry buildBootstrapServiceRegistry() {
final BootstrapServiceRegistryBuilder builder = new BootstrapServiceRegistryBuilder();
builder.applyClassLoader( getClass().getClassLoader() );
prepareBootstrapRegistryBuilder( builder );
return builder.build();
}
protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) {
}
private StandardServiceRegistryImpl buildServiceRegistry(BootstrapServiceRegistry bootRegistry, Configuration configuration) {
Properties properties = new Properties();
properties.putAll( configuration.getProperties() );
Environment.verifyProperties( properties );
ConfigurationHelper.resolvePlaceHolders( properties );
StandardServiceRegistryBuilder cfgRegistryBuilder = configuration.getStandardServiceRegistryBuilder();
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder( bootRegistry, cfgRegistryBuilder.getAggregatedCfgXml() )
.applySettings( properties );
return (StandardServiceRegistryImpl) registryBuilder.build();
}
private Properties buildProperties() {
Properties properties = Environment.getProperties();
properties.put( org.hibernate.cfg.AvailableSettings.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() );
for ( Map.Entry<Class, String> entry : getCachedClasses().entrySet() ) {
properties.put( AvailableSettings.CLASS_CACHE_PREFIX + "." + entry.getKey().getName(), entry.getValue() );
}
for ( Map.Entry<String, String> entry : getCachedCollections().entrySet() ) {
properties.put( AvailableSettings.COLLECTION_CACHE_PREFIX + "." + entry.getKey(), entry.getValue() );
}
configure( properties );
if ( createSchema() ) {
properties.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "create-drop" );
}
properties.put( org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true" );
properties.put( org.hibernate.cfg.AvailableSettings.DIALECT, getDialect().getClass().getName() );
return properties;
}
protected void configure(Map<Object, Object> properties) {
}
protected static final Class<?>[] NO_CLASSES = new Class[0];
protected Class<?>[] getAnnotatedClasses() {
return NO_CLASSES;
}
public Map<Class, String> getCachedClasses() {
return new HashMap<>();
}
public Map<String, String> getCachedCollections() {
return new HashMap<>();
}
protected void afterSessionOrEntityManagerFactoryBuilt() {
}
protected boolean createSchema() {
return true;
}
@After
public final void afterTest() throws Exception {
completeStrayTransaction();
cleanupSession();
}
@AfterClassOnce
@SuppressWarnings( {"UnusedDeclaration"})
protected void releaseSessionFactory() {
if ( sessionFactory == null ) {
return;
}
sessionFactory.close();
sessionFactory = null;
if ( serviceRegistry != null ) {
if ( serviceRegistry.isActive() ) {
try {
serviceRegistry.destroy();
}
catch (Exception ignore) {
}
fail( "StandardServiceRegistry was not closed down as expected" );
}
}
serviceRegistry=null;
}
private void completeStrayTransaction() {
if ( session == null ) {
// nothing to do
return;
}
if ( ( (SessionImplementor) session ).isClosed() ) {
// nothing to do
return;
}
if ( !session.isConnected() ) {
// nothing to do
return;
}
final TransactionCoordinator.TransactionDriver tdc =
( (SessionImplementor) session ).getTransactionCoordinator().getTransactionDriverControl();
if ( tdc.getStatus().canRollback() ) {
session.getTransaction().rollback();
}
session.close();
}
private void cleanupSession() {
if ( session != null && ! ( (SessionImplementor) session ).isClosed() ) {
session.close();
}
session = null;
}
}

View File

@ -0,0 +1,180 @@
/*
* 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.exceptionhandling;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.fail;
@TestForIssue(jiraKey = "HHH-12666")
public class ConstraintViolationExceptionHandlingTest extends BaseExceptionHandlingTest {
public ConstraintViolationExceptionHandlingTest(BootstrapMethod bootstrapMethod,
ExceptionHandlingSetting exceptionHandlingSetting,
ExceptionExpectations exceptionExpectations) {
super( bootstrapMethod, exceptionHandlingSetting, exceptionExpectations );
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
A.class,
AInfo.class
};
}
@Test
public void testConstraintViolationOnSave() {
Session s = openSession();
Transaction tx = s.beginTransaction();
AInfo aInfo = new AInfo();
aInfo.uniqueString = "unique";
s.persist( aInfo );
s.flush();
s.clear();
try {
AInfo anotherAInfo = new AInfo();
anotherAInfo.uniqueString = "unique";
s.save( anotherAInfo );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onConstraintViolationOnSaveAndSaveOrUpdate( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testConstraintViolationOnSaveOrUpdate() {
Session s = openSession();
Transaction tx = s.beginTransaction();
AInfo aInfo = new AInfo();
aInfo.uniqueString = "unique";
s.persist( aInfo );
s.flush();
s.clear();
try {
AInfo anotherAInfo = new AInfo();
anotherAInfo.uniqueString = "unique";
s.saveOrUpdate( anotherAInfo );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onConstraintViolationOnSaveAndSaveOrUpdate( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testConstraintViolationOnPersist() {
Session s = openSession();
Transaction tx = s.beginTransaction();
AInfo aInfo = new AInfo();
aInfo.uniqueString = "unique";
s.persist( aInfo );
s.flush();
s.clear();
try {
AInfo anotherAInfo = new AInfo();
anotherAInfo.uniqueString = "unique";
s.persist( anotherAInfo );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onConstraintViolationOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testConstraintViolationOnMerge() {
Session s = openSession();
Transaction tx = s.beginTransaction();
AInfo aInfo = new AInfo();
aInfo.uniqueString = "unique";
s.persist( aInfo );
s.flush();
s.clear();
try {
AInfo anotherAInfo = new AInfo();
anotherAInfo.uniqueString = "unique";
s.merge( anotherAInfo );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onConstraintViolationOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testConstraintViolationUpdateFlush() {
Session s = openSession();
Transaction tx = s.beginTransaction();
AInfo aInfo = new AInfo();
aInfo.uniqueString = "unique";
s.persist( aInfo );
AInfo aInfo1 = new AInfo();
s.persist( aInfo1 );
s.flush();
s.clear();
try {
aInfo1 = s.get( AInfo.class, aInfo1.id );
aInfo1.uniqueString = "unique";
s.flush();
}
catch (RuntimeException expected) {
exceptionExpectations.onConstraintViolationOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Entity(name = "A")
public static class A {
@Id
private long id;
@ManyToOne(optional = false)
private AInfo aInfo;
}
@Entity(name = "AInfo")
public static class AInfo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(unique = true)
private String uniqueString;
}
}

View File

@ -0,0 +1,108 @@
/*
* 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.exceptionhandling;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import org.hibernate.TransientObjectException;
import org.hibernate.exception.ConstraintViolationException;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;
interface ExceptionExpectations {
static ExceptionExpectations jpa() {
return new ExceptionExpectations() {
@Override
public void onConstraintViolationOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( PersistenceException.class ) );
}
@Override
public void onConstraintViolationOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( PersistenceException.class ) );
assertThat( e.getCause(), instanceOf( ConstraintViolationException.class ) );
assertThat( e.getCause().getCause(), instanceOf( SQLException.class ) );
}
@Override
public void onTransientObjectOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( TransientObjectException.class ) );
}
@Override
public void onTransientObjectOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( IllegalStateException.class ) );
assertThat( e.getCause(), instanceOf( TransientObjectException.class ) );
}
};
}
static ExceptionExpectations nativePre52() {
return new ExceptionExpectations() {
@Override
public void onConstraintViolationOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( ConstraintViolationException.class ) );
assertThat( e.getCause(), instanceOf( SQLException.class ) );
}
@Override
public void onConstraintViolationOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( ConstraintViolationException.class ) );
assertThat( e.getCause(), instanceOf( SQLException.class ) );
}
@Override
public void onTransientObjectOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( TransientObjectException.class ) );
}
@Override
public void onTransientObjectOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( TransientObjectException.class ) );
}
};
}
static ExceptionExpectations nativePost52() {
return new ExceptionExpectations() {
@Override
public void onConstraintViolationOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( ConstraintViolationException.class ) );
assertThat( e.getCause(), instanceOf( SQLException.class ) );
}
@Override
public void onConstraintViolationOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( PersistenceException.class ) );
assertThat( e.getCause(), instanceOf( ConstraintViolationException.class ) );
assertThat( e.getCause().getCause(), instanceOf( SQLException.class ) );
}
@Override
public void onTransientObjectOnSaveAndSaveOrUpdate(RuntimeException e) {
assertThat( e, instanceOf( TransientObjectException.class ) );
}
@Override
public void onTransientObjectOnPersistAndMergeAndFlush(RuntimeException e) {
assertThat( e, instanceOf( IllegalStateException.class ) );
assertThat( e.getCause(), instanceOf( TransientObjectException.class ) );
}
};
}
void onConstraintViolationOnSaveAndSaveOrUpdate(RuntimeException e);
void onConstraintViolationOnPersistAndMergeAndFlush(RuntimeException e);
void onTransientObjectOnSaveAndSaveOrUpdate(RuntimeException e);
void onTransientObjectOnPersistAndMergeAndFlush(RuntimeException e);
}

View File

@ -0,0 +1,155 @@
/*
* 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.exceptionhandling;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.fail;
@TestForIssue(jiraKey = "HHH-12666")
public class TransientObjectExceptionHandlingTest extends BaseExceptionHandlingTest {
public TransientObjectExceptionHandlingTest(BootstrapMethod bootstrapMethod,
ExceptionHandlingSetting exceptionHandlingSetting,
ExceptionExpectations exceptionExpectations) {
super( bootstrapMethod, exceptionHandlingSetting, exceptionExpectations );
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
A.class,
AInfo.class
};
}
@Test
public void testSave() {
Session s = openSession();
Transaction tx = s.beginTransaction();
A a = new A();
a.id = 1;
a.aInfo = new AInfo();
try {
s.save( a );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onTransientObjectOnSaveAndSaveOrUpdate( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testSaveOrUpdate() {
Session s = openSession();
Transaction tx = s.beginTransaction();
A a = new A();
a.id = 1;
a.aInfo = new AInfo();
try {
s.saveOrUpdate( a );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onTransientObjectOnSaveAndSaveOrUpdate( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testPersist() {
Session s = openSession();
Transaction tx = s.beginTransaction();
A a = new A();
a.id = 1;
a.aInfo = new AInfo();
try {
s.persist( a );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onTransientObjectOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testMerge() {
Session s = openSession();
Transaction tx = s.beginTransaction();
A a = new A();
a.id = 1;
a.aInfo = new AInfo();
try {
s.merge( a );
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onTransientObjectOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Test
public void testUpdateFlush() {
Session s = openSession();
Transaction tx = s.beginTransaction();
A a = new A();
a.id = 1;
a.aInfo = new AInfo();
try {
s.update( a );
s.flush();
fail( "should have thrown an exception" );
}
catch (RuntimeException expected) {
exceptionExpectations.onTransientObjectOnPersistAndMergeAndFlush( expected );
}
finally {
tx.rollback();
s.close();
}
}
@Entity(name = "A")
public static class A {
@Id
private long id;
@ManyToOne(optional = false)
private AInfo aInfo;
}
@Entity(name = "AInfo")
public static class AInfo {
@Id
@GeneratedValue
private long id;
}
}