Add cleaners for clearing a schema the first time before running a test
This commit is contained in:
parent
d767d46d05
commit
08d9fe1a3b
|
@ -9,6 +9,8 @@ package org.hibernate.envers.test;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.testing.cleaner.DatabaseCleaner;
|
||||||
|
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
@ -22,6 +24,10 @@ import org.jboss.logging.Logger;
|
||||||
@RunWith(EnversRunner.class)
|
@RunWith(EnversRunner.class)
|
||||||
public abstract class AbstractEnversTest {
|
public abstract class AbstractEnversTest {
|
||||||
|
|
||||||
|
static {
|
||||||
|
DatabaseCleaner.clearSchemas();
|
||||||
|
}
|
||||||
|
|
||||||
protected final Logger log = Logger.getLogger( getClass() );
|
protected final Logger log = Logger.getLogger( getClass() );
|
||||||
|
|
||||||
private String auditStrategy;
|
private String auditStrategy;
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, List<String>> 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<Statement, ResultSet> 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<String> 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);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, Map<String, List<String>>> 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<String> 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<String> 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<String, List<String>> 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<String, List<String>> cachedForeignKeys = cachedForeignKeysPerSchema.get( schemaName );
|
||||||
|
if ( cachedForeignKeys == null ) {
|
||||||
|
cachedForeignKeys = collectForeignKeys( connection, schemaName.toUpperCase() );
|
||||||
|
cachedForeignKeysPerSchema.put( schemaName, cachedForeignKeys );
|
||||||
|
}
|
||||||
|
deleteAllData( connection, cachedForeignKeys );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<String>> 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<String, List<String>> foreignKeys = new HashMap<>();
|
||||||
|
while ( rs.next() ) {
|
||||||
|
foreignKeys.put( rs.getString( 1 ), new ArrayList<String>() );
|
||||||
|
}
|
||||||
|
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<String, List<String>> 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<String, List<String>> foreignKeys = new HashMap<>();
|
||||||
|
while ( rs.next() ) {
|
||||||
|
foreignKeys.put( rs.getString( 1 ), new ArrayList<String>() );
|
||||||
|
}
|
||||||
|
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<String, List<String>> foreignKeys) {
|
||||||
|
try (Statement s = c.createStatement()) {
|
||||||
|
// Disable foreign keys
|
||||||
|
LOG.log( Level.FINEST, "Disable foreign keys: START" );
|
||||||
|
for ( Map.Entry<String, List<String>> 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<String, List<String>> 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, List<String>> 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<Statement, ResultSet> 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<String> 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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> R workReturning(ConnectionFunction<R> 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> {
|
||||||
|
R apply(Connection c) throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JdbcConnectionContext() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, List<String>> cachedTruncateTableSqlPerSchema = new HashMap<>();
|
||||||
|
private final Map<String, List<String>> cachedConstraintDisableSqlPerSchema = new HashMap<>();
|
||||||
|
private final Map<String, List<String>> 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<Statement, ResultSet> sqlProvider) {
|
||||||
|
try (Statement s = c.createStatement()) {
|
||||||
|
ResultSet rs;
|
||||||
|
List<String> 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<Statement, ResultSet> tablesProvider) {
|
||||||
|
try (Statement s = connection.createStatement()) {
|
||||||
|
List<String> cachedTruncateTableSql = cachedTruncateTableSqlPerSchema.get( schemaName );
|
||||||
|
List<String> cachedConstraintDisableSql = cachedConstraintDisableSqlPerSchema.get( schemaName );
|
||||||
|
List<String> 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, String> 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<Statement, ResultSet> schemasProvider) {
|
||||||
|
try (Statement s = c.createStatement()) {
|
||||||
|
ResultSet rs;
|
||||||
|
final List<String> 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<Statement, ResultSet> 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
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<String> ignoredTables = new ArrayList<>();
|
||||||
|
private final Map<String, List<String>> 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<String> 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<String> 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<Statement, ResultSet> tablesProvider) {
|
||||||
|
try (Statement s = connection.createStatement()) {
|
||||||
|
List<String> 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import javax.transaction.SystemException;
|
||||||
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
||||||
|
|
||||||
import org.hibernate.testing.AfterClassOnce;
|
import org.hibernate.testing.AfterClassOnce;
|
||||||
|
import org.hibernate.testing.cleaner.DatabaseCleaner;
|
||||||
import org.hibernate.testing.jdbc.leak.ConnectionLeakUtil;
|
import org.hibernate.testing.jdbc.leak.ConnectionLeakUtil;
|
||||||
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
|
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -34,6 +35,10 @@ import org.jboss.logging.Logger;
|
||||||
@RunWith( CustomRunner.class )
|
@RunWith( CustomRunner.class )
|
||||||
public abstract class BaseUnitTestCase {
|
public abstract class BaseUnitTestCase {
|
||||||
|
|
||||||
|
static {
|
||||||
|
DatabaseCleaner.clearSchemas();
|
||||||
|
}
|
||||||
|
|
||||||
protected final Logger log = Logger.getLogger( getClass() );
|
protected final Logger log = Logger.getLogger( getClass() );
|
||||||
|
|
||||||
private static boolean enableConnectionLeakDetection = Boolean.TRUE.toString()
|
private static boolean enableConnectionLeakDetection = Boolean.TRUE.toString()
|
||||||
|
|
Loading…
Reference in New Issue