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:
parent
8ef0ca9a13
commit
88fb16e5c9
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue