diff --git a/.github/workflows/contributor-build.yml b/.github/workflows/contributor-build.yml index 93931253fa..966e5a3011 100644 --- a/.github/workflows/contributor-build.yml +++ b/.github/workflows/contributor-build.yml @@ -73,7 +73,7 @@ jobs: with: name: test-reports-java8-${{ matrix.rdbms }} path: | - ./**/target/reports/tests/test/ + ./**/target/reports/tests/ ./**/target/reports/checkstyle/ - name: Omit produced artifacts from build cache run: ./ci/before-cache.sh @@ -112,7 +112,7 @@ jobs: with: name: test-reports-java11 path: | - ./**/target/reports/tests/test/ + ./**/target/reports/tests/ ./**/target/reports/checkstyle/ - name: Omit produced artifacts from build cache run: ./ci/before-cache.sh \ No newline at end of file diff --git a/docker_db.sh b/docker_db.sh index 2ab4d92e4c..8f9d2c0914 100755 --- a/docker_db.sh +++ b/docker_db.sh @@ -64,6 +64,32 @@ oracle() { # We need to use the defaults # SYSTEM/Oracle18 docker run --shm-size=1536m --name oracle -d -p 1521:1521 quillbuilduser/oracle-18-xe + until [ "`docker inspect -f {{.State.Health.Status}} oracle`" == "healthy" ]; + do + echo "Waiting for Oracle to start..." + sleep 10; + done + echo "Oracle successfully started" + # We increase file sizes to avoid online resizes as that requires lots of CPU which is restricted in XE + docker exec oracle bash -c "source /home/oracle/.bashrc; bash -c \" +cat < { //tag::hql-concat-function-example[] @@ -1312,6 +1314,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(SQLServerDialect.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") public void test_hql_current_date_function_example() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-current-date-function-example[] @@ -1383,6 +1386,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "See https://issues.apache.org/jira/browse/DERBY-2072") public void test_hql_cast_function_example() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-cast-function-example[] @@ -1396,6 +1400,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support extract function") public void test_hql_extract_function_example() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-extract-function-example[] @@ -1550,6 +1555,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") public void test_hql_collection_expressions_example_7() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-collection-expressions-example[] @@ -1565,6 +1571,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Comparisons between 'DATE' and 'TIMESTAMP' are not supported") public void test_hql_collection_expressions_example_8() { doInJPA( this::entityManagerFactory, entityManager -> { //tag::hql-collection-expressions-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java index a5110d5f8b..50fa6a64d2 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobCharArrayTest.java @@ -11,6 +11,7 @@ import javax.persistence.Id; import javax.persistence.Lob; import org.hibernate.annotations.Nationalized; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -27,9 +28,10 @@ import static org.junit.Assert.assertArrayEquals; @SkipForDialect( value = { PostgreSQL81Dialect.class, - MySQL5Dialect.class + MySQL5Dialect.class, + DB2Dialect.class }, - comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695" + comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473" ) public class NClobCharArrayTest extends BaseEntityManagerFunctionalTestCase { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java index 561d392284..03d20f9234 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobStringTest.java @@ -11,6 +11,7 @@ import javax.persistence.Id; import javax.persistence.Lob; import org.hibernate.annotations.Nationalized; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -27,9 +28,10 @@ import static org.junit.Assert.assertEquals; @SkipForDialect( value = { PostgreSQL81Dialect.class, - MySQL5Dialect.class + MySQL5Dialect.class, + DB2Dialect.class }, - comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695" + comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473" ) public class NClobStringTest extends BaseEntityManagerFunctionalTestCase { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java index 3e654931a6..787fbe9d5c 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NClobTest.java @@ -19,6 +19,7 @@ import org.hibernate.Session; import org.hibernate.annotations.Nationalized; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.engine.jdbc.NClobProxy; @@ -39,9 +40,10 @@ import static org.junit.Assert.fail; PostgreSQL81Dialect.class, MySQL5Dialect.class, AbstractHANADialect.class, - CockroachDialect.class + CockroachDialect.class, + DB2Dialect.class }, - comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695" + comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and https://hibernate.atlassian.net/browse/HHH-10695 and https://hibernate.atlassian.net/browse/HHH-10473" ) public class NClobTest extends BaseEntityManagerFunctionalTestCase { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java index 5a7cb3f6dd..22ba662aa2 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/NationalizedTest.java @@ -10,6 +10,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import org.hibernate.annotations.Nationalized; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -24,9 +25,10 @@ import static org.junit.Assert.assertEquals; */ @SkipForDialect( value = { - PostgreSQL81Dialect.class + PostgreSQL81Dialect.class, + DerbyDialect.class }, - comment = "@see https://hibernate.atlassian.net/browse/HHH-10693" + comment = "@see https://hibernate.atlassian.net/browse/HHH-10693 and Derby doesn't support nationalized type" ) public class NationalizedTest extends BaseEntityManagerFunctionalTestCase { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java index 447cfbb9fa..d64f14813f 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/SubselectTest.java @@ -15,8 +15,10 @@ import javax.persistence.Table; import org.hibernate.annotations.Subselect; import org.hibernate.annotations.Synchronize; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.SkipForDialect; import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; @@ -25,6 +27,7 @@ import static org.junit.Assert.assertEquals; /** * @author Vlad Mihalcea */ +@SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support a CONCAT function") public class SubselectTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/TypeCategoryTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/TypeCategoryTest.java index d3aae6afa3..7ec10e9e01 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/TypeCategoryTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/TypeCategoryTest.java @@ -59,11 +59,11 @@ public class TypeCategoryTest extends BaseEntityManagerFunctionalTestCase { @Embeddable public class Name { - private String first; + private String firstName; - private String middle; + private String middleName; - private String last; + private String lastName; // getters and setters omitted } diff --git a/gradle/databases.gradle b/gradle/databases.gradle index c95e113e75..766610886b 100644 --- a/gradle/databases.gradle +++ b/gradle/databases.gradle @@ -45,7 +45,16 @@ ext { 'jdbc.driver': 'org.postgresql.Driver', 'jdbc.user' : 'hibernate_orm_test', 'jdbc.pass' : 'hibernate_orm_test', - 'jdbc.url' : 'jdbc:postgresql://127.0.0.1/hibernate_orm_test' + // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com + 'jdbc.url' : 'jdbc:postgresql://127.0.0.1/hibernate_orm_test?preparedStatementCacheQueries=0' + ], + pgsql_ci : [ + 'db.dialect' : 'org.hibernate.dialect.PostgreSQL95Dialect', + 'jdbc.driver': 'org.postgresql.Driver', + 'jdbc.user' : 'hibernate_orm_test', + 'jdbc.pass' : 'hibernate_orm_test', + // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com + 'jdbc.url' : 'jdbc:postgresql://' + dbHost + '/hibernate_orm_test?preparedStatementCacheQueries=0' ], pgsql_ci : [ 'db.dialect' : 'org.hibernate.dialect.PostgreSQLDialect', @@ -175,7 +184,8 @@ ext { 'jdbc.driver': 'org.postgresql.Driver', 'jdbc.user' : 'root', 'jdbc.pass' : '', - 'jdbc.url' : 'jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable' + // Disable prepared statement caching due to https://www.postgresql.org/message-id/CAEcMXhmmRd4-%2BNQbnjDT26XNdUoXdmntV9zdr8%3DTu8PL9aVCYg%40mail.gmail.com + 'jdbc.url' : 'jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0' ], ] } diff --git a/gradle/java-module.gradle b/gradle/java-module.gradle index 07951fc82a..bb81425bff 100644 --- a/gradle/java-module.gradle +++ b/gradle/java-module.gradle @@ -102,6 +102,7 @@ dependencies { //Databases testRuntime( libraries.h2 ) + testRuntime( libraries.derby ) testRuntime( libraries.hsqldb ) testRuntime( libraries.postgresql ) testRuntime( libraries.mysql ) @@ -111,13 +112,15 @@ dependencies { testRuntime( libraries.hana ) testRuntime( libraries.cockroachdb ) - if ( db.startsWith( 'oracle' ) ) { - testRuntime( libraries.oracle ) - } - else if ( db.startsWith( 'db2' ) ) { + testRuntime( libraries.oracle ) + + // Since both the DB2 driver and HANA have a package "net.jpountz" we have to add dependencies conditionally + // This is due to the "no split-packages" requirement of Java 9+ + + if ( db.startsWith( 'db2' ) ) { testRuntime( libraries.db2 ) } - else if ( db.equalsIgnoreCase( 'hana' ) ) { + else if ( db.startsWith( 'hana' ) ) { testRuntime( libraries.hana ) } diff --git a/hibernate-agroal/src/test/java/org/hibernate/test/agroal/util/PreparedStatementSpyConnectionProvider.java b/hibernate-agroal/src/test/java/org/hibernate/test/agroal/util/PreparedStatementSpyConnectionProvider.java index 43b8ba31e8..1f9025f42a 100644 --- a/hibernate-agroal/src/test/java/org/hibernate/test/agroal/util/PreparedStatementSpyConnectionProvider.java +++ b/hibernate-agroal/src/test/java/org/hibernate/test/agroal/util/PreparedStatementSpyConnectionProvider.java @@ -53,7 +53,7 @@ public class PreparedStatementSpyConnectionProvider extends AgroalConnectionProv public void closeConnection(Connection conn) throws SQLException { acquiredConnections.remove( conn ); releasedConnections.add( conn ); - super.closeConnection( conn ); + super.closeConnection( (Connection) MockUtil.getMockSettings( conn ).getSpiedInstance() ); } @Override diff --git a/hibernate-c3p0/src/test/resources/hibernate.properties b/hibernate-c3p0/src/test/resources/hibernate.properties index 332d78bd38..0d39da782e 100644 --- a/hibernate-c3p0/src/test/resources/hibernate.properties +++ b/hibernate-c3p0/src/test/resources/hibernate.properties @@ -22,6 +22,7 @@ hibernate.c3p0.testConnectionOnCheckout true hibernate.show_sql false hibernate.max_fetch_depth 5 +hibernate.connection.provider_class C3P0ConnectionProvider hibernate.cache.region_prefix hibernate.test hibernate.cache.region.factory_class org.hibernate.testing.cache.CachingRegionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java index 50a1f2d98e..3cb7ab2882 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryBuilderImpl.java @@ -461,7 +461,6 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement @Override public SessionFactory build() { - metadata.validate(); final StandardServiceRegistry serviceRegistry = metadata.getMetadataBuildingOptions().getServiceRegistry(); BytecodeProvider bytecodeProvider = serviceRegistry.getService( BytecodeProvider.class ); addSessionFactoryObservers( new SessionFactoryObserverForBytecodeEnhancer( bytecodeProvider ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedTableName.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedTableName.java index 14c63c15d8..c6c110ab05 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedTableName.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedTableName.java @@ -23,4 +23,20 @@ public class QualifiedTableName extends QualifiedNameImpl { public Identifier getTableName() { return getObjectName(); } + + public QualifiedTableName quote() { + Identifier catalogName = getCatalogName(); + if ( catalogName != null ) { + catalogName = new Identifier( catalogName.getText(), true ); + } + Identifier schemaName = getSchemaName(); + if ( schemaName != null ) { + schemaName = new Identifier( schemaName.getText(), true ); + } + Identifier tableName = getTableName(); + if ( tableName != null ) { + tableName = new Identifier( tableName.getText(), true ); + } + return new QualifiedTableName( catalogName, schemaName, tableName ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index d3eb763e57..ee7dabac21 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -652,7 +652,7 @@ public class DB2Dialect extends Dialect { nullPrecedence == NullPrecedence.FIRST ? "0" : "1", nullPrecedence == NullPrecedence.FIRST ? "1" : "0", expression, - order + order == null ? "asc" : order ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index d9686a2657..cc434ee5d4 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -800,4 +800,9 @@ public class DerbyDialect extends Dialect { public GroupBySummarizationRenderingStrategy getGroupBySummarizationRenderingStrategy() { return GroupBySummarizationRenderingStrategy.FUNCTION; } + + @Override + public boolean supportsPartitionBy() { + return false; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionValidator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionValidator.java new file mode 100644 index 0000000000..61fc9d2145 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/ConnectionValidator.java @@ -0,0 +1,28 @@ +/* + * 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 . + */ +package org.hibernate.engine.jdbc.connections.internal; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Contract for validating JDBC Connections + * + * @author Christian Beikov + */ +public interface ConnectionValidator { + + ConnectionValidator ALWAYS_VALID = connection -> true; + + /** + * Checks if the connection is still valid + * + * @return true if the connection is valid, false otherwise + * @throws SQLException when an error happens due to the connection usage leading to a connection close + */ + boolean isValid(Connection connection) throws SQLException; +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java index 2e8bcf0c3d..33dc6f8b8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java @@ -50,7 +50,7 @@ import static org.hibernate.internal.log.ConnectionPoolingLogger.CONNECTIONS_MES * @author Steve Ebersole */ public class DriverManagerConnectionProviderImpl - implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService { + implements ConnectionProvider, Configurable, Stoppable, ServiceRegistryAwareService, ConnectionValidator { public static final String MIN_SIZE = "hibernate.connection.min_pool_size"; public static final String INITIAL_SIZE = "hibernate.connection.initial_pool_size"; @@ -95,7 +95,7 @@ public class DriverManagerConnectionProviderImpl pooledConnectionBuilder.initialSize( initialSize ); pooledConnectionBuilder.minSize( minSize ); pooledConnectionBuilder.maxSize( maxSize ); - + pooledConnectionBuilder.validator( this ); return pooledConnectionBuilder.build(); } @@ -247,6 +247,12 @@ public class DriverManagerConnectionProviderImpl } } + protected void validateConnectionsReturned() { + int allocationCount = state.pool.allConnections.size() - state.pool.availableConnections.size(); + if ( allocationCount != 0 ) { + CONNECTIONS_MESSAGE_LOGGER.error( "Connection leak detected: there are " + allocationCount + " unclosed connections!"); + } + } // destroy the pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -254,6 +260,7 @@ public class DriverManagerConnectionProviderImpl public void stop() { if ( state != null ) { state.stop(); + validateConnectionsReturned(); } } @@ -275,6 +282,10 @@ public class DriverManagerConnectionProviderImpl return connectionCreator.getConnectionProperties(); } + @Override + public boolean isValid(Connection connection) throws SQLException { + return true; + } public static class PooledConnections { @@ -282,6 +293,7 @@ public class DriverManagerConnectionProviderImpl private final ConcurrentLinkedQueue availableConnections = new ConcurrentLinkedQueue<>(); private final ConnectionCreator connectionCreator; + private final ConnectionValidator connectionValidator; private final boolean autoCommit; private final int minSize; private final int maxSize; @@ -292,6 +304,9 @@ public class DriverManagerConnectionProviderImpl Builder builder) { CONNECTIONS_LOGGER.debugf( "Initializing Connection pool with %s Connections", builder.initialSize ); connectionCreator = builder.connectionCreator; + connectionValidator = builder.connectionValidator == null + ? ConnectionValidator.ALWAYS_VALID + : builder.connectionValidator; autoCommit = builder.autoCommit; maxSize = builder.maxSize; minSize = builder.minSize; @@ -322,26 +337,79 @@ public class DriverManagerConnectionProviderImpl } public void add(Connection conn) throws SQLException { - conn.setAutoCommit( true ); - conn.clearWarnings(); - availableConnections.offer( conn ); + final Connection connection = releaseConnection( conn ); + if ( connection != null ) { + availableConnections.offer( connection ); + } + } + + protected Connection releaseConnection(Connection conn) { + Exception t = null; + try { + conn.setAutoCommit( true ); + conn.clearWarnings(); + if ( connectionValidator.isValid( conn ) ) { + return conn; + } + } + catch (SQLException ex) { + t = ex; + } + closeConnection( conn, t ); + CONNECTIONS_MESSAGE_LOGGER.debug( "Connection release failed. Closing pooled connection", t ); + return null; } public Connection poll() throws SQLException { - Connection conn = availableConnections.poll(); - if ( conn == null ) { - synchronized (allConnections) { - if(allConnections.size() < maxSize) { - addConnections( 1 ); - return poll(); + Connection conn; + do { + conn = availableConnections.poll(); + if ( conn == null ) { + synchronized (allConnections) { + if ( allConnections.size() < maxSize ) { + addConnections( 1 ); + return poll(); + } } + throw new HibernateException( + "The internal connection pool has reached its maximum size and no connection is currently available!" ); } - throw new HibernateException( "The internal connection pool has reached its maximum size and no connection is currently available!" ); - } - conn.setAutoCommit( autoCommit ); + conn = prepareConnection( conn ); + } while ( conn == null ); return conn; } + protected Connection prepareConnection(Connection conn) { + Exception t = null; + try { + conn.setAutoCommit( autoCommit ); + if ( connectionValidator.isValid( conn ) ) { + return conn; + } + } + catch (SQLException ex) { + t = ex; + } + closeConnection( conn, t ); + CONNECTIONS_MESSAGE_LOGGER.debug( "Connection preparation failed. Closing pooled connection", t ); + return null; + } + + protected void closeConnection(Connection conn, Throwable t) { + try { + conn.close(); + } + catch (SQLException ex) { + CONNECTIONS_MESSAGE_LOGGER.unableToCloseConnection( ex ); + if ( t != null ) { + t.addSuppressed( ex ); + } + } + finally { + allConnections.remove( conn ); + } + } + public void close() throws SQLException { try { int allocationCount = allConnections.size() - availableConnections.size(); @@ -389,6 +457,7 @@ public class DriverManagerConnectionProviderImpl public static class Builder { private final ConnectionCreator connectionCreator; + private ConnectionValidator connectionValidator; private boolean autoCommit; private int initialSize = 1; private int minSize = 1; @@ -414,6 +483,11 @@ public class DriverManagerConnectionProviderImpl return this; } + public Builder validator(ConnectionValidator connectionValidator) { + this.connectionValidator = connectionValidator; + return this; + } + public PooledConnections build() { return new PooledConnections( this ); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java index 07a7bd228c..48bb777680 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventEngine.java @@ -66,7 +66,7 @@ public class EventEngine { for ( Iterator propertyIterator = persistentClass.getDeclaredPropertyIterator(); propertyIterator.hasNext(); ) { final Property property = propertyIterator.next(); - if ( property.getType().isComponentType() ) { + if ( property.isComposite() ) { this.callbackBuilder.buildCallbacksForEmbeddable( property, persistentClass.getMappedClass(), diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index f1a79d0310..0c1b48af37 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -228,6 +228,15 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont transactionCoordinator.invalidate(); } + protected void prepareForAutoClose() { + waitingForAutoClose = true; + closed = true; + // For non-shared transaction coordinators, we have to add the observer + if ( !isTransactionCoordinatorShared ) { + addSharedSessionTransactionObserver( transactionCoordinator ); + } + } + @Override public boolean shouldAutoJoinTransaction() { return autoJoinTransactions; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 57e8ec4251..5d6e312975 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -293,6 +293,7 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { ); identifierGenerators.put( model.getEntityName(), generator ); } ); + bootMetamodel.validate(); LOG.debug( "Instantiated session factory" ); @@ -314,15 +315,6 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { queryEngine.getNamedObjectRepository().checkNamedQueries( queryEngine ); } - // todo (6.0) : manage old getMultiTableBulkIdStrategy - -// settings.getMultiTableBulkIdStrategy().prepare( -// jdbcServices, -// buildLocalConnectionAccess(), -// metadata, -// sessionFactoryOptions -// ); - SchemaManagementToolCoordinator.process( bootMetamodel, serviceRegistry, @@ -815,9 +807,6 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { LOG.closing(); observer.sessionFactoryClosing( this ); - // todo (6.0) : manage old getMultiTableBulkIdStrategy -// settings.getMultiTableBulkIdStrategy().release( serviceRegistry.getService( JdbcServices.class ), buildLocalConnectionAccess() ); - // NOTE : the null checks below handle cases where close is called from // a failed attempt to create the SessionFactory @@ -826,6 +815,14 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { } if ( runtimeMetamodels != null ) { + final JdbcConnectionAccess jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess(); + runtimeMetamodels.getMappingMetamodel().visitEntityDescriptors( + entityPersister -> { + if ( entityPersister.getSqmMultiTableMutationStrategy() != null ) { + entityPersister.getSqmMultiTableMutationStrategy().release( this, jdbcConnectionAccess ); + } + } + ); ( (MappingMetamodelImpl) runtimeMetamodels.getMappingMetamodel() ).close(); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 18100ff751..dc98d0435e 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -347,8 +347,7 @@ public class SessionImpl } else { //Otherwise, session auto-close will be enabled by shouldAutoCloseSession(). - waitingForAutoClose = true; - closed = true; + prepareForAutoClose(); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 7f105070ca..eb992b16b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -55,6 +55,7 @@ import org.hibernate.bytecode.enhance.spi.UnloadedClass; import org.hibernate.bytecode.enhance.spi.UnloadedField; import org.hibernate.cfg.Environment; import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.id.factory.spi.MutableIdentifierGeneratorFactory; import org.hibernate.integrator.spi.Integrator; @@ -78,7 +79,9 @@ import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.secure.spi.GrantedPermission; import org.hibernate.secure.spi.JaccPermissionDeclarations; import org.hibernate.service.ServiceRegistry; +import org.hibernate.service.spi.ServiceBinding; import org.hibernate.service.spi.ServiceRegistryImplementor; +import org.hibernate.service.spi.Stoppable; import org.hibernate.tool.schema.spi.DelayedDropRegistryNotAvailableImpl; import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator; @@ -1237,9 +1240,24 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil @Override public void cancel() { + cleanup(); // todo : close the bootstrap registry (not critical, but nice to do) } + private void cleanup() { + // Stop and de-register the ConnectionProvider to prevent connections lying around + if ( standardServiceRegistry instanceof ServiceRegistryImplementor && + standardServiceRegistry instanceof ServiceBinding.ServiceLifecycleOwner ) { + final ServiceRegistryImplementor serviceRegistry = (ServiceRegistryImplementor) standardServiceRegistry; + final ServiceBinding.ServiceLifecycleOwner lifecycleOwner = (ServiceBinding.ServiceLifecycleOwner) serviceRegistry; + final ServiceBinding binding = serviceRegistry.locateServiceBinding( ConnectionProvider.class ); + if ( binding != null && binding.getService() instanceof Stoppable ) { + lifecycleOwner.stopService( binding ); + binding.setService( null ); + } + } + } + @Override public void generateSchema() { // This seems overkill, but building the SF is necessary to get the Integrators to kick in. @@ -1255,21 +1273,32 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil catch (Exception e) { throw persistenceException( "Error performing schema management", e ); } - - // release this builder - cancel(); + finally { + // release this builder + cancel(); + } } @Override public EntityManagerFactory build() { - final SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); - populateSfBuilder( sfBuilder, standardServiceRegistry ); - + boolean success = false; try { - return sfBuilder.build(); + final SessionFactoryBuilder sfBuilder = metadata().getSessionFactoryBuilder(); + populateSfBuilder( sfBuilder, standardServiceRegistry ); + + try { + final EntityManagerFactory emf = sfBuilder.build(); + success = true; + return emf; + } + catch (Exception e) { + throw persistenceException( "Unable to build Hibernate SessionFactory", e ); + } } - catch (Exception e) { - throw persistenceException( "Unable to build Hibernate SessionFactory", e ); + finally { + if ( !success ) { + cleanup(); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 356adfb57d..2ea970c022 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -6044,6 +6044,12 @@ public abstract class AbstractEntityPersister if ( sqmMultiTableMutationStrategy == null ) { return false; } + sqmMultiTableMutationStrategy.prepare( + creationProcess, + creationContext.getSessionFactory() + .getJdbcServices() + .getBootstrapJdbcConnectionAccess() + ); return true; } ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java index 9dd18d5ac5..c9864ec833 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/GlobalTemporaryTableStrategy.java @@ -11,6 +11,9 @@ import java.sql.SQLException; import java.util.function.Supplier; import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; @@ -40,8 +43,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate private final SessionFactoryImplementor sessionFactory; private boolean prepared; - private boolean created; - private boolean released; + private boolean dropIdTables; public GlobalTemporaryTableStrategy( IdTable idTable, @@ -131,7 +133,14 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate try { idTableCreationWork.execute( connection ); - created = true; + final ConfigurationService configService = mappingModelCreationProcess.getCreationContext() + .getBootstrapContext() + .getServiceRegistry().getService( ConfigurationService.class ); + this.dropIdTables = configService.getSetting( + DROP_ID_TABLES, + StandardConverters.BOOLEAN, + false + ); } finally { try { @@ -140,25 +149,17 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate catch (SQLException ignore) { } } - - if ( created ) { - // todo (6.0) : register strategy for dropping of the table if requested - DROP_ID_TABLES - } } @Override public void release( SessionFactoryImplementor sessionFactory, JdbcConnectionAccess connectionAccess) { - if ( released ) { + if ( !dropIdTables ) { return; } - released = true; - - if ( ! created ) { - return; - } + dropIdTables = false; log.debugf( "Dropping global-temp ID table : %s", idTable.getTableExpression() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java index 9e20969745..89e118662c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java @@ -6,24 +6,35 @@ */ package org.hibernate.query.sqm.mutation.internal.idtable; +import java.sql.Connection; +import java.sql.SQLException; import java.util.function.Function; import java.util.function.Supplier; import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.config.spi.StandardConverters; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.jboss.logging.Logger; + /** * Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session). * * @author Steve Ebersole */ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy { + private static final Logger log = Logger.getLogger( LocalTemporaryTableStrategy.class ); + public static final String SHORT_NAME = "local_temporary"; + public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.local_temporary.drop_tables"; private final IdTable idTable; private final Supplier idTableExporterAccess; @@ -31,6 +42,8 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg private final TempTableDdlTransactionHandling ddlTransactionHandling; private final SessionFactoryImplementor sessionFactory; + private boolean dropIdTables; + public LocalTemporaryTableStrategy( IdTable idTable, Supplier idTableExporterAccess, @@ -98,4 +111,61 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg sessionFactory ).execute( context ); } + + @Override + public void prepare( + MappingModelCreationProcess mappingModelCreationProcess, + JdbcConnectionAccess connectionAccess) { + final ConfigurationService configService = mappingModelCreationProcess.getCreationContext() + .getBootstrapContext() + .getServiceRegistry().getService( ConfigurationService.class ); + this.dropIdTables = configService.getSetting( + DROP_ID_TABLES, + StandardConverters.BOOLEAN, + false + ); + } + + @Override + public void release( + SessionFactoryImplementor sessionFactory, + JdbcConnectionAccess connectionAccess) { + if ( !dropIdTables ) { + return; + } + + dropIdTables = false; + + log.debugf( "Dropping local-temp ID table : %s", idTable.getTableExpression() ); + + final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork( + idTable, + idTableExporterAccess.get(), + sessionFactory + ); + Connection connection; + try { + connection = connectionAccess.obtainConnection(); + } + catch (UnsupportedOperationException e) { + // assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl + log.debugf( "Unable to obtain JDBC connection; unable to drop local-temp ID table : %s", idTable.getTableExpression() ); + return; + } + catch (SQLException e) { + log.error( "Unable obtain JDBC Connection", e ); + return; + } + + try { + idTableDropWork.execute( connection ); + } + finally { + try { + connectionAccess.releaseConnection( connection ); + } + catch (SQLException ignore) { + } + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/DdlTransactionIsolatorNonJtaImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/DdlTransactionIsolatorNonJtaImpl.java index 0da21d016b..3cee311244 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/DdlTransactionIsolatorNonJtaImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jdbc/internal/DdlTransactionIsolatorNonJtaImpl.java @@ -22,6 +22,7 @@ public class DdlTransactionIsolatorNonJtaImpl implements DdlTransactionIsolator private final JdbcContext jdbcContext; private Connection jdbcConnection; + private boolean unsetAutoCommit; public DdlTransactionIsolatorNonJtaImpl(JdbcContext jdbcContext) { this.jdbcContext = jdbcContext; @@ -49,6 +50,7 @@ public class DdlTransactionIsolatorNonJtaImpl implements DdlTransactionIsolator try { jdbcConnection.commit(); jdbcConnection.setAutoCommit( true ); + unsetAutoCommit = true; } catch (SQLException e) { throw jdbcContext.getSqlExceptionHelper().convert( @@ -80,10 +82,28 @@ public class DdlTransactionIsolatorNonJtaImpl implements DdlTransactionIsolator public void release() { if ( jdbcConnection != null ) { try { - jdbcContext.getJdbcConnectionAccess().releaseConnection( jdbcConnection ); + if ( unsetAutoCommit ) { + try { + jdbcConnection.setAutoCommit( false ); + } + catch (SQLException e) { + throw jdbcContext.getSqlExceptionHelper().convert( + e, + "Unable to set auto commit to false for JDBC Connection used for DDL execution" + ); + } + } } - catch (SQLException e) { - throw jdbcContext.getSqlExceptionHelper().convert( e, "Unable to release JDBC Connection used for DDL execution" ); + finally { + try { + jdbcContext.getJdbcConnectionAccess().releaseConnection( jdbcConnection ); + } + catch (SQLException e) { + throw jdbcContext.getSqlExceptionHelper().convert( + e, + "Unable to release JDBC Connection used for DDL execution" + ); + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/DdlTransactionIsolatorJtaImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/DdlTransactionIsolatorJtaImpl.java index 643bf73c4e..88cd7eb316 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/DdlTransactionIsolatorJtaImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/DdlTransactionIsolatorJtaImpl.java @@ -29,8 +29,8 @@ public class DdlTransactionIsolatorJtaImpl implements DdlTransactionIsolator { private final JdbcContext jdbcContext; - private Transaction suspendedTransaction; - private Connection jdbcConnection; + private final Transaction suspendedTransaction; + private final Connection jdbcConnection; public DdlTransactionIsolatorJtaImpl(JdbcContext jdbcContext) { this.jdbcContext = jdbcContext; diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java index 2e7ca0f13e..967656d134 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java @@ -8,7 +8,9 @@ package org.hibernate.tool.schema.extract.internal; import java.sql.DatabaseMetaData; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -540,62 +542,53 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl implements Information } private void addColumns(TableInformation tableInformation) { - final QualifiedTableName tableName = tableInformation.getName(); - final Identifier catalog = tableName.getCatalogName(); - final Identifier schema = tableName.getSchemaName(); + // We use this dummy query to retrieve the table information through the ResultSetMetaData + // This is significantly better than to use the DatabaseMetaData especially on Oracle with synonyms enabled + final String tableName = extractionContext.getJdbcEnvironment().getQualifiedObjectNameFormatter().format( + // The name comes from the database, so the case is correct + // But we quote here to avoid issues with reserved words + tableInformation.getName().quote(), + extractionContext.getJdbcEnvironment().getDialect() + ); + final String query = "select * from " + tableName + " where 1=0"; + try (Statement statement = extractionContext.getJdbcConnection() + .createStatement(); ResultSet resultSet = statement.executeQuery( query )) { + final ResultSetMetaData metaData = resultSet.getMetaData(); + final int columnCount = metaData.getColumnCount(); - final String catalogFilter; - final String schemaFilter; - - if ( catalog == null ) { - catalogFilter = ""; - } - else { - catalogFilter = catalog.getText(); - } - - if ( schema == null ) { - schemaFilter = ""; - } - else { - schemaFilter = schema.getText(); - } - - try { - ResultSet resultSet = extractionContext.getJdbcDatabaseMetaData().getColumns( - catalogFilter, - schemaFilter, - tableName.getTableName().getText(), - "%" - ); - - try { - while ( resultSet.next() ) { - final String columnName = resultSet.getString( "COLUMN_NAME" ); - final ColumnInformationImpl columnInformation = new ColumnInformationImpl( - tableInformation, - DatabaseIdentifier.toIdentifier( columnName ), - resultSet.getInt( "DATA_TYPE" ), - new StringTokenizer( resultSet.getString( "TYPE_NAME" ), "() " ).nextToken(), - resultSet.getInt( "COLUMN_SIZE" ), - resultSet.getInt( "DECIMAL_DIGITS" ), - interpretTruthValue( resultSet.getString( "IS_NULLABLE" ) ) - ); - tableInformation.addColumn( columnInformation ); - } - } - finally { - resultSet.close(); + for ( int i = 1; i <= columnCount; i++ ) { + final String columnName = metaData.getColumnName( i ); + final ColumnInformationImpl columnInformation = new ColumnInformationImpl( + tableInformation, + DatabaseIdentifier.toIdentifier( columnName ), + metaData.getColumnType( i ), + new StringTokenizer( metaData.getColumnTypeName( i ), "() " ).nextToken(), + metaData.getPrecision( i ), + metaData.getScale( i ), + interpretNullable( metaData.isNullable( i ) ) + ); + tableInformation.addColumn( columnInformation ); } } catch (SQLException e) { throw convertSQLException( e, - "Error accessing column metadata: " + tableName.toString() + "Error accessing column metadata: " + tableInformation.getName().toString() ); } } + private TruthValue interpretNullable(int nullable) { + switch ( nullable ) { + case ResultSetMetaData.columnNullable: + return TruthValue.TRUE; + case ResultSetMetaData.columnNoNulls: + return TruthValue.FALSE; + default: + return TruthValue.UNKNOWN; + } + } + private TruthValue interpretTruthValue(String nullable) { if ( "yes".equalsIgnoreCase( nullable ) ) { return TruthValue.TRUE; diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/TypeConfigurationRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/internal/TypeConfigurationRegistry.java deleted file mode 100644 index 7d8e039aed..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/TypeConfigurationRegistry.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.type.internal; - -import java.util.concurrent.ConcurrentHashMap; - -import org.hibernate.type.spi.TypeConfiguration; - -import org.jboss.logging.Logger; - -/** - * A Registry of TypeConfiguration references based on the - * TypeConfiguration's UUID. - * - * @author Steve Ebersole - */ -public class TypeConfigurationRegistry { - private static final Logger LOG = Logger.getLogger( TypeConfigurationRegistry.class ); - - /** - * Singleton access - */ - public static final TypeConfigurationRegistry INSTANCE = new TypeConfigurationRegistry(); - - private TypeConfigurationRegistry() { - } - - private ConcurrentHashMap configurationMap; - - public void registerTypeConfiguration(TypeConfiguration typeConfiguration) { - if ( configurationMap == null ) { - configurationMap = new ConcurrentHashMap<>(); - } - configurationMap.put( typeConfiguration.getUuid(), typeConfiguration ); - } - - public TypeConfiguration findTypeConfiguration(String uuid) { - if ( configurationMap == null ) { - return null; - } - - return configurationMap.get( uuid ); - } - - public void deregisterTypeConfiguration(TypeConfiguration typeConfiguration) { - final TypeConfiguration existing = configurationMap.remove( typeConfiguration.getUuid() ); - if ( existing != typeConfiguration ) { - LOG.debugf( - "Different TypeConfiguration [%s] passed to #deregisterTypeConfiguration than previously registered [%s] under that UUID [%s]", - typeConfiguration, - existing, - typeConfiguration.getUuid() - ); - } - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java index cf1d42e917..97f3601d4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/type/spi/TypeConfiguration.java @@ -54,7 +54,6 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators; import org.hibernate.type.descriptor.sql.spi.SqlTypeDescriptorRegistry; import org.hibernate.type.internal.StandardBasicTypeImpl; -import org.hibernate.type.internal.TypeConfigurationRegistry; import java.sql.Time; import java.sql.Timestamp; @@ -106,8 +105,6 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { this.basicTypeRegistry = new BasicTypeRegistry( this ); StandardBasicTypes.prime( this ); - - TypeConfigurationRegistry.INSTANCE.registerTypeConfiguration( this ); } public String getUuid() { @@ -211,8 +208,6 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { public void sessionFactoryClosed(SessionFactory factory) { log.tracef( "Handling #sessionFactoryClosed from [%s] for TypeConfiguration", factory ); - TypeConfigurationRegistry.INSTANCE.deregisterTypeConfiguration( this ); - scope.unsetSessionFactory( factory ); // todo (6.0) : finish this @@ -440,18 +435,6 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable { } - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Custom serialization hook - - private Object readResolve() throws InvalidObjectException { - log.trace( "Resolving serialized TypeConfiguration - readResolve" ); - return TypeConfigurationRegistry.INSTANCE.findTypeConfiguration( getUuid() ); - } - - - - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java index 36752b05b6..7e850cb919 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java @@ -25,10 +25,15 @@ import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; + +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.After; import org.junit.Before; @@ -223,6 +228,14 @@ public abstract class BaseEntityManagerFunctionalTestCase extends BaseUnitTestCa config.put( AvailableSettings.XML_FILE_NAMES, dds ); } + if ( !config.containsKey( Environment.CONNECTION_PROVIDER ) ) { + config.put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + config.put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + config.put( + AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } addConfigOptions( config ); return config; } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/TreatKeywordTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/TreatKeywordTest.java index 4701f51c14..422ddc7e89 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/TreatKeywordTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/TreatKeywordTest.java @@ -254,14 +254,14 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase { em.close(); } - @Entity + @Entity(name = "TreatAnimal") public static abstract class TreatAnimal { @Id @GeneratedValue private Long id; } - @Entity + @Entity(name = "Dog") public static abstract class Dog extends TreatAnimal { private boolean fast; @@ -274,14 +274,14 @@ public class TreatKeywordTest extends BaseEntityManagerFunctionalTestCase { } } - @Entity + @Entity(name = "Dachshund") public static class Dachshund extends Dog { public Dachshund() { super( false ); } } - @Entity + @Entity(name = "Greyhound") public static class Greyhound extends Dog { public Greyhound() { super( true ); diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java index 96593860f0..e0f08b7643 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/basic/PredicateTest.java @@ -14,6 +14,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle12cDialect; import org.hibernate.jpa.test.metamodel.AbstractMetamodelSpecificTest; import org.hibernate.jpa.test.metamodel.CreditCard; @@ -300,6 +301,7 @@ public class PredicateTest extends AbstractMetamodelSpecificTest { @Test @TestForIssue( jiraKey = "HHH-8901" ) @RequiresDialectFeature( DialectChecks.NotSupportsEmptyInListCheck.class ) + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't like `x in (null)`") public void testEmptyInPredicate() { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralWithSingleQuoteTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralWithSingleQuoteTest.java index b082ce2f6f..987144359a 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralWithSingleQuoteTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/literal/CriteriaLiteralWithSingleQuoteTest.java @@ -16,6 +16,7 @@ import javax.persistence.criteria.CriteriaQuery; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.PostgreSQL81Dialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.SkipForDialect; @@ -64,11 +65,11 @@ public class CriteriaLiteralWithSingleQuoteTest extends BaseEntityManagerFunctio @Test @SkipForDialects( value = { + @SkipForDialect(value = SQLServerDialect.class, comment = "SQLServer does not support literals in group by statement"), @SkipForDialect(value = PostgreSQL81Dialect.class, comment = "PostgreSQL does not support literals in group by statement"), @SkipForDialect( value = CockroachDialect.class, comment = "CockroachDB does not support literals in group by statement") } ) - @SkipForDialect(value = PostgreSQL81Dialect.class, comment = "PostgreSQL does not support literals in group by statement") public void testLiteralProjectionAndGroupBy() throws Exception { doInJPA( this::entityManagerFactory, diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/GroupBySelectCaseTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/GroupBySelectCaseTest.java index f68e6b9d86..ee4acdf8fd 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/GroupBySelectCaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/GroupBySelectCaseTest.java @@ -17,6 +17,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.PostgreSQL95Dialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.metadata.Person_; @@ -32,6 +33,7 @@ import static org.junit.Assert.assertTrue; @TestForIssue(jiraKey = "HHH-12230") @SkipForDialect(value = DB2Dialect.class, comment = "We would need casts in the case clauses. See HHH-12822.") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby requires either casted parameters or literals in the result arms of CASE expressions") public class GroupBySelectCaseTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseLiteralHandlingBindTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseLiteralHandlingBindTest.java index faa9c27195..8b7572b5db 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseLiteralHandlingBindTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseLiteralHandlingBindTest.java @@ -17,9 +17,12 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.query.criteria.LiteralHandlingMode; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.junit.Test; @@ -101,6 +104,8 @@ public class SelectCaseLiteralHandlingBindTest extends BaseEntityManagerFunction } @Test + @SkipForDialect(value = DB2Dialect.class, comment = "We would need casts in the case clauses. See HHH-12822.") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby requires either casted parameters or literals in the result arms of CASE expressions") public void whereCaseExpression() { doInJPA( this::entityManagerFactory, entityManager -> { diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseTest.java index 28486f5162..6537ad7b5e 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/criteria/selectcase/SelectCaseTest.java @@ -35,6 +35,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; @@ -45,6 +46,7 @@ import org.junit.Test; @TestForIssue( jiraKey = "HHH-9731" ) @SkipForDialect(value = DB2Dialect.class, comment = "We would need casts in the case clauses. See HHH-12822.") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby requires either casted parameters or literals in the result arms of CASE expressions") public class SelectCaseTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/DisableDiscardPersistenceContextOnCloseTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/DisableDiscardPersistenceContextOnCloseTest.java index 8ed46a1a83..a6d0777fde 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/DisableDiscardPersistenceContextOnCloseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/DisableDiscardPersistenceContextOnCloseTest.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import java.util.Map; import javax.persistence.EntityManager; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.Wallet; @@ -34,6 +35,7 @@ public class DisableDiscardPersistenceContextOnCloseTest extends BaseEntityManag @Override protected Map getConfig() { Map config = super.getConfig(); + connectionProvider.setConnectionProvider( (ConnectionProvider) config.get( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER ) ); config.put( AvailableSettings.DISCARD_PC_ON_CLOSE, "false"); config.put( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/EnableDiscardPersistenceContextOnCloseTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/EnableDiscardPersistenceContextOnCloseTest.java index 71fc4fda17..328a1eb25e 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/EnableDiscardPersistenceContextOnCloseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/ejb3configuration/EnableDiscardPersistenceContextOnCloseTest.java @@ -10,6 +10,7 @@ import java.util.Map; import javax.persistence.EntityManager; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.Wallet; @@ -35,6 +36,7 @@ public class EnableDiscardPersistenceContextOnCloseTest extends BaseEntityManage @Override protected Map getConfig() { Map config = super.getConfig(); + connectionProvider.setConnectionProvider( (ConnectionProvider) config.get( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER ) ); config.put( AvailableSettings.DISCARD_PC_ON_CLOSE, "true"); config.put( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java index 44d1c686a5..50538fd387 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java @@ -25,6 +25,7 @@ import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.TransactionException; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.Oracle10gDialect; @@ -38,6 +39,7 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.transaction.TransactionUtil; import org.hibernate.testing.util.ExceptionUtil; import org.jboss.logging.Logger; @@ -56,6 +58,14 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { private static final Logger log = Logger.getLogger( LockTest.class ); + @Override + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + // Looks like Oracle Connections that experience a timeout produce different errors when they timeout again?! + SharedDriverManagerConnectionProviderImpl.getInstance().reset(); + options.remove( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER ); + } + @Test @SkipForDialect( value = CockroachDialect.class ) public void testFindWithTimeoutHint() { @@ -557,6 +567,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { // ASE15.5 will generate select...holdlock and fail at this test, but ASE15.7 passes it. Skip it for ASE15.5 // only. @SkipForDialect(value = { SQLServerDialect.class }) + @SkipForDialect(DerbyDialect.class) public void testContendedPessimisticLock() throws Exception { final CountDownLatch latch = new CountDownLatch( 1 ); final Lock lock = new Lock(); @@ -622,7 +633,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean backGroundThreadCompleted = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean backGroundThreadCompleted = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success if ( backGroundThreadCompleted ) { // the background thread read a value. At the very least we need to assert that he did not see the @@ -646,12 +657,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testContendedPessimisticLock", t, lock.getId() ); } } @@ -720,7 +726,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -728,17 +734,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testContendedPessimisticReadLockTimeout", t, lock.getId() ); } } @@ -805,7 +806,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -813,17 +814,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testContendedPessimisticWriteLockTimeout", t, lock.getId() ); } } @@ -890,7 +886,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -898,17 +894,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testContendedPessimisticWriteLockNoWait", t, lock.getId() ); } } @@ -980,7 +971,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -988,17 +979,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testQueryTimeout", t, lock.getId() ); } } @@ -1071,7 +1057,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -1079,17 +1065,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testQueryTimeoutEMProps", t, lock.getId() ); } } @@ -1157,7 +1138,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { try { t.start(); - boolean latchSet = latch.await( 10, TimeUnit.SECONDS ); // should return quickly on success + boolean latchSet = latch.await( 20, TimeUnit.SECONDS ); // should return quickly on success assertTrue( "background test thread finished (lock timeout is broken)", latchSet ); assertTrue( "background test thread timed out on lock attempt", bgTask.get() ); } @@ -1165,17 +1146,12 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { Thread.interrupted(); } catch (ExecutionException e) { - fail(e.getMessage()); + throw new AssertionError( e ); } } ); } finally { - t.join(); // wait for background thread to finish before deleting entity - - doInJPA( this::entityManagerFactory, em -> { - Lock _lock = em.getReference( Lock.class, lock.getId() ); - em.remove( _lock ); - } ); + awaitThenDelete( "testLockTimeoutEMProps", t, lock.getId() ); } } @@ -1186,4 +1162,22 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { UnversionedLock.class }; } + + private void awaitThenDelete(String test, Thread t, Integer lockId) throws InterruptedException { + // wait for background thread to finish before deleting entity + try { + while ( t.isAlive() ) { + t.join( TimeUnit.SECONDS.toMillis( 30 ) ); + final Throwable temp = new Throwable(); + temp.setStackTrace( t.getStackTrace() ); + log.info( test + ": Thread seems stuck", temp ); + t.interrupt(); + } + } finally { + doInJPA( this::entityManagerFactory, em -> { + Lock _lock = em.getReference( Lock.class, lockId ); + em.remove( _lock ); + } ); + } + } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java index d97a19288c..ca59edbb14 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java @@ -12,7 +12,9 @@ import java.util.HashMap; import java.util.Map; import javax.persistence.LockModeType; import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider; import org.hibernate.testing.DialectChecks; @@ -42,6 +44,7 @@ public class StatementIsClosedAfterALockExceptionTest extends BaseEntityManagerF @Override protected Map getConfig() { Map config = super.getConfig(); + CONNECTION_PROVIDER.setConnectionProvider( (ConnectionProvider) config.get( AvailableSettings.CONNECTION_PROVIDER ) ); config.put( org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, CONNECTION_PROVIDER diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/metamodel/Translation.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/metamodel/Translation.java index dc54bf1613..1ab70f659b 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/metamodel/Translation.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/metamodel/Translation.java @@ -8,11 +8,13 @@ package org.hibernate.jpa.test.metamodel; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Table; /** * @author Steve Ebersole */ @Entity +@Table(name = "translation_tbl") public class Translation { @Id Integer id; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/QueryTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/QueryTest.java index e7f71f3d76..afef8ea852 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/QueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/QueryTest.java @@ -26,6 +26,7 @@ import org.hibernate.Hibernate; import org.hibernate.QueryException; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.PostgreSQL9Dialect; import org.hibernate.dialect.PostgresPlusDialect; @@ -136,6 +137,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullPositionalParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -166,6 +168,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullPositionalParameterParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -213,6 +216,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullPositionalParameterParameterIncompatible() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -260,6 +264,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullNamedParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -290,6 +295,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullNamedParameterParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -336,6 +342,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Test @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testNullNamedParameterParameterIncompatible() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -386,6 +393,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to INTEGER") public void testNativeQueryNullPositionalParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -423,6 +431,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = Oracle8iDialect.class, comment = "ORA-00932: inconsistent datatypes: expected NUMBER got BINARY") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to INTEGER") public void testNativeQueryNullPositionalParameterParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -476,6 +485,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to INTEGER") public void testNativeQueryNullNamedParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); @@ -513,6 +523,7 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = Oracle8iDialect.class, comment = "ORA-00932: inconsistent datatypes: expected NUMBER got BINARY") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to INTEGER") public void testNativeQueryNullNamedParameterParameter() throws Exception { EntityManager em = getOrCreateEntityManager(); em.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java index 7fde17a6aa..8ebb3cbb80 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/schemagen/JpaSchemaGeneratorTest.java @@ -49,7 +49,7 @@ public class JpaSchemaGeneratorTest extends BaseEntityManagerFunctionalTestCase @TestForIssue(jiraKey = "HHH-8271") public void testSqlLoadScriptSourceClasspath() throws Exception { Map settings = buildSettings(); - settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "create-drop" ); settings.put( AvailableSettings.HBM2DDL_LOAD_SCRIPT_SOURCE, getLoadSqlScript() ); doTest( settings ); } @@ -60,7 +60,7 @@ public class JpaSchemaGeneratorTest extends BaseEntityManagerFunctionalTestCase @TestForIssue(jiraKey = "HHH-8271") public void testSqlLoadScriptSourceUrl() throws Exception { Map settings = buildSettings(); - settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "create-drop" ); settings.put( AvailableSettings.HBM2DDL_LOAD_SCRIPT_SOURCE, getResourceUrlString( getLoadSqlScript() ) ); doTest( settings ); } @@ -78,7 +78,7 @@ public class JpaSchemaGeneratorTest extends BaseEntityManagerFunctionalTestCase @TestForIssue(jiraKey = "HHH-8271") public void testSqlCreateScriptSourceClasspath() throws Exception { Map settings = buildSettings(); - settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "create-drop" ); settings.put( AvailableSettings.HBM2DDL_CREATE_SOURCE, "metadata-then-script" ); settings.put( AvailableSettings.HBM2DDL_CREATE_SCRIPT_SOURCE, getCreateSqlScript() ); doTest( settings ); @@ -89,7 +89,7 @@ public class JpaSchemaGeneratorTest extends BaseEntityManagerFunctionalTestCase @TestForIssue(jiraKey = "HHH-8271") public void testSqlCreateScriptSourceUrl() throws Exception { Map settings = buildSettings(); - settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "drop-and-create" ); + settings.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "create-drop" ); settings.put( AvailableSettings.HBM2DDL_CREATE_SOURCE, "metadata-then-script" ); settings.put( AvailableSettings.HBM2DDL_CREATE_SCRIPT_SOURCE, getResourceUrlString( getCreateSqlScript() ) ); doTest( settings ); diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java index 1037e9940b..6d7344945f 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/FlushAndTransactionTest.java @@ -165,6 +165,11 @@ public class FlushAndTransactionTest extends BaseEntityManagerFunctionalTestCase catch ( IllegalStateException e ) { //success } + finally { + if ( em.getTransaction().isActive() ) { + em.getTransaction().rollback(); + } + } } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/batch/JtaWithFailingBatchTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/batch/JtaWithFailingBatchTest.java index ee38b55626..71fb84cd75 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/batch/JtaWithFailingBatchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/transaction/batch/JtaWithFailingBatchTest.java @@ -75,8 +75,10 @@ public class JtaWithFailingBatchTest extends AbstractJtaBatchTest { } catch (Exception expected) { //expected - if ( transactionManager.getStatus() == Status.STATUS_ACTIVE ) { - transactionManager.rollback(); + switch ( transactionManager.getStatus() ) { + case Status.STATUS_ACTIVE: + case Status.STATUS_MARKED_ROLLBACK: + transactionManager.rollback(); } } @@ -89,7 +91,6 @@ public class JtaWithFailingBatchTest extends AbstractJtaBatchTest { assertStatementsListIsCleared(); } finally { - em.close(); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/MergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/MergeTest.java index e27db38081..815e52b9c1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/MergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/cascade/MergeTest.java @@ -14,6 +14,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.Table; import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; @@ -32,8 +33,8 @@ public class MergeTest { public void tearDown(EntityManagerFactoryScope scope) { scope.inTransaction( entityManager -> { - entityManager.createQuery( "delete from MergeTest$Item" ).executeUpdate(); - entityManager.createQuery( "delete from MergeTest$Order" ).executeUpdate(); + entityManager.createQuery( "delete from Item" ).executeUpdate(); + entityManager.createQuery( "delete from Order" ).executeUpdate(); } ); } @@ -104,7 +105,8 @@ public class MergeTest { ); } - @Entity + @Entity(name = "Order") + @Table(name = "orders") public static class Order { @Id @GeneratedValue @@ -122,7 +124,7 @@ public class MergeTest { } } - @Entity + @Entity(name = "Item") public static class Item { @Id @GeneratedValue diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java index c4a61fb1ff..3337e4cb9c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/criteria/BasicCriteriaExecutionTests.java @@ -10,6 +10,9 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Root; +import org.hibernate.dialect.DerbyDialect; + +import org.hibernate.testing.SkipForDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.query.criteria.JpaCriteriaQuery; @@ -111,6 +114,7 @@ public class BasicCriteriaExecutionTests { // Doing ... where ? = ? ... is only allowed in a few DBs. Since this is useless, we don't bother to emulate this @Test @RequiresDialect(H2Dialect.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support comparing parameters against each other") public void testExecutingBasicCriteriaQueryParameterPredicate(SessionFactoryScope scope) { scope.inStatelessTransaction( session -> { @@ -130,6 +134,7 @@ public class BasicCriteriaExecutionTests { // Doing ... where ? = ? ... is only allowed in a few DBs. Since this is useless, we don't bother to emulate this @Test @RequiresDialect(H2Dialect.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support comparing parameters against each other") public void testExecutingBasicCriteriaQueryParameterPredicateInStatelessSession(SessionFactoryScope scope) { scope.inStatelessTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java index c92d60c619..0590ecdea8 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/query/criteria/internal/expression/SearchedCaseExpressionTest.java @@ -19,6 +19,7 @@ import javax.persistence.criteria.Path; import javax.persistence.criteria.Root; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; @@ -65,6 +66,7 @@ public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase { @Test @SkipForDialect(value = DB2Dialect.class, comment = "We would need casts in the case clauses. See HHH-12822.") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby requires either casted parameters or literals in the result arms of CASE expressions") public void testEqualClause() { doInHibernate( this::sessionFactory, session -> { CriteriaBuilder cb = session.getCriteriaBuilder(); @@ -89,6 +91,8 @@ public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-13167") + @SkipForDialect(value = DB2Dialect.class, comment = "We would need casts in the case clauses. See HHH-12822.") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby requires either casted parameters or literals in the result arms of CASE expressions") public void testMissingElseClause() { doInHibernate( this::sessionFactory, session -> { Event event = new Event(); diff --git a/hibernate-core/src/test/java/org/hibernate/query/hhh14156/HHH14156Test.java b/hibernate-core/src/test/java/org/hibernate/query/hhh14156/HHH14156Test.java index 48bc7cb191..bb943030b2 100644 --- a/hibernate-core/src/test/java/org/hibernate/query/hhh14156/HHH14156Test.java +++ b/hibernate-core/src/test/java/org/hibernate/query/hhh14156/HHH14156Test.java @@ -7,6 +7,10 @@ import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Table; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.dialect.SQLServerDialect; + +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -24,6 +28,8 @@ public class HHH14156Test extends BaseCoreFunctionalTestCase { } @Test + @SkipForDialect(value = SQLServerDialect.class, comment = "SQLServer doesn't support tuple comparisons") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't support tuple comparisons") public void testNoExceptionThrown() { inTransaction( session -> session.createQuery( diff --git a/hibernate-core/src/test/java/org/hibernate/secure/JaccIntegratorTest.java b/hibernate-core/src/test/java/org/hibernate/secure/JaccIntegratorTest.java index f3e0f7eadd..e27160755a 100644 --- a/hibernate-core/src/test/java/org/hibernate/secure/JaccIntegratorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/secure/JaccIntegratorTest.java @@ -161,7 +161,7 @@ public class JaccIntegratorTest extends BaseEntityManagerFunctionalTestCase { } } - @Entity + @Entity(name = "Person") public static class Person { @Id diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/hhh4635/LobTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/hhh4635/LobTest.java index 5d543fa15f..71d3bbac35 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/hhh4635/LobTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/hhh4635/LobTest.java @@ -36,6 +36,7 @@ public class LobTest extends BaseCoreFunctionalTestCase { entity.setQwerty(randomString(4000)); session.save(entity); session.getTransaction().commit(); + session.close(); } @Override @@ -60,5 +61,7 @@ public class LobTest extends BaseCoreFunctionalTestCase { String s = (String) query.uniqueResult(); log.debug( "Using Oracle charset " + s ); + session.getTransaction().commit(); + session.close(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java index 6a1005bb5e..adcfe25ffe 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/query/QueryAndSQLTest.java @@ -23,6 +23,7 @@ import org.hibernate.Transaction; import org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.PostgreSQL9Dialect; @@ -107,6 +108,7 @@ public class QueryAndSQLTest extends BaseCoreFunctionalTestCase { @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to BIGINT") public void testQueryWithNullParameter(){ Chaos c0 = new Chaos(); c0.setId( 0L ); @@ -193,6 +195,7 @@ public class QueryAndSQLTest extends BaseCoreFunctionalTestCase { @SkipForDialect(value = PostgresPlusDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = CockroachDialect.class, jiraKey = "HHH-10312", comment = "Cannot convert untyped null (assumed to be bytea type) to bigint") @SkipForDialect(value = SybaseDialect.class, comment = "Null == null on Sybase") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to BIGINT") public void testNativeQueryWithNullParameter(){ Chaos c0 = new Chaos(); c0.setId( 0L ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/cdi/converters/legacy/QueryTest.java b/hibernate-core/src/test/java/org/hibernate/test/cdi/converters/legacy/QueryTest.java index a92fad7ff7..6b5615770a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cdi/converters/legacy/QueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cdi/converters/legacy/QueryTest.java @@ -178,17 +178,17 @@ public class QueryTest extends BaseEntityManagerFunctionalTestCase { @Embeddable public static class Name { - public String first; - public String middle; - public String last; + public String firstName; + public String middleName; + public String lastName; public Name() { } - public Name(String first, String middle, String last) { - this.first = first; - this.middle = middle; - this.last = last; + public Name(String firstName, String middleName, String lastName) { + this.firstName = firstName; + this.middleName = middleName; + this.lastName = lastName; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java index 4d50804aef..365dd3cdc4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java @@ -135,6 +135,7 @@ public class BeforeCompletionReleaseTest extends BaseEntityManagerFunctionalTest inOrder.verify( statementMock ).close(); inOrder.verify( connectionSpy ).close(); inOrder.verify( transactionSpy ).commit( any(), anyBoolean() ); + Mockito.reset( connectionSpy ); } private void spyOnTransaction(XAResource xaResource) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/connections/ConnectionsReleaseAutoCommitTest.java b/hibernate-core/src/test/java/org/hibernate/test/connections/ConnectionsReleaseAutoCommitTest.java index 00ec9be69f..7f868348cb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/connections/ConnectionsReleaseAutoCommitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/connections/ConnectionsReleaseAutoCommitTest.java @@ -27,6 +27,9 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.internal.util.MockUtil; + import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.spy; @@ -92,6 +95,7 @@ public class ConnectionsReleaseAutoCommitTest extends BaseEntityManagerFunctiona assertEquals( 1, connectionProvider.getConnectionCount() ); verify( connectionProvider.connection, times( 1 ) ).close(); + Mockito.reset( connectionProvider.connection ); } @Entity(name = "Thing") diff --git a/hibernate-core/src/test/java/org/hibernate/test/converter/AndNationalizedTests.java b/hibernate-core/src/test/java/org/hibernate/test/converter/AndNationalizedTests.java index 7e90bf6255..029b174f62 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/converter/AndNationalizedTests.java +++ b/hibernate-core/src/test/java/org/hibernate/test/converter/AndNationalizedTests.java @@ -20,8 +20,12 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.dialect.Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.mapping.PersistentClass; + +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; @@ -34,6 +38,7 @@ import static org.junit.Assert.assertEquals; * @author Steve Ebersole */ //@SkipForDialect(value = PostgreSQL81Dialect.class, comment = "Postgres does not support ") +@SkipForDialect(value = DB2Dialect.class, comment = "DB2 jdbc driver doesn't support setNString") public class AndNationalizedTests extends BaseUnitTestCase { @Test @TestForIssue( jiraKey = "HHH-9599") @@ -44,9 +49,10 @@ public class AndNationalizedTests extends BaseUnitTestCase { ( (MetadataImpl) metadata ).validate(); final PersistentClass entityBinding = metadata.getEntityBinding( TestEntity.class.getName() ); - if(metadata.getDatabase().getDialect() instanceof PostgreSQL81Dialect - || metadata.getDatabase().getDialect() instanceof DB2Dialect - || metadata.getDatabase().getDialect() instanceof CockroachDialect ){ + final Dialect dialect = metadata.getDatabase().getDialect(); + if ( dialect instanceof PostgreSQL81Dialect + || dialect instanceof DB2Dialect && !( dialect instanceof DerbyDialect ) + || dialect instanceof CockroachDialect ){ // See issue HHH-10693 for PostgreSQL and CockroachDB, HHH-12753 for DB2 assertEquals( Types.VARCHAR, diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java index 46237acc8b..b58142cf08 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java @@ -23,11 +23,14 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.SQLServer2005Dialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.service.ServiceRegistry; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.transaction.TransactionUtil; import org.junit.Test; import org.jboss.logging.Logger; @@ -56,19 +59,31 @@ public class SQLServerDialectCollationTest extends BaseCoreFunctionalTestCase { StandardServiceRegistryImpl _serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() ); try { - try (Connection connection = _serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection(); - Statement statement = connection.createStatement()) { - connection.setAutoCommit( true ); - statement.executeUpdate( "DROP DATABASE hibernate_orm_test_collation" ); + try { + TransactionUtil.doWithJDBC( + _serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + statement.executeUpdate( "DROP DATABASE hibernate_orm_test_collation" ); + } + } + ); } catch (SQLException e) { log.debug( e.getMessage() ); } - try (Connection connection = _serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection(); - Statement statement = connection.createStatement()) { - connection.setAutoCommit( true ); - statement.executeUpdate( "CREATE DATABASE hibernate_orm_test_collation COLLATE Latin1_General_CS_AS" ); - statement.executeUpdate( "ALTER DATABASE [hibernate_orm_test_collation] SET AUTO_CLOSE OFF " ); + try { + TransactionUtil.doWithJDBC( + _serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + statement.executeUpdate( "CREATE DATABASE hibernate_orm_test_collation COLLATE Latin1_General_CS_AS" ); + statement.executeUpdate( "ALTER DATABASE [hibernate_orm_test_collation] SET AUTO_CLOSE OFF " ); + } + } + ); } catch (SQLException e) { log.debug( e.getMessage() ); @@ -149,7 +164,6 @@ public class SQLServerDialectCollationTest extends BaseCoreFunctionalTestCase { } } - @Override protected boolean rebuildSessionFactoryOnError() { return false; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java index dd87f79765..217fc7c8c0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java @@ -6,7 +6,6 @@ */ package org.hibernate.test.dialect.functional; -import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -21,12 +20,12 @@ import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.SQLServerDialect; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.testing.AfterClassOnce; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.transaction.TransactionUtil; import org.junit.Assert; import org.junit.Test; @@ -50,20 +49,34 @@ public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTe return configuration; } - @AfterClassOnce - protected void revertBackOriginalDBCollation() { + @Override + protected void releaseSessionFactory() { + super.releaseSessionFactory(); if ( originalDBCollation != null && collationChanged && !changedDBCollation.equals( originalDBCollation ) ) { BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() ); - try (Connection connection = serviceRegistry.getService( JdbcServices.class ) - .getBootstrapJdbcConnectionAccess() - .obtainConnection(); - Statement statement = connection.createStatement()) { - connection.setAutoCommit( true ); - statement.executeUpdate( "ALTER DATABASE CURRENT COLLATE " + originalDBCollation ); + try { + TransactionUtil.doWithJDBC( + serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + String dbName; + try ( ResultSet rs = statement.executeQuery( "SELECT DB_NAME()" ) ) { + rs.next(); + dbName = rs.getString( 1 ); + } + statement.execute( "USE master" ); + statement.execute( "ALTER DATABASE " + dbName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE" ); + statement.executeUpdate( "ALTER DATABASE " + dbName + " COLLATE " + originalDBCollation ); + statement.execute( "ALTER DATABASE " + dbName + " SET MULTI_USER WITH ROLLBACK IMMEDIATE" ); + statement.execute( "USE " + dbName ); + } + } + ); } catch (SQLException e) { throw new RuntimeException( "Failed to revert back database collation to " + originalDBCollation, e ); @@ -72,42 +85,70 @@ public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTe serviceRegistry.destroy(); } } + // The alter database calls could lead to issues with existing connections, so we reset the shared pool here + SharedDriverManagerConnectionProviderImpl.getInstance().reset(); } + @Override protected void buildSessionFactory() { BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() ); try { - try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection(); - Statement statement = connection.createStatement() ) { - connection.setAutoCommit( true ); - try ( ResultSet rs = statement.executeQuery( "SELECT SERVERPROPERTY('collation')" ) ) { - rs.next(); - String instanceCollation = rs.getString( 1 ); - Assert.assertNotEquals( instanceCollation, changedDBCollation ); - } + try { + TransactionUtil.doWithJDBC( + serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + try ( ResultSet rs = statement.executeQuery( "SELECT DATABASEPROPERTYEX(DB_NAME(),'collation')" ) ) { + rs.next(); + String instanceCollation = rs.getString( 1 ); + Assert.assertNotEquals( instanceCollation, changedDBCollation ); + } + } + } + ); } catch (SQLException e) { log.debug( e.getMessage() ); } - try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection(); - Statement statement = connection.createStatement() ) { - connection.setAutoCommit( true ); - try ( ResultSet rs = statement.executeQuery( "SELECT CONVERT (varchar(256), DATABASEPROPERTYEX(DB_NAME(),'collation'))" ) ) { - rs.next(); - originalDBCollation = rs.getString( 1 ); - } + try { + TransactionUtil.doWithJDBC( + serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + try ( ResultSet rs = statement.executeQuery( "SELECT CONVERT (varchar(256), DATABASEPROPERTYEX(DB_NAME(),'collation'))" ) ) { + rs.next(); + originalDBCollation = rs.getString( 1 ); + } + } + } + ); } catch (SQLException e) { log.debug( e.getMessage() ); } - try ( Connection connection = serviceRegistry.getService( JdbcServices.class ).getBootstrapJdbcConnectionAccess().obtainConnection(); - Statement statement = connection.createStatement() ) { - connection.setAutoCommit( true ); - statement.executeUpdate( "ALTER DATABASE CURRENT COLLATE " + changedDBCollation ); - collationChanged = true; - } + TransactionUtil.doWithJDBC( + serviceRegistry, + connection -> { + try (Statement statement = connection.createStatement()) { + connection.setAutoCommit( true ); + String dbName; + try ( ResultSet rs = statement.executeQuery( "SELECT DB_NAME()" ) ) { + rs.next(); + dbName = rs.getString( 1 ); + } + statement.execute( "USE master" ); + statement.execute( "ALTER DATABASE " + dbName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE" ); + statement.executeUpdate( "ALTER DATABASE " + dbName + " COLLATE " + changedDBCollation ); + statement.execute( "ALTER DATABASE " + dbName + " SET MULTI_USER WITH ROLLBACK IMMEDIATE" ); + statement.execute( "USE " + dbName ); + collationChanged = true; + } + } + ); } catch ( SQLException e ) { throw new RuntimeException( e ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index 80218b0a8f..c2ef0ea7dc 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -676,6 +676,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-8901" ) @RequiresDialectFeature(DialectChecks.NotSupportsEmptyInListCheck.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't like `x in (null)`") public void testEmptyInListForDialectsNotSupportsEmptyInList() { Session session = openSession(); session.beginTransaction(); @@ -704,6 +705,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-2851") + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void testMultipleRefsToSameParam() { Session s = openSession(); s.beginTransaction(); @@ -1800,6 +1802,7 @@ public class ASTParserLoadingTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-1830") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testAggregatedJoinAlias() { Session s = openSession(); s.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/CoalesceTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/CoalesceTest.java index 3903664a3e..75157a986d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/CoalesceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/CoalesceTest.java @@ -12,6 +12,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.query.Query; @@ -66,6 +67,7 @@ public class CoalesceTest extends BaseCoreFunctionalTestCase { @Test @SkipForDialect( jiraKey = "HHH-10463", value = PostgreSQL81Dialect.class) @SkipForDialect( jiraKey = "HHH-10463", value = Oracle8iDialect.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Cannot convert untyped null (assumed to be VARBINARY type) to VARCHAR") public void HHH_10463_NullInCoalesce() { doInHibernate( this::sessionFactory, session -> { Query query = session.createQuery("from Person p where p.name = coalesce(:name, p.name) "); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/WithClauseTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/WithClauseTest.java index 2bf10ce97e..cf05b80212 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/WithClauseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/WithClauseTest.java @@ -16,7 +16,9 @@ import org.hibernate.QueryException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -243,6 +245,7 @@ public class WithClauseTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue(jiraKey = "HHH-11401") + @SkipForDialect(value = DerbyDialect.class,comment = "Derby does not support cast from INTEGER to VARCHAR") public void testWithClauseAsSubqueryWithKeyAndOtherJoinReference() { TestData data = new TestData(); data.prepare(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest.java index 371c570b59..22f5924a6b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest.java @@ -16,7 +16,9 @@ import javax.persistence.Id; import javax.persistence.ManyToMany; import org.hibernate.Hibernate; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.After; @@ -45,6 +47,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsCompoundSelectExpression() { doInHibernate( this::sessionFactory, @@ -73,6 +76,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsCtorSelectExpression() { doInHibernate( this::sessionFactory, @@ -102,6 +106,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionWithLeftJoin() { doInHibernate( this::sessionFactory, @@ -131,6 +136,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionWithInnerJoin() { doInHibernate( this::sessionFactory, @@ -156,6 +162,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionOfAliasWithInnerJoin() { doInHibernate( this::sessionFactory, @@ -181,6 +188,7 @@ public class ManyToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionExcludeEmptyCollection() { doInHibernate( this::sessionFactory, diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest2.java b/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest2.java index ff60f8009f..f134d966ae 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest2.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/size/ManyToManySizeTest2.java @@ -557,6 +557,7 @@ public class ManyToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase { } private static int countNumberOfJoins(String query) { - return query.toLowerCase( Locale.ROOT ).split( " join ", -1 ).length - 1; + String fromPart = query.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0]; + return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest.java index b355ec9f4c..1643732acb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest.java @@ -17,7 +17,9 @@ import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import org.hibernate.Hibernate; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.After; @@ -32,6 +34,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpression() { doInHibernate( this::sessionFactory, @@ -61,6 +64,7 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionWithLeftJoin() { doInHibernate( this::sessionFactory, @@ -90,6 +94,7 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionWithInnerJoin() { doInHibernate( this::sessionFactory, @@ -115,6 +120,7 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionOfAliasWithInnerJoin() { doInHibernate( this::sessionFactory, @@ -140,6 +146,7 @@ public class OneToManySizeTest extends BaseNonConfigCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't see that the subquery is functionally dependent") public void testSizeAsSelectExpressionExcludeEmptyCollection() { doInHibernate( this::sessionFactory, diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest2.java b/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest2.java index 5e29b99fde..5670173021 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest2.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/size/OneToManySizeTest2.java @@ -554,6 +554,7 @@ public class OneToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase { } private static int countNumberOfJoins(String query) { - return query.toLowerCase( Locale.ROOT ).split( " join ", -1 ).length - 1; + String fromPart = query.toLowerCase( Locale.ROOT ).split( " from " )[1].split( " where " )[0]; + return fromPart.split( "(\\sjoin\\s|,\\s)", -1 ).length - 1; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceMismatchStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceMismatchStrategyTest.java index 5b950cf2b1..7593925325 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceMismatchStrategyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/idgen/enhanced/sequence/HiLoSequenceMismatchStrategyTest.java @@ -78,6 +78,7 @@ public class HiLoSequenceMismatchStrategyTest extends BaseCoreFunctionalTestCase for ( String createSequenceStatement : createSequenceStatements ) { statement.execute( createSequenceStatement ); } + connection.commit(); } catch (SQLException e) { fail( e.getMessage() ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentJoined.java b/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentJoined.java index 0901121872..d9942e8bc8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentJoined.java +++ b/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentJoined.java @@ -6,6 +6,7 @@ */ package org.hibernate.test.inheritance; +import java.util.Comparator; import java.util.List; import javax.persistence.Column; import javax.persistence.ConstraintMode; @@ -85,6 +86,7 @@ public class TransientOverrideAsPersistentJoined extends BaseNonConfigCoreFuncti final List employees = session.createQuery( "from Employee", Employee.class ) .getResultList(); assertEquals( 2, employees.size() ); + employees.sort( Comparator.comparing( Employee::getName ) ); assertTrue( Editor.class.isInstance( employees.get( 0 ) ) ); assertTrue( Writer.class.isInstance( employees.get( 1 ) ) ); final Editor editor = (Editor) employees.get( 0 ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentTablePerClass.java b/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentTablePerClass.java index 68cbc5d209..a06849d271 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentTablePerClass.java +++ b/hibernate-core/src/test/java/org/hibernate/test/inheritance/TransientOverrideAsPersistentTablePerClass.java @@ -6,6 +6,7 @@ */ package org.hibernate.test.inheritance; +import java.util.Comparator; import java.util.List; import javax.persistence.Column; import javax.persistence.ConstraintMode; @@ -84,6 +85,7 @@ public class TransientOverrideAsPersistentTablePerClass extends BaseNonConfigCor final List employees = session.createQuery( "from Employee", Employee.class ) .getResultList(); assertEquals( 2, employees.size() ); + employees.sort( Comparator.comparing( Employee::getName ) ); assertTrue( Editor.class.isInstance( employees.get( 0 ) ) ); assertTrue( Writer.class.isInstance( employees.get( 1 ) ) ); final Editor editor = (Editor) employees.get( 0 ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/insertordering/BaseInsertOrderingTest.java b/hibernate-core/src/test/java/org/hibernate/test/insertordering/BaseInsertOrderingTest.java index f2882b17e2..0b326efee5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/insertordering/BaseInsertOrderingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/insertordering/BaseInsertOrderingTest.java @@ -6,6 +6,7 @@ import java.util.Map; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.test.util.jdbc.PreparedStatementSpyConnectionProvider; @@ -39,6 +40,7 @@ abstract class BaseInsertOrderingTest extends BaseNonConfigCoreFunctionalTestCas protected void addSettings(Map settings) { settings.put( Environment.ORDER_INSERTS, "true" ); settings.put( Environment.STATEMENT_BATCH_SIZE, "10" ); + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/BasicConnectionTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/BasicConnectionTest.java index 762bc1ead6..9c9fa089ba 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/BasicConnectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/BasicConnectionTest.java @@ -16,10 +16,12 @@ import java.sql.Statement; import org.hibernate.JDBCException; import org.hibernate.Session; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.resource.jdbc.ResourceRegistry; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -50,6 +52,7 @@ public class BasicConnectionTest extends BaseCoreFunctionalTestCase { } @Test + @SkipForDialect(value = DerbyDialect.class,comment = "Derby can't drop tables that are still referred to from open ResultSets") public void testBasicJdbcUsage() throws JDBCException { Session session = openSession(); SessionImplementor sessionImpl = (SessionImplementor) session; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/SessionJdbcBatchTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/SessionJdbcBatchTest.java index 0e4e917d98..fc95433cf1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/SessionJdbcBatchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/internal/SessionJdbcBatchTest.java @@ -16,6 +16,7 @@ import javax.persistence.Id; import org.hibernate.Session; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -45,6 +46,7 @@ public class SessionJdbcBatchTest @Override protected void addSettings(Map settings) { settings.put( AvailableSettings.STATEMENT_BATCH_SIZE, 2 ); + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java index 078ffeecb2..d32f896869 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java @@ -12,6 +12,7 @@ import org.hibernate.Transaction; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -40,6 +41,7 @@ public class JPALockTest extends AbstractJPATest { public void configure(Configuration cfg) { super.configure( cfg ); if( SQLServerDialect.class.isAssignableFrom( DIALECT.getClass() )) { + connectionProvider.setConnectionProvider( (ConnectionProvider) cfg.getProperties().get( AvailableSettings.CONNECTION_PROVIDER ) ); cfg.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java index 716e28bcaf..6f1a38fb7f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java @@ -16,6 +16,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -37,14 +38,24 @@ import static org.junit.Assert.fail; */ @RequiresDialectFeature(DialectChecks.SupportNoWait.class) public class LockExceptionTests extends AbstractJPATest { + + private SQLServerSnapshotIsolationConnectionProvider connectionProvider = new SQLServerSnapshotIsolationConnectionProvider(); + @Override public void configure(Configuration cfg) { super.configure( cfg ); if( SQLServerDialect.class.isAssignableFrom( DIALECT.getClass() )) { - cfg.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, new SQLServerSnapshotIsolationConnectionProvider() ); + connectionProvider.setConnectionProvider( (ConnectionProvider) cfg.getProperties().get( AvailableSettings.CONNECTION_PROVIDER ) ); + cfg.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider ); } } + @Override + protected void releaseSessionFactory() { + super.releaseSessionFactory(); + connectionProvider.stop(); + } + @Test @TestForIssue( jiraKey = "HHH-8786" ) public void testLockTimeoutFind() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java index 43e1862afa..b210e8f53f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java @@ -17,6 +17,7 @@ import org.hibernate.Transaction; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.exception.SQLGrammarException; import org.hibernate.test.jpa.AbstractJPATest; import org.hibernate.test.jpa.Item; @@ -44,6 +45,7 @@ public class RepeatableReadTest extends AbstractJPATest { public void configure(Configuration cfg) { super.configure( cfg ); if( SQLServerDialect.class.isAssignableFrom( DIALECT.getClass() )) { + connectionProvider.setConnectionProvider( (ConnectionProvider) cfg.getProperties().get( AvailableSettings.CONNECTION_PROVIDER ) ); cfg.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/orderby/OrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/orderby/OrderByTest.java new file mode 100644 index 0000000000..1ae2032ed9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/orderby/OrderByTest.java @@ -0,0 +1,115 @@ +package org.hibernate.test.orderby; + +import java.util.List; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; + +/** + * @author Christian Beikov + */ +public class OrderByTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + P1.class, + P2.class + }; + } + + @Override + protected void prepareTest() throws Exception { + doInHibernate( + this::sessionFactory, session -> { + session.persist( new P1( 1L, "abc" ) ); + session.persist( new P1( 2L, "abc" ) ); + session.persist( new P2( 3L, "def" ) ); + } + ); + } + + @Override + protected void cleanupTest() throws Exception { + doInHibernate( + this::sessionFactory, session -> { + session.createQuery( "delete from Person" ).executeUpdate(); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-14351") + public void testOrderBySqlNode() { + doInHibernate( + this::sessionFactory, session -> { + List list = session.createQuery( "from Person p order by type(p) desc, p.id", Person.class ) + .getResultList(); + assertEquals( 3L, list.get( 0 ).getId().longValue() ); + assertEquals( 1L, list.get( 1 ).getId().longValue() ); + assertEquals( 2L, list.get( 2 ).getId().longValue() ); + } + ); + } + + @Entity(name = "Person") + public static abstract class Person { + @Id + private Long id; + private String name; + + public Person() { + } + + public Person(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Entity(name = "P1") + @DiscriminatorValue( "P1" ) + public static class P1 extends Person { + public P1() { + } + + public P1(Long id, String name) { + super( id, name ); + } + } + + @Entity(name = "P2") + @DiscriminatorValue( "P2" ) + public static class P2 extends Person { + public P2() { + } + + public P2(Long id, String name) { + super( id, name ); + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/proxy/ProxyReferenceEqualityTest.java b/hibernate-core/src/test/java/org/hibernate/test/proxy/ProxyReferenceEqualityTest.java index f645bf1e43..04f07e12b7 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/proxy/ProxyReferenceEqualityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/proxy/ProxyReferenceEqualityTest.java @@ -63,7 +63,7 @@ public class ProxyReferenceEqualityTest extends BaseCoreFunctionalTestCase { } ); } - @Entity + @Entity(name = "A") public static class A { @Id Long id; @@ -72,7 +72,7 @@ public class ProxyReferenceEqualityTest extends BaseCoreFunctionalTestCase { B b; } - @Entity + @Entity(name = "B") public static class B { @Id Long id; diff --git a/hibernate-core/src/test/java/org/hibernate/test/querytimeout/QueryTimeOutTest.java b/hibernate-core/src/test/java/org/hibernate/test/querytimeout/QueryTimeOutTest.java index fa8f308024..9298b7c143 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/querytimeout/QueryTimeOutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/querytimeout/QueryTimeOutTest.java @@ -15,6 +15,7 @@ import javax.persistence.QueryHint; import javax.persistence.Table; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.jpa.QueryHints; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; @@ -48,6 +49,7 @@ public class QueryTimeOutTest extends BaseNonConfigCoreFunctionalTestCase { @Override protected void addSettings(Map settings) { + CONNECTION_PROVIDER.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, CONNECTION_PROVIDER ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/quote/TableGeneratorQuotingTest.java b/hibernate-core/src/test/java/org/hibernate/test/quote/TableGeneratorQuotingTest.java index e6ef1a0a17..f87ea73575 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/quote/TableGeneratorQuotingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/quote/TableGeneratorQuotingTest.java @@ -61,23 +61,26 @@ public class TableGeneratorQuotingTest extends BaseUnitTestCase { final Metadata metadata = new MetadataSources( serviceRegistry ).addAnnotatedClass( TestEntity.class ).buildMetadata(); final ConnectionProvider connectionProvider = serviceRegistry.getService( ConnectionProvider.class ); - final GenerationTarget target = new GenerationTargetToDatabase( - new DdlTransactionIsolatorTestingImpl( - serviceRegistry, - new JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess( connectionProvider ) - ) + + final DdlTransactionIsolatorTestingImpl ddlTransactionIsolator = new DdlTransactionIsolatorTestingImpl( + serviceRegistry, + new JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess( connectionProvider ) ); - - new SchemaCreatorImpl( serviceRegistry ).doCreation( metadata, false, target ); - try { - new SchemaValidator().validate( metadata ); - } - catch (HibernateException e) { - fail( "The identifier generator table should have validated. " + e.getMessage() ); + final GenerationTarget target = new GenerationTargetToDatabase( ddlTransactionIsolator, false ); + try { + new SchemaCreatorImpl( serviceRegistry ).doCreation( metadata, false, target ); + new SchemaValidator().validate( metadata ); + } + catch (HibernateException e) { + fail( "The identifier generator table should have validated. " + e.getMessage() ); + } + finally { + new SchemaDropperImpl( serviceRegistry ).doDrop( metadata, false, target ); + } } finally { - new SchemaDropperImpl( serviceRegistry ).doDrop( metadata, false, target ); + ddlTransactionIsolator.release(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/schematools/TestExtraPhysicalTableTypes.java b/hibernate-core/src/test/java/org/hibernate/test/schematools/TestExtraPhysicalTableTypes.java index 9cd339cc76..6547c7adf3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schematools/TestExtraPhysicalTableTypes.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schematools/TestExtraPhysicalTableTypes.java @@ -51,53 +51,77 @@ public class TestExtraPhysicalTableTypes { @Test public void testAddOneExtraPhysicalTableType() throws Exception { buildMetadata( "BASE TABLE" ); - InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest(); - - assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( true ) ); - assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + DdlTransactionIsolator ddlTransactionIsolator = buildDdlTransactionIsolator(); + try { + InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest( + ddlTransactionIsolator + ); + assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( true ) ); + assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + } + finally { + ddlTransactionIsolator.release(); + } } @Test public void testAddingMultipleExtraPhysicalTableTypes() throws Exception { buildMetadata( "BASE, BASE TABLE" ); - InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest(); - - assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( true ) ); - assertThat( informationExtractor.isPhysicalTableType( "BASE" ), is( true ) ); - assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); - assertThat( informationExtractor.isPhysicalTableType( "TABLE 1" ), is( false ) ); + DdlTransactionIsolator ddlTransactionIsolator = buildDdlTransactionIsolator(); + try { + InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest( + ddlTransactionIsolator + ); + assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( true ) ); + assertThat( informationExtractor.isPhysicalTableType( "BASE" ), is( true ) ); + assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + assertThat( informationExtractor.isPhysicalTableType( "TABLE 1" ), is( false ) ); + } + finally { + ddlTransactionIsolator.release(); + } } @Test public void testExtraPhysicalTableTypesPropertyEmptyStringValue() throws Exception { buildMetadata( " " ); - InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest(); - - assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( false ) ); - assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + DdlTransactionIsolator ddlTransactionIsolator = buildDdlTransactionIsolator(); + try { + InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest( + ddlTransactionIsolator + ); + assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( false ) ); + assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + } + finally { + ddlTransactionIsolator.release(); + } } @Test public void testNoExtraPhysicalTabeTypesProperty() throws Exception { buildMetadata( null ); - InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest(); - - assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( false ) ); - assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + DdlTransactionIsolator ddlTransactionIsolator = buildDdlTransactionIsolator(); + try { + InformationExtractorJdbcDatabaseMetaDataImplTest informationExtractor = buildInformationExtractorJdbcDatabaseMetaDataImplTest( + ddlTransactionIsolator + ); + assertThat( informationExtractor.isPhysicalTableType( "BASE TABLE" ), is( false ) ); + assertThat( informationExtractor.isPhysicalTableType( "TABLE" ), is( true ) ); + } + finally { + ddlTransactionIsolator.release(); + } } - private InformationExtractorJdbcDatabaseMetaDataImplTest buildInformationExtractorJdbcDatabaseMetaDataImplTest() + private InformationExtractorJdbcDatabaseMetaDataImplTest buildInformationExtractorJdbcDatabaseMetaDataImplTest(DdlTransactionIsolator ddlTransactionIsolator) throws SQLException { Database database = metadata.getDatabase(); - final ConnectionProvider connectionProvider = ssr.getService( ConnectionProvider.class ); DatabaseInformation dbInfo = new DatabaseInformationImpl( ssr, database.getJdbcEnvironment(), - new DdlTransactionIsolatorTestingImpl( ssr, - new JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess( - connectionProvider ) - ), + ddlTransactionIsolator, database.getDefaultNamespace().getName() ); ExtractionContextImpl extractionContext = new ExtractionContextImpl( @@ -113,6 +137,14 @@ public class TestExtraPhysicalTableTypes { extractionContext ); } + private DdlTransactionIsolator buildDdlTransactionIsolator() { + final ConnectionProvider connectionProvider = ssr.getService( ConnectionProvider.class ); + return new DdlTransactionIsolatorTestingImpl( + ssr, + new JdbcEnvironmentInitiator.ConnectionProviderJdbcConnectionAccess( connectionProvider ) + ); + } + private void buildMetadata(String extraPhysicalTableTypes) { if ( extraPhysicalTableTypes == null ) { ssr = new StandardServiceRegistryBuilder().build(); @@ -128,8 +160,16 @@ public class TestExtraPhysicalTableTypes { } public class InformationExtractorJdbcDatabaseMetaDataImplTest extends InformationExtractorJdbcDatabaseMetaDataImpl { + + private final ExtractionContext extractionContext; + public InformationExtractorJdbcDatabaseMetaDataImplTest(ExtractionContext extractionContext) { super( extractionContext ); + this.extractionContext = extractionContext; + } + + public ExtractionContext getExtractionContext() { + return extractionContext; } public boolean isPhysicalTableType(String tableType) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/PostgreSQLMultipleSchemaSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/PostgreSQLMultipleSchemaSequenceTest.java index 2d95d0c540..fffd46889d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/PostgreSQLMultipleSchemaSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/PostgreSQLMultipleSchemaSequenceTest.java @@ -98,8 +98,15 @@ public class PostgreSQLMultipleSchemaSequenceTest extends BaseUnitTestCase { fail(e.getMessage()); } + String existingUrl = (String) Environment.getProperties().get( AvailableSettings.URL ); + if ( existingUrl.indexOf( '?' ) == -1 ) { + existingUrl += "?"; + } + else { + existingUrl += "&"; + } StandardServiceRegistry ssr2 = new StandardServiceRegistryBuilder() - .applySetting( AvailableSettings.URL, Environment.getProperties().get(AvailableSettings.URL) + "?currentSchema=" + extraSchemaName ) + .applySetting( AvailableSettings.URL, existingUrl + "currentSchema=" + extraSchemaName ) .build(); try { diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaMigratorHaltOnErrorTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaMigratorHaltOnErrorTest.java index 9f753b41d1..2e4acd9792 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaMigratorHaltOnErrorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaMigratorHaltOnErrorTest.java @@ -16,6 +16,7 @@ import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.SkipForDialect; import org.hibernate.tool.schema.spi.SchemaManagementException; @@ -25,6 +26,7 @@ import org.junit.Test; * @author Vlad Mihalcea */ @SkipForDialect(value = DB2Dialect.class, comment = "DB2 is far more resistant to the reserved keyword usage. See HHH-12832.") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby is far more resistant to the reserved keyword usage.") public class SchemaMigratorHaltOnErrorTest extends BaseEntityManagerFunctionalTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaUpdateHaltOnErrorTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaUpdateHaltOnErrorTest.java index a52c80d65e..dc8a7124d4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaUpdateHaltOnErrorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schemaupdate/SchemaUpdateHaltOnErrorTest.java @@ -23,6 +23,8 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.dialect.DB2Dialect; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.CustomRunner; + +import org.hibernate.dialect.DerbyDialect; import org.hibernate.tool.hbm2ddl.SchemaUpdate; import org.hibernate.tool.schema.TargetType; import org.hibernate.tool.schema.spi.SchemaManagementException; @@ -36,6 +38,7 @@ import org.junit.runner.RunWith; * @author Gail Badner */ @SkipForDialect(value = DB2Dialect.class, comment = "DB2 is far more resistant to the reserved keyword usage. See HHH-12832.") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby is far more resistant to the reserved keyword usage.") @RunWith(CustomRunner.class) public class SchemaUpdateHaltOnErrorTest { diff --git a/hibernate-core/src/test/java/org/hibernate/test/schemavalidation/LongVarcharValidationTest.java b/hibernate-core/src/test/java/org/hibernate/test/schemavalidation/LongVarcharValidationTest.java index bd96d28c44..6218d9e8fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/schemavalidation/LongVarcharValidationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/schemavalidation/LongVarcharValidationTest.java @@ -12,6 +12,7 @@ import java.util.EnumSet; import java.util.Map; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Table; import org.hibernate.annotations.Type; import org.hibernate.boot.MetadataSources; @@ -155,13 +156,14 @@ public class LongVarcharValidationTest implements ExecutionOptions { ); } -@Entity(name = "Translation") -public static class Translation { - @Id - public Integer id; - @Type(type = "text") - String text; -} + @Entity(name = "Translation") + @Table(name = "translation_tbl") + public static class Translation { + @Id + public Integer id; + @Type(type = "text") + String text; + } @Override public Map getConfigurationValues() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeCustomTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeCustomTimeZoneTest.java index 6baae104f6..84c152be58 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeCustomTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeCustomTimeZoneTest.java @@ -22,6 +22,7 @@ import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.MySQL5Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -62,6 +63,7 @@ public class JdbcTimeCustomTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeDefaultTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeDefaultTimeZoneTest.java index 666e3517ca..0c8f09f7fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeDefaultTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimeDefaultTimeZoneTest.java @@ -15,6 +15,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -49,6 +50,7 @@ public class JdbcTimeDefaultTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomSessionLevelTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomSessionLevelTimeZoneTest.java index 95a65a8f9c..1cc0d02be6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomSessionLevelTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomSessionLevelTimeZoneTest.java @@ -19,6 +19,7 @@ import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.MySQL5Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -59,6 +60,7 @@ public class JdbcTimestampCustomSessionLevelTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomTimeZoneTest.java index 8db15237d9..a7be6e01ee 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampCustomTimeZoneTest.java @@ -19,6 +19,7 @@ import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.MySQL5Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -59,6 +60,7 @@ public class JdbcTimestampCustomTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampDefaultTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampDefaultTimeZoneTest.java index 9cfb20911b..1ad16d8122 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampDefaultTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampDefaultTimeZoneTest.java @@ -14,6 +14,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -47,6 +48,7 @@ public class JdbcTimestampDefaultTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampUTCTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampUTCTimeZoneTest.java index f07509bda6..68ea20f04a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampUTCTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampUTCTimeZoneTest.java @@ -11,6 +11,7 @@ import java.util.TimeZone; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.PostgreSQL82Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.RequiresDialect; import org.hibernate.test.util.jdbc.TimeZoneConnectionProvider; @@ -29,6 +30,7 @@ public class JdbcTimestampUTCTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithDefaultUTCTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithDefaultUTCTimeZoneTest.java index 817a0c9253..7cc4a08ac6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithDefaultUTCTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithDefaultUTCTimeZoneTest.java @@ -10,6 +10,7 @@ import java.util.Map; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.PostgreSQL82Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.RequiresDialect; import org.hibernate.test.util.jdbc.TimeZoneConnectionProvider; @@ -26,6 +27,7 @@ public class JdbcTimestampWithDefaultUTCTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithoutUTCTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithoutUTCTimeZoneTest.java index 10c64076ee..c1cb8f6530 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithoutUTCTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/JdbcTimestampWithoutUTCTimeZoneTest.java @@ -18,6 +18,7 @@ import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.PostgreSQL82Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.SkipForDialect; @@ -47,6 +48,7 @@ public class JdbcTimestampWithoutUTCTimeZoneTest @Override protected void addSettings(Map settings) { + connectionProvider.setConnectionProvider( (ConnectionProvider) settings.get( AvailableSettings.CONNECTION_PROVIDER ) ); settings.put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider diff --git a/hibernate-core/src/test/java/org/hibernate/test/timestamp/LocalDateCustomSessionLevelTimeZoneTest.java b/hibernate-core/src/test/java/org/hibernate/test/timestamp/LocalDateCustomSessionLevelTimeZoneTest.java index e96b512f0f..050149e7e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/timestamp/LocalDateCustomSessionLevelTimeZoneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/timestamp/LocalDateCustomSessionLevelTimeZoneTest.java @@ -13,9 +13,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQL5Dialect; -import org.hibernate.dialect.MySQL8Dialect; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java index 72aa37e67e..0a3db67312 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java @@ -142,19 +142,21 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase } ); inTransaction( session -> { session.doWork( connection -> { - final PreparedStatement statement = connection.prepareStatement( + try (PreparedStatement statement = connection.prepareStatement( "SELECT " + PROPERTY_COLUMN_NAME + " FROM " + ENTITY_NAME + " WHERE " + ID_COLUMN_NAME + " = ?" - ); - statement.setInt( 1, 1 ); - statement.execute(); - final ResultSet resultSet = statement.getResultSet(); - resultSet.next(); - Object nativeRead = getActualJdbcValue( resultSet, 1 ); - assertEquals( - "Values written by Hibernate ORM should match the original value (same day, hour, ...)", - getExpectedJdbcValueAfterHibernateWrite(), - nativeRead - ); + )) { + statement.setInt( 1, 1 ); + statement.execute(); + try (ResultSet resultSet = statement.getResultSet()) { + resultSet.next(); + Object nativeRead = getActualJdbcValue( resultSet, 1 ); + assertEquals( + "Values written by Hibernate ORM should match the original value (same day, hour, ...)", + getExpectedJdbcValueAfterHibernateWrite(), + nativeRead + ); + } + } } ); } ); } ); @@ -168,13 +170,14 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase withDefaultTimeZone( () -> { inTransaction( session -> { session.doWork( connection -> { - final PreparedStatement statement = connection.prepareStatement( + try (PreparedStatement statement = connection.prepareStatement( "INSERT INTO " + ENTITY_NAME + " (" + ID_COLUMN_NAME + ", " + PROPERTY_COLUMN_NAME + ") " + " VALUES ( ? , ? )" - ); - statement.setInt( 1, 1 ); - setJdbcValueForNonHibernateWrite( statement, 2 ); - statement.execute(); + )) { + statement.setInt( 1, 1 ); + setJdbcValueForNonHibernateWrite( statement, 2 ); + statement.execute(); + } } ); } ); inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java index f6c1f591e6..282662db09 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass/UnionSubclassTest.java @@ -17,6 +17,7 @@ import org.hibernate.Hibernate; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.Query; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; @@ -47,6 +48,7 @@ public class UnionSubclassTest extends BaseCoreFunctionalTestCase { public void configure(Configuration cfg) { super.configure( cfg ); if ( SQLServerDialect.class.isAssignableFrom( DIALECT.getClass() ) ) { + connectionProvider.setConnectionProvider( (ConnectionProvider) cfg.getProperties().get( AvailableSettings.CONNECTION_PROVIDER ) ); cfg.getProperties().put( AvailableSettings.CONNECTION_PROVIDER, connectionProvider ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/usertype/IntegratorProvidedUserTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/usertype/IntegratorProvidedUserTypeTest.java new file mode 100644 index 0000000000..fb015b2a16 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/usertype/IntegratorProvidedUserTypeTest.java @@ -0,0 +1,120 @@ +/* + * 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 . + */ +package org.hibernate.test.usertype; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.Enumeration; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.internal.util.ConfigHelper; +import org.hibernate.type.CustomType; +import org.hibernate.type.Type; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Christian Beikov + */ +public class IntegratorProvidedUserTypeTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + StringWrapperTestEntity.class + }; + } + + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + builder.applyClassLoader( new TestClassLoader() ); + } + + @Test + @TestForIssue( jiraKey = "HHH-14408" ) + public void test() { + Type type = sessionFactory().getMetamodel().entityPersister( StringWrapperTestEntity.class ) + .getPropertyType( "stringWrapper" ); + Assert.assertTrue( "Type was initialized too early i.e. before integrators were run", type instanceof CustomType ); + } + + @Entity + public static class StringWrapperTestEntity implements Serializable { + @Id + private Integer id; + private StringWrapper stringWrapper; + } + + private static class TestClassLoader extends ClassLoader { + + /** + * testStoppableClassLoaderService() needs a custom JDK service implementation. Rather than using a real one + * on the test classpath, force it in here. + */ + @Override + protected Enumeration findResources(String name) throws IOException { + if (name.equals( "META-INF/services/org.hibernate.integrator.spi.Integrator" )) { + final URL serviceUrl = ConfigHelper.findAsResource( + "org/hibernate/test/service/org.hibernate.integrator.spi.Integrator" ); + return new Enumeration() { + boolean hasMore = true; + + @Override + public boolean hasMoreElements() { + return hasMore; + } + + @Override + public URL nextElement() { + hasMore = false; + return serviceUrl; + } + }; + } + else { + return java.util.Collections.enumeration( java.util.Collections.emptyList() ); + } + } + + /** + * Reloading class from binary file. + * + * @param originalClass Original class. + * @throws IOException . + */ + public void overrideClass(final Class originalClass) throws IOException { + String originalPath = "/" + originalClass.getName().replaceAll("\\.", "/") + ".class"; + InputStream inputStream = originalClass.getResourceAsStream( originalPath); + Assert.assertNotNull(inputStream); + try { + byte[] data = toByteArray( inputStream ); + defineClass(originalClass.getName(), data, 0, data.length); + } finally { + inputStream.close(); + } + } + + private byte[] toByteArray(InputStream inputStream) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int read; + byte[] slice = new byte[2000]; + while ( (read = inputStream.read(slice, 0, slice.length) ) != -1) { + out.write( slice, 0, read ); + } + out.flush(); + return out.toByteArray(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapper.java b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapper.java new file mode 100644 index 0000000000..15312359fe --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapper.java @@ -0,0 +1,38 @@ +package org.hibernate.test.usertype; + +import java.io.Serializable; + +/** + * @author Christian Beikov + */ +public class StringWrapper implements Serializable { + + private final String value; + + public StringWrapper(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + StringWrapper that = (StringWrapper) o; + + return value.equals( that.value ); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserType.java b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserType.java new file mode 100644 index 0000000000..cc25a75836 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserType.java @@ -0,0 +1,112 @@ +package org.hibernate.test.usertype; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Objects; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.type.StringType; +import org.hibernate.usertype.UserType; + +import org.jboss.logging.Logger; + +/** + * @author Christian Beikov + */ +public class StringWrapperUserType implements UserType { + + public static final StringWrapperUserType INSTANCE = new StringWrapperUserType(); + + private static final Logger log = Logger.getLogger( StringWrapperUserType.class ); + + @Override + public int[] sqlTypes() { + return new int[] {StringType.INSTANCE.sqlType()}; + } + + @Override + public Class returnedClass() { + return StringWrapper.class; + } + + @Override + public boolean equals(Object x, Object y) + throws HibernateException { + return Objects.equals( x, y ); + } + + @Override + public int hashCode(Object x) + throws HibernateException { + return Objects.hashCode( x ); + } + + @Override + public Object nullSafeGet( + ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) + throws HibernateException, SQLException { + String columnName = names[0]; + String columnValue = (String) rs.getObject( columnName ); + log.debugv("Result set column {0} value is {1}", columnName, columnValue); + return columnValue == null ? null : + fromString( columnValue ); + } + + @Override + public void nullSafeSet( + PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) + throws HibernateException, SQLException { + if ( value == null ) { + log.debugv("Binding null to parameter {0} ",index); + st.setNull( index, Types.VARCHAR ); + } + else { + String stringValue = toString( (StringWrapper) value ); + log.debugv("Binding {0} to parameter {1} ", stringValue, index); + st.setString( index, stringValue ); + } + } + public String toString(StringWrapper value) { + return value.getValue(); + } + + public StringWrapper fromString(String string) { + if ( string == null || string.isEmpty() ) { + return null; + } + return new StringWrapper( string ); + } + + @Override + public Object deepCopy(Object value) + throws HibernateException { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value) + throws HibernateException { + return (StringWrapper) deepCopy( value ); + } + + @Override + public Object assemble(Serializable cached, Object owner) + throws HibernateException { + return deepCopy( cached ); + } + + @Override + public Object replace(Object original, Object target, Object owner) + throws HibernateException { + return deepCopy( original ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserTypeIntegrator.java b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserTypeIntegrator.java new file mode 100644 index 0000000000..71eb293508 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/usertype/StringWrapperUserTypeIntegrator.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.hibernate.test.usertype; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.integrator.spi.Integrator; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +/** + * @author Christian Beikov + */ +public class StringWrapperUserTypeIntegrator implements Integrator { + + @Override + public void integrate( + Metadata metadata, + BootstrapContext bootstrapContext, + SessionFactoryImplementor sessionFactory) { + MetadataImplementor mi = (MetadataImplementor) metadata; + mi.getTypeConfiguration().getBasicTypeRegistry().register( StringWrapperUserType.INSTANCE, new String[] { StringWrapperUserType.INSTANCE.returnedClass().getName()}); + } + + @Override + public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java index 69ee14569c..b4ac7872f3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/util/jdbc/PreparedStatementSpyConnectionProvider.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -39,6 +41,9 @@ public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDe .defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS ); private static final MockSettings VERIFIEABLE_MOCK_SETTINGS = Mockito.withSettings() .defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS ); + // We must keep around the mocked connections, otherwise they are garbage collected and trigger finalizers + // Since we use CALLS_REAL_METHODS this might close underlying IO resources which makes other objects unusable + private static final Queue MOCKS = new LinkedBlockingQueue<>(); private final Map preparedStatementMap = new LinkedHashMap<>(); @@ -75,6 +80,7 @@ public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDe @Override public Connection getConnection() throws SQLException { Connection connection = instrumentConnection( actualConnection() ); + MOCKS.add( connection ); acquiredConnections.add( connection ); return connection; } @@ -83,7 +89,7 @@ public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDe public void closeConnection(Connection conn) throws SQLException { acquiredConnections.remove( conn ); releasedConnections.add( conn ); - super.closeConnection( conn ); + super.closeConnection( (Connection) MockUtil.getMockSettings( conn ).getSpiedInstance() ); } @Override diff --git a/hibernate-core/src/test/resources/org/hibernate/query/hhh12225/Vehicle.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/query/hhh12225/Vehicle.hbm.xml index 0ba5e09cde..c026b5d57e 100644 --- a/hibernate-core/src/test/resources/org/hibernate/query/hhh12225/Vehicle.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/query/hhh12225/Vehicle.hbm.xml @@ -20,7 +20,7 @@ - + diff --git a/hibernate-core/src/test/resources/org/hibernate/test/service/org.hibernate.integrator.spi.Integrator b/hibernate-core/src/test/resources/org/hibernate/test/service/org.hibernate.integrator.spi.Integrator index 373ccfec43..0f46bd3b38 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/service/org.hibernate.integrator.spi.Integrator +++ b/hibernate-core/src/test/resources/org/hibernate/test/service/org.hibernate.integrator.spi.Integrator @@ -1 +1,2 @@ -org.hibernate.test.service.TestIntegrator \ No newline at end of file +org.hibernate.test.service.TestIntegrator +org.hibernate.test.usertype.StringWrapperUserTypeIntegrator \ No newline at end of file diff --git a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/GlobalTemporaryTableBulkCompositeIdTest.java b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/GlobalTemporaryTableBulkCompositeIdTest.java index 56cbfe9922..97a3486179 100644 --- a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/GlobalTemporaryTableBulkCompositeIdTest.java +++ b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/GlobalTemporaryTableBulkCompositeIdTest.java @@ -1,18 +1,15 @@ package org.hibernate.test.bulkid; -import org.hibernate.dialect.CockroachDB192Dialect; -import org.hibernate.dialect.PostgreSQL82Dialect; import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; import org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy; -import org.hibernate.testing.RequiresDialect; -import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.DialectChecks; +import org.hibernate.testing.RequiresDialectFeature; /** * @author Vlad Mihalcea */ -@RequiresDialect(PostgreSQL82Dialect.class) -@SkipForDialect(value = CockroachDB192Dialect.class, comment = "https://github.com/cockroachdb/cockroach/issues/5807") +@RequiresDialectFeature(DialectChecks.SupportsGlobalTemporaryTables.class) public class GlobalTemporaryTableBulkCompositeIdTest extends AbstractBulkCompositeIdTest { @Override diff --git a/hibernate-core/src/test_legacy/org/hibernate/test/criteria/InTest.java b/hibernate-core/src/test_legacy/org/hibernate/test/criteria/InTest.java index c520afab58..dc06b79740 100644 --- a/hibernate-core/src/test_legacy/org/hibernate/test/criteria/InTest.java +++ b/hibernate-core/src/test_legacy/org/hibernate/test/criteria/InTest.java @@ -14,8 +14,11 @@ import java.util.List; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; +import org.hibernate.dialect.DerbyDialect; + import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; +import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -50,6 +53,7 @@ public class InTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-8901" ) @RequiresDialectFeature(DialectChecks.NotSupportsEmptyInListCheck.class) + @SkipForDialect(value = DerbyDialect.class, comment = "Derby doesn't like `x in (null)`") public void testEmptyInListForDialectNotSupportsEmptyInList() { Session session = openSession(); Transaction tx = session.beginTransaction(); diff --git a/hibernate-core/src/test_legacy/org/hibernate/test/criteria/NationalizedIgnoreCaseTest.java b/hibernate-core/src/test_legacy/org/hibernate/test/criteria/NationalizedIgnoreCaseTest.java index 87644da102..9d2e501343 100644 --- a/hibernate-core/src/test_legacy/org/hibernate/test/criteria/NationalizedIgnoreCaseTest.java +++ b/hibernate-core/src/test_legacy/org/hibernate/test/criteria/NationalizedIgnoreCaseTest.java @@ -14,6 +14,7 @@ import javax.persistence.Table; import org.hibernate.annotations.Nationalized; import org.hibernate.criterion.Restrictions; import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.testing.SkipForDialect; @@ -29,6 +30,7 @@ import static org.junit.Assert.assertEquals; */ @TestForIssue( jiraKey = "HHH-8657" ) @SkipForDialect(value = DB2Dialect.class, comment = "DB2 jdbc driver doesn't support setNString") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby jdbc driver doesn't support setNString") @SkipForDialect(value = PostgreSQL81Dialect.class, comment = "PostgreSQL jdbc driver doesn't support setNString") public class NationalizedIgnoreCaseTest extends BaseCoreFunctionalTestCase { diff --git a/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/test/TestHelper.java b/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/test/TestHelper.java index 2155f26477..81be696f60 100644 --- a/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/test/TestHelper.java +++ b/hibernate-ehcache/src/test/java/org/hibernate/cache/ehcache/test/TestHelper.java @@ -76,7 +76,7 @@ public class TestHelper { final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder() .configure( "hibernate-config/hibernate.cfg.xml" ) .applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ) - .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE ) + .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP ) .applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" ); if ( H2Dialect.class.equals( Dialect.getDialect().getClass() ) ) { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/AbstractEnversTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/AbstractEnversTest.java index b49075665b..126491ece6 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/AbstractEnversTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/AbstractEnversTest.java @@ -9,6 +9,8 @@ package org.hibernate.envers.test; import java.util.Arrays; import java.util.List; +import org.hibernate.testing.cleaner.DatabaseCleaner; + import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -22,6 +24,10 @@ import org.jboss.logging.Logger; @RunWith(EnversRunner.class) public abstract class AbstractEnversTest { + static { + DatabaseCleaner.clearSchemas(); + } + protected final Logger log = Logger.getLogger( getClass() ); private String auditStrategy; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java index 6771ea9d09..8721186d6a 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java @@ -16,6 +16,7 @@ import javax.transaction.SystemException; import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -31,8 +32,12 @@ import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; + import org.hibernate.testing.AfterClassOnce; import org.hibernate.testing.BeforeClassOnce; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.jta.TestingJtaPlatformImpl; import org.hibernate.testing.junit4.Helper; import org.jboss.logging.Logger; @@ -139,6 +144,14 @@ public abstract class BaseEnversJPAFunctionalTestCase extends AbstractEnversTest config.put( AvailableSettings.XML_FILE_NAMES, dds ); } + if ( !Environment.getProperties().containsKey( Environment.CONNECTION_PROVIDER ) ) { + config.put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + config.put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + config.put( + org.hibernate.cfg.AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } addConfigOptions( config ); return config; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapLobTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapLobTest.java index 3eb944598f..347b9dfc92 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapLobTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapLobTest.java @@ -16,6 +16,8 @@ import javax.persistence.Id; import javax.persistence.Lob; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.envers.Audited; @@ -37,6 +39,8 @@ import static org.junit.Assert.assertEquals; @SkipForDialect(Oracle8iDialect.class) @SkipForDialect(value = PostgreSQL81Dialect.class, jiraKey = "HHH-11477", comment = "@Lob field in HQL predicate fails with error about text = bigint") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA doesn't support comparing LOBs with the = operator") +@SkipForDialect(value = DB2Dialect.class, comment = "DB2 jdbc driver doesn't support setNString") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby jdbc driver doesn't support setNString") public class StringMapLobTest extends BaseEnversJPAFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapNationalizedLobTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapNationalizedLobTest.java index 4c94ab6ac3..9eab2375b2 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapNationalizedLobTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/collection/StringMapNationalizedLobTest.java @@ -17,6 +17,8 @@ import javax.persistence.Lob; import org.hibernate.annotations.Nationalized; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.SybaseDialect; @@ -40,6 +42,8 @@ import static org.junit.Assert.assertEquals; @SkipForDialect(value = PostgreSQL81Dialect.class, jiraKey = "HHH-11477", comment = "@Lob field in HQL predicate fails with error about text = bigint") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA doesn't support comparing LOBs with the = operator") @SkipForDialect(value = SybaseDialect.class, comment = "Sybase doesn't support comparing LOBs with the = operator") +@SkipForDialect(value = DB2Dialect.class, comment = "DB2 jdbc driver doesn't support setNString") +@SkipForDialect(value = DerbyDialect.class, comment = "Derby jdbc driver doesn't support setNString") public class StringMapNationalizedLobTest extends BaseEnversJPAFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/tools/OrderSequenceGenerationTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/tools/OrderSequenceGenerationTest.java index 4ba281af9c..7ae397167d 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/tools/OrderSequenceGenerationTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/tools/OrderSequenceGenerationTest.java @@ -17,6 +17,7 @@ import org.hibernate.dialect.Oracle8iDialect; import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; import org.hibernate.envers.test.entities.StrTestEntity; +import org.hibernate.testing.BeforeClassOnce; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.TestForIssue; @@ -45,7 +46,7 @@ public class OrderSequenceGenerationTest extends BaseEnversJPAFunctionalTestCase return new Class[] { StrTestEntity.class }; } - @Before + @Override public void buildEntityManagerFactory() throws Exception { createSchema = File.createTempFile( "create_schema", ".sql" ); dropSchema = File.createTempFile( "drop_schema", ".sql" ); @@ -58,7 +59,7 @@ public class OrderSequenceGenerationTest extends BaseEnversJPAFunctionalTestCase options.put( AvailableSettings.HBM2DDL_SCRIPTS_CREATE_TARGET, createSchema.toPath() ); options.put( AvailableSettings.HBM2DDL_SCRIPTS_DROP_TARGET, dropSchema.toPath() ); options.put( AvailableSettings.HBM2DDL_SCRIPTS_ACTION, "drop-and-create" ); - options.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "drop-and-create" ); + options.put( AvailableSettings.HBM2DDL_DATABASE_ACTION, "create-drop" ); options.put( AvailableSettings.HBM2DDL_AUTO, "create-drop" ); } diff --git a/hibernate-hikaricp/src/test/java/org/hibernate/test/util/PreparedStatementSpyConnectionProvider.java b/hibernate-hikaricp/src/test/java/org/hibernate/test/util/PreparedStatementSpyConnectionProvider.java index 4b72c265b1..7fdd38ef2c 100644 --- a/hibernate-hikaricp/src/test/java/org/hibernate/test/util/PreparedStatementSpyConnectionProvider.java +++ b/hibernate-hikaricp/src/test/java/org/hibernate/test/util/PreparedStatementSpyConnectionProvider.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -32,6 +34,9 @@ import org.mockito.internal.util.MockUtil; public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDelegate { + // We must keep around the mocked connections, otherwise the are garbage collected and trigger finalizers + // Since we use CALLS_REAL_METHODS this might close underlying IO resources which make other objects unusable + private static final Queue MOCKS = new LinkedBlockingQueue<>(); private final Map preparedStatementMap = new LinkedHashMap<>(); private final List acquiredConnections = new ArrayList<>( ); @@ -47,6 +52,7 @@ public class PreparedStatementSpyConnectionProvider @Override public Connection getConnection() throws SQLException { Connection connection = spy( actualConnection() ); + MOCKS.add( connection ); acquiredConnections.add( connection ); return connection; } @@ -55,7 +61,7 @@ public class PreparedStatementSpyConnectionProvider public void closeConnection(Connection conn) throws SQLException { acquiredConnections.remove( conn ); releasedConnections.add( conn ); - super.closeConnection( conn ); + super.closeConnection( (Connection) MockUtil.getMockSettings( conn ).getSpiedInstance() ); } @Override diff --git a/hibernate-jcache/src/test/java/org/hibernate/jcache/test/RefreshUpdatedDataTest.java b/hibernate-jcache/src/test/java/org/hibernate/jcache/test/RefreshUpdatedDataTest.java index a1d7e0a471..9b84faf34f 100644 --- a/hibernate-jcache/src/test/java/org/hibernate/jcache/test/RefreshUpdatedDataTest.java +++ b/hibernate-jcache/src/test/java/org/hibernate/jcache/test/RefreshUpdatedDataTest.java @@ -21,6 +21,7 @@ import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -58,7 +59,7 @@ public class RefreshUpdatedDataTest extends BaseUnitTestCase { serviceRegistry = ssrb .configure( "hibernate-config/hibernate.cfg.xml" ) - .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE ) + .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP ) .build(); final MetadataSources metadataSources = new MetadataSources( serviceRegistry ); @@ -87,6 +88,7 @@ public class RefreshUpdatedDataTest extends BaseUnitTestCase { @Test @SkipForDialect(value = CockroachDialect.class, comment = "does not support nested transactions") + @SkipForDialect(value = DerbyDialect.class, comment = "Derby does not support nested transactions") public void testUpdateAndFlushThenRefresh() { final String BEFORE = "before"; diff --git a/hibernate-jcache/src/test/java/org/hibernate/jcache/test/TestHelper.java b/hibernate-jcache/src/test/java/org/hibernate/jcache/test/TestHelper.java index bbcb0d329b..fa1711d855 100644 --- a/hibernate-jcache/src/test/java/org/hibernate/jcache/test/TestHelper.java +++ b/hibernate-jcache/src/test/java/org/hibernate/jcache/test/TestHelper.java @@ -113,7 +113,7 @@ public class TestHelper { final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder() .configure( "hibernate-config/hibernate.cfg.xml" ) .applySetting( AvailableSettings.GENERATE_STATISTICS, "true" ) - .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE ) + .applySetting( AvailableSettings.HBM2DDL_DATABASE_ACTION, Action.CREATE_DROP ) .applySetting( AvailableSettings.HBM2DDL_AUTO, "create-drop" ); if ( H2Dialect.class.equals( Dialect.getDialect().getClass() ) ) { diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index 2ffcaed9f0..46a76e83ec 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -9,9 +9,17 @@ package org.hibernate.testing; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.FirebirdDialect; +import org.hibernate.dialect.HANAColumnStoreDialect; +import org.hibernate.dialect.HANARowStoreDialect; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.dialect.IngresDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.SybaseDialect; +import org.hibernate.dialect.TeradataDialect; +import org.hibernate.dialect.TimesTenDialect; /** * Container class for different implementation of the {@link DialectCheck} interface. @@ -291,4 +299,17 @@ abstract public class DialectChecks { ); } } + + public static class SupportsGlobalTemporaryTables implements DialectCheck { + public boolean isMatch(Dialect dialect) { + return dialect instanceof FirebirdDialect || + dialect instanceof HANAColumnStoreDialect || + dialect instanceof HANARowStoreDialect || + dialect instanceof HSQLDialect || + dialect instanceof IngresDialect || + dialect instanceof OracleDialect || + dialect instanceof TeradataDialect || + dialect instanceof TimesTenDialect; + } + } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/AbstractMySQLDatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/AbstractMySQLDatabaseCleaner.java new file mode 100644 index 0000000000..c4620a4b3d --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/AbstractMySQLDatabaseCleaner.java @@ -0,0 +1,194 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public abstract class AbstractMySQLDatabaseCleaner implements DatabaseCleaner { + private static final Logger LOG = Logger.getLogger( AbstractMySQLDatabaseCleaner.class.getName() ); + private static final String SYSTEM_SCHEMAS = "'information_schema'," + + "'mysql'," + + "'sys'," + + "'performance_schema'"; + private final List ignoredTables = new ArrayList<>(); + private final Map> clearingSqlsPerSchema = new HashMap<>(); + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName ); + } + + @Override + public void clearAllSchemas(Connection connection) { + try { + clearSchema( connection, connection.getSchema() ); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } + + @Override + public void clearSchema(Connection connection, String schemaName) { + clearSchema0( connection, schemaName ); + } + + private void clearSchema0(Connection c, String schemaName) { + clearingSqlsPerSchema.remove( schemaName ); + try (Statement s = c.createStatement()) { + // Collect schema names + LOG.log( Level.FINEST, "Collect table names: START" ); + ResultSet rs = s.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + schemaName + "'" ); + + StringBuilder sb = new StringBuilder( "DROP TABLE " ); + if ( rs.next() ) { + do { + String tableSchema = rs.getString( 1 ); + String tableName = rs.getString( 2 ); + sb.append( tableSchema ); + sb.append( '.' ); + sb.append( tableName ); + sb.append( ',' ); + } while ( rs.next() ); + } + else { + // Nothing to clear since there are no tables + return; + } + sb.setCharAt( sb.length() - 1, ' ' ); + LOG.log( Level.FINEST, "Collect table names: END" ); + + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + s.execute( "SET FOREIGN_KEY_CHECKS = 0" ); + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + LOG.log( Level.FINEST, "Dropping tables: START" ); + String sql = sb.toString(); + s.execute( sql ); + LOG.log( Level.FINEST, "Dropping tables: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + s.execute( "SET FOREIGN_KEY_CHECKS = 1" ); + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearAllData(Connection connection) { + clearData0( + connection, + null, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" ); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } + ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + clearData0( + connection, + schemaName, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + schemaName + "'" ); + } + catch (SQLException e) { + throw new RuntimeException( e ); + } + } + ); + } + + private void clearData0(Connection connection, String schemaName, Function tablesProvider) { + try (Statement s = connection.createStatement()) { + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + s.execute( "SET FOREIGN_KEY_CHECKS = 0" ); + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + List clearingSqls = clearingSqlsPerSchema.get( schemaName ); + if ( clearingSqls == null ) { + clearingSqls = new ArrayList<>(); + ResultSet rs = tablesProvider.apply( s ); + while ( rs.next() ) { + String tableSchema = rs.getString( 1 ); + String tableName = rs.getString( 2 ); + if ( !ignoredTables.contains( tableName ) ) { + clearingSqls.add( createClearingStatementForTable( tableSchema, tableName ) ); + } + } + clearingSqlsPerSchema.put( schemaName, clearingSqls ); + } + for ( String clearingSql : clearingSqls ) { + s.execute( clearingSql ); + } + LOG.log( Level.FINEST, "Deleting data: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + s.execute( "SET FOREIGN_KEY_CHECKS = 1" ); + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + connection.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + connection.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + protected abstract String createClearingStatementForTable(String tableSchema, String tableName); + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DB2DatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DB2DatabaseCleaner.java new file mode 100644 index 0000000000..b359cbada1 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DB2DatabaseCleaner.java @@ -0,0 +1,416 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public class DB2DatabaseCleaner implements DatabaseCleaner { + + private static final Logger LOG = Logger.getLogger( DB2DatabaseCleaner.class.getName() ); + private static final String SYSTEM_SCHEMAS = "'SYSCAT'," + + "'SYSIBM'," + + "'SYSIBMADM'," + + "'SYSPUBLIC'," + + "'SYSSTAT'," + + "'SYSTOOLS'"; + + private final List ignoredTables = new ArrayList<>(); + private final Map>> cachedForeignKeysPerSchema = new HashMap<>(); + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "DB2" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName ); + } + + @Override + public void clearAllSchemas(Connection c) { + cachedForeignKeysPerSchema.clear(); + try (Statement s = c.createStatement()) { + ResultSet rs; + List sqls = new ArrayList<>(); + + // Collect schema objects + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = s.executeQuery( + "SELECT 'DROP INDEX \"' || TRIM(INDSCHEMA) || '\".\"' || TRIM(INDNAME) || '\"' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'D' " + + "AND INDSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP FOREIGN KEY \"' || TRIM(CONSTNAME) || '\"' " + + "FROM SYSCAT.TABCONST " + + "WHERE TYPE = 'F' " + + "AND TABSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP UNIQUE \"' || TRIM(INDNAME) || '\"' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'U' " + + "AND INDSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP PRIMARY KEY' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'P' " + + "AND INDSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP VIEW \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\"' " + + "FROM SYSCAT.TABLES " + + "WHERE TYPE = 'V' " + + "AND TABSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\"' " + + "FROM SYSCAT.TABLES " + + "WHERE TYPE = 'T' " + + "AND TABSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP SEQUENCE \"' || TRIM(SEQSCHEMA) || '\".\"' || TRIM(SEQNAME) || '\"' " + + "FROM SYSCAT.SEQUENCES " + + "WHERE SEQSCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + try { + s.execute( sql ); + } + catch (SQLException e) { + if ( -204 == e.getErrorCode() ) { + // Apparently we deleted this along with a dependent object since it doesn't exist anymore + } + else { + throw e; + } + } + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearSchema(Connection c, String schemaName) { + cachedForeignKeysPerSchema.remove( schemaName ); + schemaName = schemaName.toUpperCase(); + try (Statement s = c.createStatement()) { + ResultSet rs; + List sqls = new ArrayList<>(); + + // Collect schema objects + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = s.executeQuery( + "SELECT 'DROP INDEX \"' || TRIM(INDSCHEMA) || '\".\"' || TRIM(INDNAME) || '\"' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'D' " + + "AND INDSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP FOREIGN KEY \"' || TRIM(CONSTNAME) || '\"' " + + "FROM SYSCAT.TABCONST " + + "WHERE TYPE = 'F' " + + "AND TABSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP UNIQUE \"' || TRIM(INDNAME) || '\"' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'U' " + + "AND INDSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'ALTER TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\" DROP PRIMARY KEY' " + + "FROM SYSCAT.INDEXES " + + "WHERE UNIQUERULE = 'P' " + + "AND INDSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP VIEW \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\"' " + + "FROM SYSCAT.TABLES " + + "WHERE TYPE = 'V' " + + "AND TABSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP TABLE \"' || TRIM(TABSCHEMA) || '\".\"' || TRIM(TABNAME) || '\"' " + + "FROM SYSCAT.TABLES " + + "WHERE TYPE = 'T' " + + "AND TABSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP SEQUENCE \"' || TRIM(SEQSCHEMA) || '\".\"' || TRIM(SEQNAME) || '\"' " + + "FROM SYSCAT.SEQUENCES " + + "WHERE SEQSCHEMA = '" + schemaName + "'" + ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + try { + s.execute( sql ); + } + catch (SQLException e) { + if ( -204 == e.getErrorCode() ) { + // Apparently we deleted this along with a dependent object since it doesn't exist anymore + } + else { + throw e; + } + } + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearAllData(Connection connection) { + Map> cachedForeignKeys = cachedForeignKeysPerSchema.get( null ); + if ( cachedForeignKeys == null ) { + cachedForeignKeys = collectAllForeignKeys( connection ); + cachedForeignKeysPerSchema.put( null, cachedForeignKeys ); + } + deleteAllData( connection, cachedForeignKeys ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + Map> cachedForeignKeys = cachedForeignKeysPerSchema.get( schemaName ); + if ( cachedForeignKeys == null ) { + cachedForeignKeys = collectForeignKeys( connection, schemaName.toUpperCase() ); + cachedForeignKeysPerSchema.put( schemaName, cachedForeignKeys ); + } + deleteAllData( connection, cachedForeignKeys ); + } + + private Map> collectAllForeignKeys(Connection c) { + try (Statement s = c.createStatement()) { + // Collect table names for schemas + LOG.log( Level.FINEST, "Collect table names: START" ); + ResultSet rs = s.executeQuery( + "SELECT TABLE_SCHEMA || '.' || TABLE_NAME FROM SYSIBM.TABLES WHERE TABLE_SCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + Map> foreignKeys = new HashMap<>(); + while ( rs.next() ) { + foreignKeys.put( rs.getString( 1 ), new ArrayList() ); + } + LOG.log( Level.FINEST, "Collect table names: END" ); + + // Collect foreign keys for tables + LOG.log( Level.FINEST, "Collect foreign keys: START" ); + ResultSet rs2 = s.executeQuery( + "SELECT FKTABLE_SCHEM || '.' || FKTABLE_NAME, FK_NAME FROM SYSIBM.SQLFOREIGNKEYS WHERE FKTABLE_SCHEM NOT IN (" + SYSTEM_SCHEMAS + ")" + ); + while ( rs2.next() ) { + foreignKeys.get( rs2.getString( 1 ) ).add( rs2.getString( 2 ) ); + } + LOG.log( Level.FINEST, "Collect foreign keys: END" ); + + return foreignKeys; + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + private Map> collectForeignKeys(Connection c, String schemaName) { + try (Statement s = c.createStatement()) { + // Collect table names for schemas + LOG.log( Level.FINEST, "Collect table names: START" ); + ResultSet rs = s.executeQuery( + "SELECT TRIM(TABLE_SCHEMA) || '.' || TABLE_NAME FROM SYSIBM.TABLES WHERE TABLE_SCHEMA = '" + schemaName + "'" + ); + Map> foreignKeys = new HashMap<>(); + while ( rs.next() ) { + foreignKeys.put( rs.getString( 1 ), new ArrayList() ); + } + LOG.log( Level.FINEST, "Collect table names: END" ); + + // Collect foreign keys for tables + LOG.log( Level.FINEST, "Collect foreign keys: START" ); + ResultSet rs2 = s.executeQuery( + "SELECT FKTABLE_SCHEM || '.' || FKTABLE_NAME, FK_NAME FROM SYSIBM.SQLFOREIGNKEYS WHERE FKTABLE_SCHEM = '" + schemaName + "'" + ); + while ( rs2.next() ) { + foreignKeys.get( rs2.getString( 1 ) ).add( rs2.getString( 2 ) ); + } + LOG.log( Level.FINEST, "Collect foreign keys: END" ); + + return foreignKeys; + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + private void deleteAllData(Connection c, Map> foreignKeys) { + try (Statement s = c.createStatement()) { + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + for ( Map.Entry> entry : foreignKeys.entrySet() ) { + for ( String fk : entry.getValue() ) { + s.execute( "ALTER TABLE " + entry.getKey() + " ALTER FOREIGN KEY " + fk + " NOT ENFORCED" ); + } + } + c.commit(); + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + for ( String table : foreignKeys.keySet() ) { + if ( !ignoredTables.contains( table ) ) { + s.execute( "TRUNCATE TABLE " + table + " IMMEDIATE" ); + // DB2 needs a commit after every truncate statement + c.commit(); + } + } + LOG.log( Level.FINEST, "Deleting data: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + for ( Map.Entry> entry : foreignKeys.entrySet() ) { + for ( String fk : entry.getValue() ) { + s.execute( "ALTER TABLE " + entry.getKey() + " ALTER FOREIGN KEY " + fk + " ENFORCED" ); + } + } + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleaner.java new file mode 100644 index 0000000000..c4c1750d02 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleaner.java @@ -0,0 +1,42 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; + +/** + * @author Christian Beikov + */ +public interface DatabaseCleaner { + + static void clearSchemas() { + final DatabaseCleaner cleaner = DatabaseCleanerContext.CLEANER; + if ( cleaner != null ) { + JdbcConnectionContext.work( cleaner::clearAllSchemas ); + } + } + + static void clearData() { + final DatabaseCleaner cleaner = DatabaseCleanerContext.CLEANER; + if ( cleaner != null ) { + JdbcConnectionContext.work( cleaner::clearAllData ); + } + } + + void addIgnoredTable(String tableName); + + boolean isApplicable(Connection connection); + + void clearAllSchemas(Connection connection); + + void clearSchema(Connection connection, String schemaName); + + void clearAllData(Connection connection); + + void clearData(Connection connection, String schemaName); + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleanerContext.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleanerContext.java new file mode 100644 index 0000000000..f9496a2740 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/DatabaseCleanerContext.java @@ -0,0 +1,41 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +/** + * @author Christian Beikov + */ +public final class DatabaseCleanerContext { + + public static final DatabaseCleaner CLEANER; + + static { + CLEANER = JdbcConnectionContext.workReturning( + connection -> { + final DatabaseCleaner[] cleaners = new DatabaseCleaner[] { + new DB2DatabaseCleaner(), + new H2DatabaseCleaner(), + new SQLServerDatabaseCleaner(), + new MySQL5DatabaseCleaner(), + new MySQL8DatabaseCleaner(), + new MariaDBDatabaseCleaner(), + new OracleDatabaseCleaner(), + new PostgreSQLDatabaseCleaner() + }; + for ( DatabaseCleaner cleaner : cleaners ) { + if ( cleaner.isApplicable( connection ) ) { + return cleaner; + } + } + return null; + } + ); + } + + private DatabaseCleanerContext() { + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/H2DatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/H2DatabaseCleaner.java new file mode 100644 index 0000000000..48011298bf --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/H2DatabaseCleaner.java @@ -0,0 +1,158 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public class H2DatabaseCleaner implements DatabaseCleaner { + + private static final Logger LOG = Logger.getLogger( H2DatabaseCleaner.class.getName() ); + private static final String SYSTEM_SCHEMAS = "'INFORMATION_SCHEMA'"; + + private final List ignoredTables = new ArrayList<>(); + private final Map> cachedTableNamesPerSchema = new HashMap<>(); + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "H2" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName ); + } + + @Override + public void clearAllSchemas(Connection c) { + cachedTableNamesPerSchema.clear(); + try (Statement s = c.createStatement()) { + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + s.execute( "DROP ALL OBJECTS" ); + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearSchema(Connection c, String schemaName) { + throw new UnsupportedOperationException(); + } + + @Override + public void clearAllData(Connection connection) { + clearData0( + connection, + null, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN (" + SYSTEM_SCHEMAS + ")" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + clearData0( + connection, + schemaName, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + schemaName + "'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearData0(Connection connection, String schemaName, Function tablesProvider) { + try (Statement s = connection.createStatement()) { + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + s.execute( "SET REFERENTIAL_INTEGRITY FALSE" ); + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + List cachedTableNames = cachedTableNamesPerSchema.get( schemaName ); + if ( cachedTableNames == null ) { + cachedTableNames = new ArrayList<>(); + ResultSet rs = tablesProvider.apply( s ); + while ( rs.next() ) { + String tableSchema = rs.getString( 1 ); + String tableName = rs.getString( 2 ); + if ( !ignoredTables.contains( tableName ) ) { + cachedTableNames.add( tableSchema + "." + tableName ); + } + } + cachedTableNamesPerSchema.put( schemaName, cachedTableNames ); + } + for ( String table : cachedTableNames ) { + s.execute( "TRUNCATE TABLE " + table ); + } + LOG.log( Level.FINEST, "Deleting data: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + s.execute( "SET REFERENTIAL_INTEGRITY TRUE" ); + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + connection.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + connection.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/JdbcConnectionContext.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/JdbcConnectionContext.java new file mode 100644 index 0000000000..365a6c6f8f --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/JdbcConnectionContext.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.Driver; +import java.util.Properties; + +import org.hibernate.cfg.AvailableSettings; + +/** + * @author Christian Beikov + */ +public final class JdbcConnectionContext { + private static final Driver driver; + private static final String url; + private static final String user; + private static final String password; + private static final Properties properties; + static { + final Properties connectionProperties = new Properties(); + try (InputStream inputStream = Thread.currentThread() + .getContextClassLoader() + .getResourceAsStream( "hibernate.properties" )) { + connectionProperties.load( inputStream ); + final String driverClassName = connectionProperties.getProperty( + AvailableSettings.DRIVER ); + driver = (Driver) Class.forName( driverClassName ).newInstance(); + url = connectionProperties.getProperty( + AvailableSettings.URL ); + user = connectionProperties.getProperty( + AvailableSettings.USER ); + password = connectionProperties.getProperty( + AvailableSettings.PASS ); + Properties p = new Properties(); + p.put( "user", user ); + p.put( "password", password ); + properties = p; + } + catch (Exception e) { + throw new IllegalArgumentException( e ); + } + } + + public static void work(ConnectionConsumer work) { + try (Connection connection = driver.connect( url, properties )) { + connection.setAutoCommit( false ); + work.consume( connection ); + } + catch (Exception e) { + throw new IllegalArgumentException( e ); + } + } + + public static R workReturning(ConnectionFunction work) { + try (Connection connection = driver.connect( url, properties )) { + connection.setAutoCommit( false ); + return work.apply( connection ); + } + catch (Exception e) { + throw new IllegalArgumentException( e ); + } + } + + public static interface ConnectionConsumer { + void consume(Connection c) throws Exception; + } + public static interface ConnectionFunction { + R apply(Connection c) throws Exception; + } + + private JdbcConnectionContext() { + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MariaDBDatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MariaDBDatabaseCleaner.java new file mode 100644 index 0000000000..4d69f08699 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MariaDBDatabaseCleaner.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author Christian Beikov + */ +public class MariaDBDatabaseCleaner extends AbstractMySQLDatabaseCleaner { + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().equals( "MariaDB" ) + && connection.getMetaData().getDriverName().startsWith( "MariaDB" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + protected String createClearingStatementForTable(String tableSchema, String tableName) { + return "TRUNCATE " + tableSchema + "." + tableName; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL5DatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL5DatabaseCleaner.java new file mode 100644 index 0000000000..cdb436d836 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL5DatabaseCleaner.java @@ -0,0 +1,33 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author Christian Beikov + */ +public class MySQL5DatabaseCleaner extends AbstractMySQLDatabaseCleaner { + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "MySQL" ) && connection.getMetaData() + .getDatabaseMajorVersion() < 8; + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + protected String createClearingStatementForTable(String tableSchema, String tableName) { + // We do not use TRUNCATE for MySQL 5.7 due to https://bugs.mysql.com/bug.php?id=68184 + return "DELETE FROM " + tableSchema + "." + tableName; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL8DatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL8DatabaseCleaner.java new file mode 100644 index 0000000000..bcb81fdca0 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/MySQL8DatabaseCleaner.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.SQLException; + +/** + * @author Christian Beikov + */ +public class MySQL8DatabaseCleaner extends AbstractMySQLDatabaseCleaner { + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "MySQL" ) && connection.getMetaData() + .getDatabaseMajorVersion() >= 8; + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + protected String createClearingStatementForTable(String tableSchema, String tableName) { + return "TRUNCATE " + tableSchema + "." + tableName; + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/OracleDatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/OracleDatabaseCleaner.java new file mode 100644 index 0000000000..bf0717d3e2 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/OracleDatabaseCleaner.java @@ -0,0 +1,272 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public class OracleDatabaseCleaner implements DatabaseCleaner { + + private static final Logger LOG = Logger.getLogger( OracleDatabaseCleaner.class.getName() ); + private static final String SYSTEM_SEQUENCE_OWNERS = "'SYS'," + + "'CTXSYS'," + + "'DVSYS'," + + "'OJVMSYS'," + + "'ORDDATA'," + + "'MDSYS'," + + "'OLAPSYS'," + + "'LBACSYS'," + + "'XDB'," + + "'WMSYS'"; + + private final List ignoredTables = new ArrayList<>(); + private final Map> cachedTruncateTableSqlPerSchema = new HashMap<>(); + private final Map> cachedConstraintDisableSqlPerSchema = new HashMap<>(); + private final Map> cachedConstraintEnableSqlPerSchema = new HashMap<>(); + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "Oracle" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName ); + } + + @Override + public void clearAllSchemas(Connection connection) { + cachedTruncateTableSqlPerSchema.clear(); + cachedConstraintDisableSqlPerSchema.clear(); + cachedConstraintEnableSqlPerSchema.clear(); + clearSchema0( + connection, + statement -> { + try { + return statement.executeQuery( + "SELECT 'DROP TABLE ' || owner || '.\"' || table_name || '\" CASCADE CONSTRAINTS' " + + "FROM all_tables " + + // Exclude the tables owner by sys + "WHERE owner NOT IN ('SYS')" + + // Normally, user tables aren't in sysaux + " AND tablespace_name NOT IN ('SYSAUX')" + + // Apparently, user tables have global stats off + " AND global_stats = 'NO'" + + // Exclude the tables with names starting like 'DEF$_' + " AND table_name NOT LIKE 'DEF$\\_%' ESCAPE '\\'" + + " UNION ALL " + + "SELECT 'DROP SEQUENCE ' || sequence_owner || '.' || sequence_name FROM all_sequences WHERE sequence_owner NOT IN (" + SYSTEM_SEQUENCE_OWNERS + ")" + ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearSchema(Connection connection, String schemaName) { + cachedTruncateTableSqlPerSchema.remove( schemaName ); + cachedConstraintDisableSqlPerSchema.remove( schemaName ); + cachedConstraintEnableSqlPerSchema.remove( schemaName ); + clearSchema0( + connection, + statement -> { + try { + return statement.executeQuery( + "SELECT 'DROP TABLE ' || owner || '.\"' || table_name || '\" CASCADE CONSTRAINTS' " + + "FROM all_tables " + + "WHERE owner = '" + schemaName + "'" + + // Normally, user tables aren't in sysaux + " AND tablespace_name NOT IN ('SYSAUX')" + + // Apparently, user tables have global stats off + " AND global_stats = 'NO'" + + // Exclude the tables with names starting like 'DEF$_' + " AND table_name NOT LIKE 'DEF$\\_%' ESCAPE '\\'" + + " UNION ALL " + + "SELECT 'DROP SEQUENCE ' || sequence_owner || '.' || sequence_name FROM all_sequences WHERE sequence_owner = '" + schemaName + "'" + ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearSchema0(Connection c, Function sqlProvider) { + try (Statement s = c.createStatement()) { + ResultSet rs; + List sqls = new ArrayList<>(); + + // Collect schema objects + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = sqlProvider.apply( s ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearAllData(Connection connection) { + clearData0( + connection, + null, + statement -> { + try { + return statement.executeQuery( + "SELECT tbl.owner || '.\"' || tbl.table_name || '\"', c.constraint_name FROM (" + + "SELECT owner, table_name " + + "FROM all_tables " + + // Exclude the tables owner by sys + "WHERE owner NOT IN ('SYS')" + + // Normally, user tables aren't in sysaux + " AND tablespace_name NOT IN ('SYSAUX')" + + // Apparently, user tables have global stats off + " AND global_stats = 'NO'" + + // Exclude the tables with names starting like 'DEF$_' + " AND table_name NOT LIKE 'DEF$\\_%' ESCAPE '\\'" + + ") tbl LEFT JOIN all_constraints c ON tbl.owner = c.owner AND tbl.table_name = c.table_name AND constraint_type = 'R'" + ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + clearData0( + connection, + schemaName, statement -> { + try { + return statement.executeQuery( + "SELECT tbl.owner || '.\"' || tbl.table_name || '\"', c.constraint_name FROM (" + + "SELECT owner, table_name " + + "FROM all_tables " + + "WHERE owner = '" + schemaName + "'" + + // Normally, user tables aren't in sysaux + " AND tablespace_name NOT IN ('SYSAUX')" + + // Apparently, user tables have global stats off + " AND global_stats = 'NO'" + + // Exclude the tables with names starting like 'DEF$_' + " AND table_name NOT LIKE 'DEF$\\_%' ESCAPE '\\'" + + ") tbl LEFT JOIN all_constraints c ON tbl.owner = c.owner AND tbl.table_name = c.table_name AND constraint_type = 'R'" + ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearData0(Connection connection, String schemaName, Function tablesProvider) { + try (Statement s = connection.createStatement()) { + List cachedTruncateTableSql = cachedTruncateTableSqlPerSchema.get( schemaName ); + List cachedConstraintDisableSql = cachedConstraintDisableSqlPerSchema.get( schemaName ); + List cachedConstraintEnableSql = cachedConstraintEnableSqlPerSchema.get( schemaName ); + if ( cachedTruncateTableSql == null ) { + cachedTruncateTableSql = new ArrayList<>(); + cachedConstraintDisableSql = new ArrayList<>(); + cachedConstraintEnableSql = new ArrayList<>(); + ResultSet rs = tablesProvider.apply( s ); + while ( rs.next() ) { + String tableName = rs.getString( 1 ); + String constraintName = rs.getString( 2 ); + if ( !ignoredTables.contains( tableName ) ) { + cachedTruncateTableSql.add( "TRUNCATE TABLE \"" + tableName + "\"" ); + if ( constraintName != null ) { + cachedConstraintDisableSql.add( "ALTER TABLE \"" + tableName + "\" DISABLE CONSTRAINT " + constraintName ); + cachedConstraintEnableSql.add( "ALTER TABLE \"" + tableName + "\" ENABLE CONSTRAINT " + constraintName ); + } + } + } + cachedTruncateTableSqlPerSchema.put( schemaName, cachedTruncateTableSql ); + cachedConstraintDisableSqlPerSchema.put( schemaName, cachedConstraintDisableSql ); + cachedConstraintEnableSqlPerSchema.put( schemaName, cachedConstraintEnableSql ); + } + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + for ( String sql : cachedConstraintDisableSql ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + for ( String sql : cachedTruncateTableSql ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Deleting data: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + for ( String sql : cachedConstraintEnableSql ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + connection.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + connection.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/PostgreSQLDatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/PostgreSQLDatabaseCleaner.java new file mode 100644 index 0000000000..3d75067737 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/PostgreSQLDatabaseCleaner.java @@ -0,0 +1,200 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public class PostgreSQLDatabaseCleaner implements DatabaseCleaner { + + private static final Logger LOG = Logger.getLogger( PostgreSQLDatabaseCleaner.class.getName() ); + + private final List ignoredTables = new ArrayList<>(); + private final Map truncateSqlPerSchema = new HashMap<>(); + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "PostgreSQL" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName.toLowerCase() ); + } + + @Override + public void clearAllSchemas(Connection connection) { + truncateSqlPerSchema.clear(); + clearSchema0( + connection, + statement -> { + try { + return statement.executeQuery( + "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME <> 'information_schema' AND SCHEMA_NAME NOT LIKE 'pg_%'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearSchema(Connection connection, String schemaName) { + truncateSqlPerSchema.remove( schemaName ); + clearSchema0( + connection, + statement -> { + try { + return statement.executeQuery( + "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '" + schemaName + "'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearSchema0(Connection c, Function schemasProvider) { + try (Statement s = c.createStatement()) { + ResultSet rs; + final List sqls = new ArrayList<>(); + + // Collect schema objects + String user = c.getMetaData().getUserName(); + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = schemasProvider.apply( s ); + while ( rs.next() ) { + String schema = rs.getString( 1 ); + sqls.add( "DROP SCHEMA \"" + schema + "\" CASCADE" ); + sqls.add( "CREATE SCHEMA \"" + schema + "\"" ); + sqls.add( "GRANT ALL ON SCHEMA \"" + schema + "\" TO \"" + user + "\"" ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearAllData(Connection connection) { + clearData0( + connection, + null, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA <> 'information_schema' AND SCHEMA_NAME NOT LIKE 'pg_%'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + clearData0( + connection, + schemaName, + statement -> { + try { + return statement.executeQuery( + "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '" + schemaName + "'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearData0(Connection connection, String schemaName, Function tablesProvider) { + try (Statement s = connection.createStatement()) { + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + String truncateSql = truncateSqlPerSchema.get( schemaName ); + if ( truncateSql == null ) { + StringBuilder sb = new StringBuilder(); + sb.append( "TRUNCATE TABLE " ); + ResultSet rs = tablesProvider.apply( s ); + while ( rs.next() ) { + String tableSchema = rs.getString( 1 ); + String tableName = rs.getString( 2 ); + if ( !ignoredTables.contains( tableName ) ) { + sb.append( '"' ); + sb.append( tableSchema ); + sb.append( '"' ); + sb.append( '.' ); + sb.append( '"' ); + sb.append( tableName ); + sb.append( '"' ); + sb.append( ',' ); + } + } + sb.setCharAt( sb.length() - 1, ' ' ); + sb.append( "RESTART IDENTITY CASCADE" ); + truncateSql = sb.toString(); + truncateSqlPerSchema.put( schemaName, truncateSql ); + } + s.execute( truncateSql ); + LOG.log( Level.FINEST, "Deleting data: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + connection.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + connection.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/SQLServerDatabaseCleaner.java b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/SQLServerDatabaseCleaner.java new file mode 100644 index 0000000000..98ef6ef2d6 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/cleaner/SQLServerDatabaseCleaner.java @@ -0,0 +1,256 @@ +/* + * 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 . + */ +package org.hibernate.testing.cleaner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Christian Beikov + */ +public class SQLServerDatabaseCleaner implements DatabaseCleaner { + + private static final Logger LOG = Logger.getLogger( SQLServerDatabaseCleaner.class.getName() ); + + private final List ignoredTables = new ArrayList<>(); + private final Map> cachedTableNamesPerSchema = new HashMap<>(); + + @Override + public boolean isApplicable(Connection connection) { + try { + return connection.getMetaData().getDatabaseProductName().startsWith( "Microsoft SQL Server" ); + } + catch (SQLException e) { + throw new RuntimeException( "Could not resolve the database metadata!", e ); + } + } + + @Override + public void addIgnoredTable(String tableName) { + ignoredTables.add( tableName ); + } + + @Override + public void clearAllSchemas(Connection c) { + cachedTableNamesPerSchema.clear(); + try (Statement s = c.createStatement()) { + ResultSet rs; + List sqls = new ArrayList<>(); + + // Collect schema objects + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = s.executeQuery( + "SELECT 'ALTER TABLE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] DROP CONSTRAINT [' + CONSTRAINT_NAME + ']' FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE " + + "WHERE EXISTS (SELECT 1 FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME) " + + "AND EXISTS (SELECT 1 FROM sys.Foreign_keys WHERE name = CONSTRAINT_NAME)" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP VIEW [' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'VIEW' " + + "AND EXISTS (SELECT 1 FROM sys.Views t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME)" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP TABLE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' " + + "AND EXISTS (SELECT 1 FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME)" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP SEQUENCE [' + SEQUENCE_SCHEMA + '].[' + SEQUENCE_NAME + ']' FROM INFORMATION_SCHEMA.SEQUENCES" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearSchema(Connection c, String schemaName) { + cachedTableNamesPerSchema.remove( schemaName ); + try (Statement s = c.createStatement()) { + ResultSet rs; + List sqls = new ArrayList<>(); + + // Collect schema objects + LOG.log( Level.FINEST, "Collect schema objects: START" ); + rs = s.executeQuery( + "SELECT 'ALTER TABLE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] DROP CONSTRAINT [' + CONSTRAINT_NAME + ']' FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE " + + "WHERE EXISTS (SELECT 1 FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME) " + + "AND EXISTS (SELECT 1 FROM sys.Foreign_keys WHERE name = CONSTRAINT_NAME) " + + "AND TABLE_SCHEMA = N'" + schemaName + "'" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP VIEW [' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'VIEW' " + + "AND EXISTS (SELECT 1 FROM sys.Views t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME) " + + "AND TABLE_SCHEMA = N'" + schemaName + "'" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP TABLE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' " + + "AND EXISTS (SELECT 1 FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = TABLE_SCHEMA AND t.name = TABLE_NAME) " + + "AND TABLE_SCHEMA = N'" + schemaName + "'" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + + rs = s.executeQuery( + "SELECT 'DROP SEQUENCE [' + SEQUENCE_SCHEMA + '].[' + SEQUENCE_NAME + ']' FROM INFORMATION_SCHEMA.SEQUENCES WHERE " + + "SEQUENCE_SCHEMA = N'" + schemaName + "'" ); + while ( rs.next() ) { + sqls.add( rs.getString( 1 ) ); + } + LOG.log( Level.FINEST, "Collect schema objects: END" ); + + LOG.log( Level.FINEST, "Dropping schema objects: START" ); + for ( String sql : sqls ) { + s.execute( sql ); + } + LOG.log( Level.FINEST, "Dropping schema objects: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + c.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + c.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + + @Override + public void clearAllData(Connection connection) { + clearData0( + connection, + null, + statement -> { + try { + return statement.executeQuery( + "SELECT s.name, t.name FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + @Override + public void clearData(Connection connection, String schemaName) { + clearData0( + connection, + schemaName, + statement -> { + try { + return statement.executeQuery( + "SELECT s.name, t.name FROM sys.Tables t JOIN sys.Schemas s ON t.schema_id = s.schema_id WHERE t.is_ms_shipped = 0 AND s.name = N'" + schemaName + "'" ); + } + catch (SQLException sqlException) { + throw new RuntimeException( sqlException ); + } + } + ); + } + + private void clearData0(Connection connection, String schemaName, Function tablesProvider) { + try (Statement s = connection.createStatement()) { + List cachedTableNames = cachedTableNamesPerSchema.get( schemaName ); + if ( cachedTableNames == null ) { + cachedTableNames = new ArrayList<>(); + ResultSet rs = tablesProvider.apply( s ); + while ( rs.next() ) { + String tableSchema = rs.getString( 1 ); + String tableName = rs.getString( 2 ); + if ( !ignoredTables.contains( tableName ) ) { + cachedTableNames.add( tableSchema + "." + tableName ); + } + } + cachedTableNamesPerSchema.put( schemaName, cachedTableNames ); + } + // Disable foreign keys + LOG.log( Level.FINEST, "Disable foreign keys: START" ); + for ( String table : cachedTableNames ) { + s.execute( "ALTER TABLE " + table + " NOCHECK CONSTRAINT ALL" ); + } + LOG.log( Level.FINEST, "Disable foreign keys: END" ); + + // Delete data + LOG.log( Level.FINEST, "Deleting data: START" ); + for ( String table : cachedTableNames ) { + s.execute( "DELETE FROM " + table ); + } + LOG.log( Level.FINEST, "Deleting data: END" ); + + // Enable foreign keys + LOG.log( Level.FINEST, "Enabling foreign keys: START" ); + for ( String table : cachedTableNames ) { + s.execute( "ALTER TABLE " + table + " WITH CHECK CHECK CONSTRAINT ALL" ); + } + LOG.log( Level.FINEST, "Enabling foreign keys: END" ); + + LOG.log( Level.FINEST, "Committing: START" ); + connection.commit(); + LOG.log( Level.FINEST, "Committing: END" ); + } + catch (SQLException e) { + try { + connection.rollback(); + } + catch (SQLException e1) { + e.addSuppressed( e1 ); + } + + throw new RuntimeException( e ); + } + } + +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java index 573f8516be..c301c66688 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java @@ -34,6 +34,7 @@ public class ConnectionProviderDelegate implements private ServiceRegistryImplementor serviceRegistry; private ConnectionProvider connectionProvider; + private boolean configured; public ConnectionProviderDelegate() { } @@ -42,6 +43,14 @@ public class ConnectionProviderDelegate implements this.connectionProvider = connectionProvider; } + public ConnectionProvider getConnectionProvider() { + return connectionProvider; + } + + public void setConnectionProvider(ConnectionProvider connectionProvider) { + this.connectionProvider = connectionProvider; + } + @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { this.serviceRegistry = serviceRegistry; @@ -49,18 +58,21 @@ public class ConnectionProviderDelegate implements @Override public void configure(Map configurationValues) { - if ( connectionProvider == null ) { - @SuppressWarnings("unchecked") - Map settings = new HashMap<>( configurationValues ); - settings.remove( AvailableSettings.CONNECTION_PROVIDER ); - connectionProvider = ConnectionProviderInitiator.INSTANCE.initiateService( - settings, - serviceRegistry - ); + if ( !configured ) { + if ( connectionProvider == null ) { + @SuppressWarnings("unchecked") + Map settings = new HashMap<>( configurationValues ); + settings.remove( AvailableSettings.CONNECTION_PROVIDER ); + connectionProvider = ConnectionProviderInitiator.INSTANCE.initiateService( + settings, + serviceRegistry + ); + } if ( connectionProvider instanceof Configurable ) { Configurable configurableConnectionProvider = (Configurable) connectionProvider; - configurableConnectionProvider.configure( settings ); + configurableConnectionProvider.configure( configurationValues ); } + configured = true; } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java new file mode 100644 index 0000000000..28ba052668 --- /dev/null +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/SharedDriverManagerConnectionProviderImpl.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +package org.hibernate.testing.jdbc; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; +import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl; +import org.hibernate.internal.util.config.ConfigurationHelper; + +/** + * A special connection provider that is shared across test runs for better performance. + * + * @author Christian Beikov + */ +public class SharedDriverManagerConnectionProviderImpl extends DriverManagerConnectionProviderImpl { + + private static final SharedDriverManagerConnectionProviderImpl INSTANCE = new SharedDriverManagerConnectionProviderImpl(); + + public static SharedDriverManagerConnectionProviderImpl getInstance() { + return INSTANCE; + } + + private Config config; + + @Override + public void configure(Map configurationValues) { + final Config c = new Config( configurationValues ); + if ( !c.isCompatible( config ) ) { + if ( config != null ) { + super.stop(); + } + super.configure( configurationValues ); + config = c; + } + } + + @Override + public boolean isValid(Connection connection) throws SQLException { + // Wait at most 5 seconds to validate a connection is still valid + return connection.isValid( 5 ); + } + + @Override + public void stop() { + // No need to stop as this is a shared instance + validateConnectionsReturned(); + } + + public void reset() { + super.stop(); + config = null; + } + + private static class Config { + private final boolean autoCommit; + private final int minSize; + private final int maxSize; + private final int initialSize; + private final String driverClassName; + private final String url; + private final Properties connectionProps; + private final Integer isolation; + + public Config(Map configurationValues) { + this.autoCommit = ConfigurationHelper.getBoolean( AvailableSettings.AUTOCOMMIT, configurationValues, false ); + this.minSize = ConfigurationHelper.getInt( MIN_SIZE, configurationValues, 2 ); + this.maxSize = ConfigurationHelper.getInt( AvailableSettings.POOL_SIZE, configurationValues, 20 ); + this.initialSize = ConfigurationHelper.getInt( INITIAL_SIZE, configurationValues, minSize ); + this.driverClassName = (String) configurationValues.get( AvailableSettings.DRIVER ); + this.url = (String) configurationValues.get( AvailableSettings.URL ); + this.connectionProps = ConnectionProviderInitiator.getConnectionProperties( configurationValues ); + this.isolation = ConnectionProviderInitiator.extractIsolation( configurationValues ); + } + + boolean isCompatible(Config config) { + return config != null && autoCommit == config.autoCommit && minSize == config.minSize + && maxSize == config.maxSize && initialSize == config.initialSize + && driverClassName.equals( config.driverClassName ) + && url.equals( config.url ) + && connectionProps.equals( config.connectionProps ) + && Objects.equals( isolation, config.isolation ); + } + + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java index 2c6235b78d..7f5810768f 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jta/JtaAwareConnectionProviderImpl.java @@ -93,9 +93,14 @@ public class JtaAwareConnectionProviderImpl implements ConnectionProvider, Confi if ( connection == null ) { connection = delegate.getConnection(); TestingJtaPlatformImpl.synchronizationRegistry().putResource( CONNECTION_KEY, connection ); - - XAResourceWrapper xaResourceWrapper = new XAResourceWrapper( this, connection ); - currentTransaction.enlistResource( xaResourceWrapper ); + try { + XAResourceWrapper xaResourceWrapper = new XAResourceWrapper( this, connection ); + currentTransaction.enlistResource( xaResourceWrapper ); + } + catch (Exception e) { + delist( connection ); + throw e; + } } return connection; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index 8d71e90ead..5c72ace5a2 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -41,6 +41,8 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.jdbc.AbstractReturningWork; import org.hibernate.jdbc.Work; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.testing.AfterClassOnce; @@ -49,6 +51,7 @@ import org.hibernate.testing.OnExpectedFailure; import org.hibernate.testing.OnFailure; import org.hibernate.testing.SkipLog; import org.hibernate.testing.cache.CachingRegionFactory; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.transaction.TransactionUtil2; import org.junit.After; import org.junit.Before; @@ -177,6 +180,14 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { } configuration.setImplicitNamingStrategy( ImplicitNamingStrategyLegacyJpaImpl.INSTANCE ); configuration.setProperty( Environment.DIALECT, getDialect().getClass().getName() ); + if ( !Environment.getProperties().containsKey( Environment.CONNECTION_PROVIDER ) ) { + configuration.getProperties().put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + configuration.getProperties().put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + configuration.getProperties().put( + AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } return configuration; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseNonConfigCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseNonConfigCoreFunctionalTestCase.java index 7473d8b60f..f70aea9a8b 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseNonConfigCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseNonConfigCoreFunctionalTestCase.java @@ -33,6 +33,7 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; @@ -48,6 +49,8 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.RootClass; import org.hibernate.mapping.SimpleValue; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.type.BlobType; import org.hibernate.type.ClobType; @@ -58,6 +61,7 @@ import org.hibernate.testing.BeforeClassOnce; import org.hibernate.testing.OnExpectedFailure; import org.hibernate.testing.OnFailure; import org.hibernate.testing.cache.CachingRegionFactory; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.transaction.TransactionUtil2; import org.junit.After; import org.junit.Before; @@ -175,6 +179,14 @@ public class BaseNonConfigCoreFunctionalTestCase extends BaseUnitTestCase { afterBootstrapServiceRegistryBuilt( bsr ); final Map settings = new HashMap(); + if ( !Environment.getProperties().containsKey( Environment.CONNECTION_PROVIDER ) ) { + settings.put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + settings.put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + settings.put( + AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } addSettings( settings ); final StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder( bsr ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java index 690f526b42..fe9ed06f9a 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseUnitTestCase.java @@ -16,6 +16,7 @@ import javax.transaction.SystemException; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.testing.AfterClassOnce; +import org.hibernate.testing.cleaner.DatabaseCleaner; import org.hibernate.testing.jdbc.leak.ConnectionLeakUtil; import org.hibernate.testing.jta.TestingJtaPlatformImpl; import org.junit.After; @@ -34,6 +35,10 @@ import org.jboss.logging.Logger; @RunWith( CustomRunner.class ) public abstract class BaseUnitTestCase { + static { + DatabaseCleaner.clearSchemas(); + } + protected final Logger log = Logger.getLogger( getClass() ); private static boolean enableConnectionLeakDetection = Boolean.TRUE.toString() diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit5/EntityManagerFactoryBasedFunctionalTest.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit5/EntityManagerFactoryBasedFunctionalTest.java index be11534342..6c5fef1d83 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit5/EntityManagerFactoryBasedFunctionalTest.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit5/EntityManagerFactoryBasedFunctionalTest.java @@ -27,7 +27,10 @@ import org.hibernate.cfg.Environment; import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.junit.jupiter.api.AfterEach; import org.jboss.logging.Logger; @@ -138,6 +141,14 @@ public class EntityManagerFactoryBasedFunctionalTest config.put( org.hibernate.jpa.AvailableSettings.XML_FILE_NAMES, dds ); } + config.put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + config.put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + if ( !config.containsKey( Environment.CONNECTION_PROVIDER ) ) { + config.put( + AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } addConfigOptions( config ); return config; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/EntityManagerFactoryExtension.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/EntityManagerFactoryExtension.java index 99c9ca3342..b84bf71b83 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/EntityManagerFactoryExtension.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/EntityManagerFactoryExtension.java @@ -21,17 +21,21 @@ import org.hibernate.Transaction; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; +import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.tool.schema.Action; import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator; import org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.ActionGrouping; +import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.orm.domain.DomainModelDescriptor; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.jpa.PersistenceUnitInfoImpl; @@ -138,7 +142,15 @@ public class EntityManagerFactoryExtension } } - final Map integrationSettings = new HashMap<>(); + final Map integrationSettings = new HashMap<>(); + integrationSettings.put( GlobalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + integrationSettings.put( LocalTemporaryTableStrategy.DROP_ID_TABLES, "true" ); + if ( !integrationSettings.containsKey( Environment.CONNECTION_PROVIDER ) ) { + integrationSettings.put( + AvailableSettings.CONNECTION_PROVIDER, + SharedDriverManagerConnectionProviderImpl.getInstance() + ); + } for ( int i = 0; i < emfAnn.integrationSettings().length; i++ ) { final Setting setting = emfAnn.integrationSettings()[ i ]; integrationSettings.put( setting.name(), setting.value() ); @@ -254,14 +266,14 @@ public class EntityManagerFactoryExtension private static class EntityManagerFactoryScopeImpl implements EntityManagerFactoryScope, ExtensionContext.Store.CloseableResource { private final PersistenceUnitInfo persistenceUnitInfo; - private final Map integrationSettings; + private final Map integrationSettings; private javax.persistence.EntityManagerFactory emf; private boolean active = true; private EntityManagerFactoryScopeImpl( PersistenceUnitInfo persistenceUnitInfo, - Map integrationSettings) { + Map integrationSettings) { this.persistenceUnitInfo = persistenceUnitInfo; this.integrationSettings = integrationSettings; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java index 541640390b..0c0886c683 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/transaction/TransactionUtil.java @@ -33,7 +33,9 @@ import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SybaseASE15Dialect; +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.service.ServiceRegistry; import org.junit.Assert; @@ -155,6 +157,24 @@ public class TransactionUtil { } } + /** + * JDBC transaction function + * + * @param function result + */ + @FunctionalInterface + public interface JDBCTransactionFunction { + T accept(Connection connection) throws SQLException; + } + + /** + * JDBC transaction function without return value + */ + @FunctionalInterface + public interface JDBCTransactionVoidFunction { + void accept(Connection connection) throws SQLException; + } + /** * Execute function in a JPA transaction * @@ -692,4 +712,40 @@ public class TransactionUtil { public static void doInAutoCommit(String... statements) { doInAutoCommit( null, statements ); } + + public static void doWithJDBC(ServiceRegistry serviceRegistry, JDBCTransactionVoidFunction function) throws SQLException { + final JdbcConnectionAccess connectionAccess = serviceRegistry.getService( JdbcServices.class ) + .getBootstrapJdbcConnectionAccess(); + Connection connection = connectionAccess.obtainConnection(); + try { + function.accept( connection ); + } + finally { + if ( connection != null ) { + try { + connectionAccess.releaseConnection( connection ); + } + catch (SQLException ignore) { + } + } + } + } + + public static T doWithJDBC(ServiceRegistry serviceRegistry, JDBCTransactionFunction function) throws SQLException { + final JdbcConnectionAccess connectionAccess = serviceRegistry.getService( JdbcServices.class ) + .getBootstrapJdbcConnectionAccess(); + Connection connection = connectionAccess.obtainConnection(); + try { + return function.accept( connection ); + } + finally { + if ( connection != null ) { + try { + connectionAccess.releaseConnection( connection ); + } + catch (SQLException ignore) { + } + } + } + } }