package org.jstryker.database; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Collection; import javax.activation.DataSource; import javax.persistence.Column; import javax.persistence.Table; import javax.persistence.Transient; import org.dbunit.DatabaseUnitException; import org.dbunit.database.DatabaseConnection; import org.dbunit.database.IDatabaseConnection; import org.dbunit.dataset.DataSetException; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.ReplacementDataSet; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.dbunit.operation.DatabaseOperation; import org.dbunit.operation.TransactionOperation; import org.jstryker.exception.JStrykerException; /** * Tool for DBUnit. */ public class DBUnitHelper { /** * Reset the database to dataset content performing a {@link TransactionOperation#CLEAN_INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void cleanInsert(String resourcePath, Connection connection) { execute(resourcePath, connection, TransactionOperation.CLEAN_INSERT); } /** * Reset the database to dataset content performing a {@link TransactionOperation#CLEAN_INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void cleanInsert(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { new DBUnitHelper().cleanInsert(resourcePath, connection); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } public void cleanInsert(Object object, Connection connection) { insertDatabaseOperation(object, connection, TransactionOperation.CLEAN_INSERT); } public void cleanInsert(Object object) { Connection connection = ConnectionHelper.getConnection(); try { insertDatabaseOperation(object, connection, TransactionOperation.CLEAN_INSERT); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } private void insertDatabaseOperation(Object object, Connection connection, DatabaseOperation databaseOperation) { try { if (!object.getClass().isAnnotationPresent(Table.class)) { throw new JStrykerException("Object("+object+") isn't Entity"); } Table table = object.getClass().getAnnotation(Table.class); StringBuilder builder = new StringBuilder("<?xml version=\"1.0\"?>\n<dataset>\n<"); builder.append(table.name()).append(" "); Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); Object value = field.get(object); if (value == null) { continue; } if (field.isAnnotationPresent(Transient.class)) { continue; } Column annotation = field.getAnnotation(Column.class); if (annotation != null && !"".equals(annotation.name())) { builder.append(annotation.name()); } else { builder.append(field.getName()); } builder.append("=\""); builder.append(value); builder.append("\" "); } builder.append("/>\n</dataset>"); String string = builder.toString(); execute(null, connection, new ByteArrayInputStream(string.getBytes()), databaseOperation); } catch (IllegalAccessException e) { throw new JStrykerException(e.getMessage(), e); } } /** * Reset the database to dataset content performing a {@link TransactionOperation#TRUNCATE_TABLE} and a * {@link TransactionOperation#INSERT} from DBUnit. * @param object Entity Object. */ public void truncateAndInsert(Object object) { Connection connection = ConnectionHelper.getConnection(); try { insertDatabaseOperation(object, connection, TransactionOperation.TRUNCATE_TABLE); insertDatabaseOperation(object, connection, TransactionOperation.INSERT); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Reset the database to dataset content performing a {@link TransactionOperation#TRUNCATE_TABLE} and a * {@link TransactionOperation#INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void truncateAndInsert(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { execute(resourcePath, connection, TransactionOperation.TRUNCATE_TABLE); execute(resourcePath, connection, TransactionOperation.INSERT); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Reset the database to dataset content performing a {@link TransactionOperation#TRUNCATE_TABLE} and a * {@link TransactionOperation#INSERT} from DBUnit. * @param connection {@link Connection}. * @param objects Entity Object. */ public void truncateAndInsert(Connection connection, Collection<?> coll) { for (Object object : coll) { insertDatabaseOperation(object, connection, TransactionOperation.TRUNCATE_TABLE); insertDatabaseOperation(object, connection, TransactionOperation.INSERT); } } /** * Reset the database to dataset content performing a {@link TransactionOperation#TRUNCATE_TABLE} and a * {@link TransactionOperation#INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void truncateAndInsert(String resourcePath, Connection connection) { execute(resourcePath, connection, TransactionOperation.TRUNCATE_TABLE); execute(resourcePath, connection, TransactionOperation.INSERT); } /** * Insert dataset content into database performing a {@link TransactionOperation#INSERT} from DBUnit. * @param object Entity Object. */ public void insert(Object object) { Connection connection = ConnectionHelper.getConnection(); try { insertDatabaseOperation(object, connection, DatabaseOperation.INSERT); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Insert dataset content into database performing a {@link TransactionOperation#INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void insert(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { execute(resourcePath, connection, DatabaseOperation.INSERT); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Insert dataset content into database performing a {@link TransactionOperation#INSERT} from DBUnit. * @param object Entity Object. * @param connection {@link Connection}. */ public void insert(Object object, Connection connection) { insertDatabaseOperation(object, connection, DatabaseOperation.INSERT); } /** * Insert dataset content into database performing a {@link TransactionOperation#INSERT} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void insert(String resourcePath, Connection connection) { execute(resourcePath, connection, DatabaseOperation.INSERT); } /** * Delete dataset specified rows from database with a {@link TransactionOperation#DELETE} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void delete(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { delete(resourcePath, connection); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Delete dataset specified rows from database with a {@link TransactionOperation#DELETE} from DBUnit. * @param object Entity Object. */ public void delete(Object object) { Connection connection = ConnectionHelper.getConnection(); try { insertDatabaseOperation(object, connection, DatabaseOperation.DELETE); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Delete dataset specified rows from database with a {@link TransactionOperation#DELETE} from DBUnit. * @param object Entity Object * @param connection {@link Connection}. */ public void delete(Object object, Connection connection) { insertDatabaseOperation(object, connection, DatabaseOperation.DELETE); } /** * Delete dataset specified rows from database with a {@link TransactionOperation#DELETE} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void delete(String resourcePath, Connection connection) { execute(resourcePath, connection, DatabaseOperation.DELETE); } /** * Clean the database with a {@link TransactionOperation#DELETE_ALL} from DBUnit. * @param coll List<Entity Object>. */ public void deleteAll(Collection<?> coll) { Connection connection = ConnectionHelper.getConnection(); try { for (Object object : coll) { insertDatabaseOperation(object, connection, DatabaseOperation.DELETE_ALL); } } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Clean the database with a {@link TransactionOperation#DELETE_ALL} from DBUnit. * @param connection {@link Connection}. * @param coll List<Entity Object>. */ public void deleteAll(Connection connection, Collection<?> coll) { for (Object object : coll) { insertDatabaseOperation(object, connection, DatabaseOperation.DELETE_ALL); } } /** * Clean the database with a {@link TransactionOperation#DELETE_ALL} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void deleteAll(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { deleteAll(resourcePath, connection); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Clean the database with a {@link TransactionOperation#DELETE_ALL} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void deleteAll(String resourcePath, Connection connection) { execute(resourcePath, connection, DatabaseOperation.DELETE_ALL); } /** * Clean the database with a {@link TransactionOperation#TRUNCATE_TABLE} from DBUnit. * @param object Entity Object. */ public void truncate(Object object) { Connection connection = ConnectionHelper.getConnection(); try { insertDatabaseOperation(object, connection, DatabaseOperation.TRUNCATE_TABLE); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Clean the database with a {@link TransactionOperation#TRUNCATE_TABLE} from DBUnit. * @param resourcePath Path for dbunit dataset. */ public void truncate(String resourcePath) { Connection connection = ConnectionHelper.getConnection(); try { truncate(resourcePath, connection); } finally { try { connection.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } } /** * Clean the database with a {@link TransactionOperation#TRUNCATE_TABLE} from DBUnit. * @param object Entity Object * @param connection {@link Connection}. */ public void truncate(Object object, Connection connection) { insertDatabaseOperation(object, connection, DatabaseOperation.TRUNCATE_TABLE); } /** * Clean the database with a {@link TransactionOperation#TRUNCATE_TABLE} from DBUnit. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. */ public void truncate(String resourcePath, Connection connection) { execute(resourcePath, connection, DatabaseOperation.TRUNCATE_TABLE); } /** * Generate a DBUnit dataSet file from {@link DataSource}. * @param path Place where dataset will be created. If path does not exist, it will be created. * @param connection {@link Connection} to {@link DataSource}. */ // TODO: TEST NOT COVERED EXCEPTIONS. public static void generateDataSet(String path, Connection connection) { try { IDatabaseConnection dbUnitConnection = new DatabaseConnection(connection); IDataSet dataSet = dbUnitConnection.createDataSet(); File file = new File(path); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } FlatXmlDataSet.write(dataSet, new FileOutputStream(file)); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } catch (DataSetException e) { throw new JStrykerException(e.getMessage(), e); } catch (FileNotFoundException e) { throw new JStrykerException(e.getMessage(), e); } catch (IOException e) { throw new JStrykerException(e.getMessage(), e); } catch (DatabaseUnitException e) { throw new JStrykerException(e.getMessage(), e); } } void execute(String resourcePath, Connection connection, InputStream inputStream, DatabaseOperation... operations) { executeOperation(resourcePath, connection, inputStream, operations); } void execute(String resourcePath, Connection connection, DatabaseOperation... operations) { executeOperation(resourcePath, connection, null, operations); } /** * Execute dbunit operations in datasource. * @param resourcePath Path for dbunit dataset. * @param connection {@link Connection}. * @param operations {@link DatabaseOperation} to be executed. */ void executeOperation(String resourcePath, Connection connection, InputStream inputStream, DatabaseOperation... operations) { try { InputStream resourceAsStream = (inputStream != null ? inputStream : DBUnitHelper.class.getResourceAsStream(resourcePath)); FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder(); builder.setCaseSensitiveTableNames(true); IDataSet dataSet = builder.build(resourceAsStream); ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet); replacementDataSet.addReplacementObject("[null]", null); IDatabaseConnection iConnection = new DatabaseConnection(connection); for(DatabaseOperation operation : operations) { operation.execute(iConnection, replacementDataSet); } resourceAsStream.close(); } catch (DatabaseUnitException e) { throw new JStrykerException(e.getMessage(), e); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } catch (IOException e) { throw new JStrykerException(e.getMessage(), e); } } /** * Disable MySQL foreign key checks on this connection.<br> * For certain cases, you must disable foreign key checks before truncate or delete all table data. Remember that * you must use this connection to perform the delete all or the truncate. * @param connection {@link Connection}. * @throws JStrykerException If any error occurs during disable. * @see #enableMysqlForeignKeyChecks(java.sql.Connection) * @see #cleanInsert(String, java.sql.Connection) * @see #truncate(String, java.sql.Connection) * @see #truncateAndInsert(String, java.sql.Connection) */ public void disableMysqlForeignKeyChecks(Connection connection) throws JStrykerException { setMysqlForeignKeyChecks(connection, 0); } /** * Enable MySQL foreign key checks on this connection.<br> * @param connection {@link Connection}. * @throws JStrykerException If any error occurs during enable. * @see #disableMysqlForeignKeyChecks(java.sql.Connection) */ public void enableMysqlForeignKeyChecks(Connection connection) throws JStrykerException { setMysqlForeignKeyChecks(connection, 1); } private void setMysqlForeignKeyChecks(Connection connection, int value) throws JStrykerException { try { Statement statement = connection.createStatement(); statement.execute("SET @@foreign_key_checks = " + value); statement.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } /** * Disable HSQLDB database referential integrity on this connection.<br> * For certain cases, you must disable foreign key checks before truncate or delete all table data. Remember that * you must use this connection to perform the delete all or the truncate. * @param connection {@link Connection}. * @throws JStrykerException If any error occurs during disable. * @see #enableHsqldbDatabaseReferentialIntegrity(java.sql.Connection) * @see #cleanInsert(String, java.sql.Connection) * @see #truncate(String, java.sql.Connection) * @see #truncateAndInsert(String, java.sql.Connection) */ public void disableHsqldbDatabaseReferentialIntegrity(Connection connection) throws JStrykerException { setHsqldbDatabaseReferentialIntegrity(connection, "FALSE"); } /** * Enable HSQLDB database referential integrity on this connection.<br> * @param connection {@link Connection}. * @throws JStrykerException If any error occurs during enable. * @see #disableHsqldbDatabaseReferentialIntegrity(java.sql.Connection) */ public void enableHsqldbDatabaseReferentialIntegrity(Connection connection) throws JStrykerException { setHsqldbDatabaseReferentialIntegrity(connection, "TRUE"); } private void setHsqldbDatabaseReferentialIntegrity(Connection connection, String value) { try { Statement statement = connection.createStatement(); statement.execute("SET DATABASE REFERENTIAL INTEGRITY " + value); statement.close(); } catch (SQLException e) { throw new JStrykerException(e.getMessage(), e); } } }