Author: tchemit Date: 2013-02-14 09:42:09 +0100 (Thu, 14 Feb 2013) New Revision: 411 Url: http://forge.codelutin.com/projects/tutti/repository/revisions/411 Log: refs #1874: [IMP/EXP] - Synchronisation de r?\195?\169f?\195?\169rentiel (am?\195?\169lioration des logs+ ajout requete pour compter ce qu'il faut mettre ?\195?\160 jour) Added: trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplUpTodateTest.java Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelper.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeResult.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiAssociationTableMetadata.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiEntityTableMetadata.java trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiTableMetadata.java trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/DatabaseResource.java trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelperTest.java trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplTest.java Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelper.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelper.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelper.java 2013-02-14 08:42:09 UTC (rev 411) @@ -28,6 +28,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import fr.ifremer.tutti.persistence.entities.TuttiEntities; +import org.apache.commons.lang.time.DateUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.dialect.Dialect; @@ -40,6 +41,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.Date; import java.util.List; import java.util.Properties; @@ -94,25 +96,25 @@ TuttiEntities.getUrl(localProperties), TuttiEntities.getUrl(remoteProperties)); - Connection targetConnection = null; - Connection synchConnection = null; + Connection localConnection = null; + Connection remoteConnection = null; try { // create local connection - targetConnection = TuttiEntities.createConnection(localProperties); + localConnection = TuttiEntities.createConnection(localProperties); // create remote Connection - synchConnection = TuttiEntities.createConnection(remoteProperties); + remoteConnection = TuttiEntities.createConnection(remoteProperties); // load metas - TuttiDatabaseMetadata targetMeta = - loadDatabaseMetadata(targetConnection, dialect); - TuttiDatabaseMetadata synchMeta = - loadDatabaseMetadata(synchConnection, dialect); + TuttiDatabaseMetadata localMeta = + loadDatabaseMetadata(localConnection, dialect); + TuttiDatabaseMetadata remoteMeta = + loadDatabaseMetadata(remoteConnection, dialect); // check schema try { - checkSchemas(targetMeta, synchMeta); + checkSchemas(localMeta, remoteMeta); } catch (DataRetrievalFailureException e) { result.setError(e); } @@ -120,7 +122,7 @@ if (result.isSuccess()) { // prepare target (desactivate constraints) - prepareSynch(targetConnection); + prepareSynch(localConnection); try { @@ -128,26 +130,26 @@ String tableName = tuttiTable.name(); - TuttiTableMetadata table = synchMeta.getTable(tableName); + TuttiTableMetadata table = remoteMeta.getTable(tableName); if (log.isInfoEnabled()) { log.info("Synchronize table: " + tableName); } synchronizeTable(table, - targetConnection, - synchConnection, + localConnection, + remoteConnection, result); } } finally { - releaseSynch(synchConnection); + releaseSynch(localConnection); } - targetConnection.commit(); + localConnection.commit(); } } catch (SQLException e) { try { - if (targetConnection != null) { - targetConnection.rollback(); + if (localConnection != null) { + localConnection.rollback(); } } catch (SQLException e1) { @@ -155,8 +157,8 @@ } result.setError(e); } finally { - JdbcUtils.closeConnection(synchConnection); - JdbcUtils.closeConnection(targetConnection); + JdbcUtils.closeConnection(remoteConnection); + JdbcUtils.closeConnection(localConnection); } return result; } @@ -241,7 +243,7 @@ try { ResultSet resultSet = statement.executeQuery(); if (resultSet.next()) { - result = resultSet.getDate(1); + result = resultSet.getTimestamp(1); } statement.close(); } finally { @@ -263,7 +265,7 @@ if (table.useUpdateDateColumn() && table.isWithUpdateDateColumn() && fromDate != null) { - statement.setDate(1, new java.sql.Date(fromDate.getTime())); + statement.setTimestamp(1, new Timestamp(fromDate.getTime())); } statement.setFetchSize(1000); @@ -271,6 +273,27 @@ return result; } + public long getCountDataToUpdate(Connection connection, + TuttiTableMetadata table, + Date fromDate) throws SQLException { + + String sql = fromDate == null ? + table.getCountDataToUpdateQueryWithNull() : + table.getCountDataToUpdateQuery(); + + PreparedStatement statement = connection.prepareStatement(sql); + if (table.useUpdateDateColumn() && + table.isWithUpdateDateColumn() && + fromDate != null) { + statement.setTimestamp(1, new Timestamp(fromDate.getTime())); + } + + ResultSet queryResult = statement.executeQuery(); + queryResult.next(); + long result = queryResult.getLong(1); + return result; + } + public Set<List<String>> getExistingPrimaryKeys(Connection connection, TuttiTableMetadata table) throws SQLException { @@ -347,7 +370,8 @@ int countR = 0; int insertCount = 0; int updateCount = 0; - do { + int nbRowsToTreat = result.getNbRows(tableName); + while (incomingData.next()) { List<String> pk = getPk(incomingData, pkIndex); @@ -403,16 +427,16 @@ } if (countR % 10000 == 0) { if (log.isInfoEnabled()) { - log.info("Rows Done: " + countR); + log.info(String.format("[%s-%s] Rows Done: %s (inserts: %s, updates: %s)", tableName, nbRowsToTreat, countR, insertCount, updateCount)); } } } - while (incomingData.next()); + result.addInserts(tableName, insertCount); result.addUpdates(tableName, updateCount); if (log.isInfoEnabled()) { - log.info("Rows Done: " + countR); + log.info(String.format("[%s-%s] Rows Done: %s (inserts: %s, updates: %s)", tableName, nbRowsToTreat, countR, insertCount, updateCount)); } if (insertCount > 0 && insertCount % 1000 != 0) { @@ -436,26 +460,37 @@ // get last updateDate used by local db Date updateDate = getLastUpdateDate(localConnection, table); - if (log.isInfoEnabled()) { - log.info("[" + table.getName() + "] updateDate: " + updateDate); + if (updateDate != null) { + + // just inscrements of 1 milisecond to not having same + updateDate = new Timestamp(DateUtils.setMilliseconds(updateDate, 0).getTime()); + updateDate = new Timestamp(DateUtils.addSeconds(updateDate, 1).getTime()); } + String tableName = table.getName(); - // get data to synch - ResultSet dataToUpdate = getDataToUpdate(remoteConnection, - table, - updateDate); + long countToUpdate = + getCountDataToUpdate(remoteConnection, table, updateDate); if (log.isInfoEnabled()) { - log.info("[" + table.getName() + "] dataToUpdate: " + dataToUpdate); + log.info(String.format("[%s] count to update: %s", tableName, countToUpdate)); } - try { - if (dataToUpdate.next()) { + result.addRows(tableName, (int) countToUpdate); + + if (countToUpdate > 0) { + + // there is some row to update + + // get data to synch + ResultSet dataToUpdate = getDataToUpdate(remoteConnection, + table, + updateDate); + + try { + // there is some data to update - String tableName = table.getName(); - TuttiTable tuttiTable = TuttiTable.valueOf(tableName); if (tuttiTable.isAssociation()) { PreparedStatement deleteStatement = @@ -470,9 +505,8 @@ // gets existing ids in the target db Set<List<String>> existingIds = getExistingPrimaryKeys(localConnection, table); - if (log.isInfoEnabled()) { - log.info("[" + table.getName() + "] existingIds: " + existingIds.size()); + log.info("[" + tableName + "] existingIds: " + existingIds.size()); } // update table content @@ -481,12 +515,14 @@ existingIds, dataToUpdate, result); + + dataToUpdate.close(); + } finally { + JdbcUtils.closeResultSet(dataToUpdate); } - dataToUpdate.close(); - } finally { - JdbcUtils.closeResultSet(dataToUpdate); } + } public static void prepareSynch(Connection connection) throws SQLException { Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeResult.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeResult.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeResult.java 2013-02-14 08:42:09 UTC (rev 411) @@ -41,10 +41,32 @@ protected Exception error; + /** + * Number of rows detected to update (per table). + * + * @since 1.0 + */ + protected final Map<String, Integer> rowHits = Maps.newTreeMap(); + + /** + * Number of insert done (per table). + * + * @since 1.0 + */ protected final Map<String, Integer> insertHits = Maps.newTreeMap(); + /** + * Number of update done (per table). + * + * @since 1.0 + */ protected final Map<String, Integer> updateHits = Maps.newTreeMap(); + /** + * All table treated. + * + * @since 1.0 + */ protected final Set<String> tableNames = Sets.newHashSet(); protected final String localUrl; @@ -72,6 +94,14 @@ return ImmutableSet.copyOf(tableNames); } + public int getNbRows(String tableName) { + Integer result = rowHits.get(tableName); + if (result == null) { + result = 0; + } + return result; + } + public int getNbInserts(String tableName) { Integer result = insertHits.get(tableName); if (result == null) { @@ -88,12 +118,22 @@ return result; } + public void addRows(String tableName, int nb) { + if (nb > 0) { + rowHits.put(tableName, getNbRows(tableName) + nb); + } + } + public void addUpdates(String tableName, int nb) { - updateHits.put(tableName, getNbUpdates(tableName) + nb); + if (nb > 0) { + updateHits.put(tableName, getNbUpdates(tableName) + nb); + } } public void addInserts(String tableName, int nb) { - insertHits.put(tableName, getNbInserts(tableName) + nb); + if (nb > 0) { + insertHits.put(tableName, getNbInserts(tableName) + nb); + } } public void addTableName(String tableName) { Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiAssociationTableMetadata.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiAssociationTableMetadata.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiAssociationTableMetadata.java 2013-02-14 08:42:09 UTC (rev 411) @@ -38,6 +38,8 @@ */ public class TuttiAssociationTableMetadata extends TuttiTableMetadata { + private final String countDataToUpdateQuery; + private final String dataToUpdateQuery; public TuttiAssociationTableMetadata(TableMetadata delegate, @@ -55,6 +57,7 @@ query.append(" FROM ").append(getName()); dataToUpdateQuery = query.toString(); + countDataToUpdateQuery = "SELECT count(*) FROM " + getName(); } @Override @@ -68,6 +71,16 @@ } @Override + public String getCountDataToUpdateQuery() { + return countDataToUpdateQuery; + } + + @Override + public String getCountDataToUpdateQueryWithNull() { + return countDataToUpdateQuery; + } + + @Override public boolean useUpdateDateColumn() { return false; } Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiEntityTableMetadata.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiEntityTableMetadata.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiEntityTableMetadata.java 2013-02-14 08:42:09 UTC (rev 411) @@ -39,7 +39,12 @@ */ public class TuttiEntityTableMetadata extends TuttiTableMetadata { + private final String countDataToUpdateQuery; + + private final String countDataToUpdateQueryWithNull; + private final String dataToUpdateQuery; + private final String dataToUpdateQueryWithNull; public TuttiEntityTableMetadata(TableMetadata delegate, @@ -57,14 +62,20 @@ query.append(queryParams.substring(2)); query.append(" FROM ").append(getName()); - dataToUpdateQueryWithNull= query.toString(); + dataToUpdateQueryWithNull = query.toString(); + countDataToUpdateQueryWithNull = "SELECT count(*) FROM " + getName(); + String whereClause; + if (isWithUpdateDateColumn()) { // add a filter - query.append(" WHERE (update_date IS NULL OR update_date > ?)"); + whereClause = " WHERE (update_date IS NULL OR update_date > ?)"; + } else { + whereClause = ""; } - dataToUpdateQuery = query.toString(); + dataToUpdateQuery = dataToUpdateQueryWithNull + whereClause; + countDataToUpdateQuery = countDataToUpdateQueryWithNull + whereClause; } @Override @@ -78,6 +89,16 @@ } @Override + public String getCountDataToUpdateQuery() { + return countDataToUpdateQuery; + } + + @Override + public String getCountDataToUpdateQueryWithNull() { + return countDataToUpdateQueryWithNull; + } + + @Override public boolean useUpdateDateColumn() { return true; } Modified: trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiTableMetadata.java =================================================================== --- trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiTableMetadata.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/main/java/fr/ifremer/tutti/persistence/service/synchro/TuttiTableMetadata.java 2013-02-14 08:42:09 UTC (rev 411) @@ -93,8 +93,10 @@ protected final boolean withUpdateDateColumn; + public abstract String getCountDataToUpdateQueryWithNull(); public abstract String getDataToUpdateQueryWithNull(); + public abstract String getCountDataToUpdateQuery(); public abstract String getDataToUpdateQuery(); public abstract boolean useUpdateDateColumn(); Modified: trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/DatabaseResource.java =================================================================== --- trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/DatabaseResource.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/DatabaseResource.java 2013-02-14 08:42:09 UTC (rev 411) @@ -150,9 +150,12 @@ }; } + Class<?> testClass; + protected void before(Description description) throws Throwable { - Class<?> testClass = description.getTestClass(); + testClass = description.getTestClass(); + File db = new File("src/test/db"); if (!db.exists()) { @@ -195,7 +198,7 @@ config = new TuttiPersistenceConfig(applicationConfig); if (writeDb) { - FileUtils.copyDirectory(db, config.getDbDirectory()); + copyDb(config.getDbDirectory(), !writeDb, null); } // load db config @@ -211,10 +214,10 @@ if (writeDb) { - p.setProperty("readonly", "false"); - BufferedWriter writer = Files.newWriter(dbConfig, Charsets.UTF_8); - p.store(writer, ""); - writer.close(); + // make sure db is on readonly mode + String readonly = p.getProperty("readonly"); + Preconditions.checkNotNull(readonly, "Could not find readonly property on db confg: " + dbConfig); + Preconditions.checkState("false".equals(readonly), "readonly property must be at false value in write mode test in db confg: " + dbConfig); } else { // make sure db is on readonly mode String readonly = p.getProperty("readonly"); @@ -241,17 +244,73 @@ } } + protected final Set<File> toDetroy = Sets.newHashSet(); + + public void addToDestroy(File dir) { + toDetroy.add(dir); + } + + public void copyDb(String dbDirectory, boolean readonly, Properties p) throws IOException { + File externalDbFile = getResourceDirectory(dbDirectory); + copyDb(externalDbFile, readonly, p); + } + + public void copyDb(File target, boolean readonly, Properties p) throws IOException { + File db = new File("src/test/db"); + if (!db.exists()) { + + if (log.isWarnEnabled()) { + log.warn("Could not find db at " + db + ", test [" + + testClass + "] is skipped."); + } + Assume.assumeTrue(false); + } + toDetroy.add(target); + FileUtils.copyDirectory(db, target); + if (p != null) { + TuttiEntities.fillConnectionProperties( + p, + "jdbc:hsqldb:file:" + target.getAbsolutePath() + "/" + config.getDbName(), + config.getJdbcUsername(), + config.getJdbcPassword()); + } + + // load db config + File dbConfig = new File(target, config.getDbName() + ".properties"); + Properties dbconf = new Properties(); + BufferedReader reader = Files.newReader(dbConfig, Charsets.UTF_8); + dbconf.load(reader); + reader.close(); + + // switch readonly flag according to the write parameter + dbconf.setProperty("readonly", String.valueOf(readonly)); + BufferedWriter writer = Files.newWriter(dbConfig, Charsets.UTF_8); + dbconf.store(writer, ""); + writer.close(); + + } + protected void after(Description description) throws IOException { - Class<?> testClass = description.getTestClass(); if (log.isInfoEnabled()) { log.info("After test " + testClass); } - if (writeDb && !witherror) { - if (log.isInfoEnabled()) { - log.info("Remove copy db at: " + config.getDbDirectory()); + if (!witherror) { + for (File file : toDetroy) { + if (file.exists()) { + if (log.isInfoEnabled()) { + log.info("Destroy directory: " + file); + } + try { + FileUtils.deleteDirectory(file); + } catch (IOException e) { + if (log.isErrorEnabled()) { + log.error("Could not delete directory: " + file, e); + } + } + } } - FileUtils.deleteDirectory(config.getDbDirectory()); + } // push back old classLoader if (oldClassLoader != null) { @@ -317,6 +376,7 @@ if (log.isInfoEnabled()) { log.info("Create new db at " + directory); } + addToDestroy(directory); String jdbcUrl = "jdbc:hsqldb:file:" + directory.getAbsolutePath() + "/" + dbName; String user = "SA"; String password = ""; Modified: trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelperTest.java =================================================================== --- trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelperTest.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeHelperTest.java 2013-02-14 08:42:09 UTC (rev 411) @@ -36,6 +36,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -403,6 +404,8 @@ } } + // To long some association tables + @Ignore @Test public void updateTable() throws SQLException, IOException { @@ -648,6 +651,7 @@ // same referentiel in both db Assert.assertNotNull(result); + Assert.assertNull(result.getError()); DatabaseSynchroFixtures fixturesExternal; DatabaseSynchroFixtures fixturesInternal; @@ -686,6 +690,8 @@ result = helper.synchronize(externalConnectionProperties, localConnectionProperties, dialect); + Assert.assertNotNull(result); + Assert.assertNull(result.getError()); fixturesExternal = new DatabaseSynchroFixtures( helper, internalDb, externalConnection); Modified: trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplTest.java =================================================================== --- trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplTest.java 2013-02-13 21:48:46 UTC (rev 410) +++ trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplTest.java 2013-02-14 08:42:09 UTC (rev 411) @@ -25,33 +25,30 @@ */ import fr.ifremer.tutti.persistence.DatabaseResource; +import fr.ifremer.tutti.persistence.entities.TuttiEntities; import fr.ifremer.tutti.persistence.service.TuttiPersistenceServiceLocator; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.hibernate.dialect.Dialect; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.TestName; import org.springframework.jdbc.support.JdbcUtils; -import java.io.IOException; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Properties; /** * @author tchemit <chemit@codelutin.com> * @since 1.0 */ -@Ignore public class ReferentialSynchronizeServiceImplTest { - /** Logger. */ - private static final Log log = - LogFactory.getLog(ReferentialSynchronizeServiceImplTest.class); - @ClassRule public static final DatabaseResource dbResource = DatabaseResource.writeDb(); @@ -60,25 +57,136 @@ protected ReferentialSynchronizeService service; - protected Connection externalConnection; + protected Properties remoteConnectionProperties; - protected Connection internalConnection; + protected Properties localConnectionProperties; + protected Connection remoteConnection; + + protected Connection localConnection; + + private Dialect dialect; + + protected ReferentialSynchronizeHelper helper; + @Before public void setUp() throws Exception { + + // create read-only remote db + remoteConnectionProperties = new Properties(); + dbResource.copyDb(n.getMethodName(), false, remoteConnectionProperties); + + // create services + helper = new ReferentialSynchronizeHelper(); service = TuttiPersistenceServiceLocator.getReferentialSynchronizeService(); + + // create grab local stuff + localConnectionProperties = service.getLocalConnectionProperties(); + dialect = service.getLocalDialect(); + + // open connections + remoteConnection = TuttiEntities.createConnection(remoteConnectionProperties); + localConnection = TuttiEntities.createConnection(localConnectionProperties); } @After public void tearDown() throws Exception { service = null; - JdbcUtils.closeConnection(internalConnection); - JdbcUtils.closeConnection(externalConnection); + remoteConnectionProperties = null; + dialect = null; + helper = null; + JdbcUtils.closeConnection(localConnection); + JdbcUtils.closeConnection(remoteConnection); } - protected void createExternalDb() throws IOException, SQLException { - externalConnection = dbResource.createEmptyDb(n.getMethodName(), "newDb"); - Assert.assertNotNull(externalConnection); + @Test + public void synchronize() throws SQLException { + + TuttiDatabaseMetadata remoteSchema = + helper.loadDatabaseMetadata(remoteConnection, dialect); + + DatabaseSynchroFixtures fixturesRemote; + DatabaseSynchroFixtures fixturesLocal; + ReferentialSynchronizeResult result; + + // add some stuff on remote db + + // add a new status + remoteConnection.prepareStatement("INSERT INTO STATUS(CODE, NAME) VALUES (4, 'NEW')").executeUpdate(); + + // add a new aggregation_level + remoteConnection.prepareStatement("INSERT INTO AGGREGATION_LEVEL (ID, NAME, RANK_ORDER, UPDATE_DATE) VALUES (-1000, 'NAME', -1000, NULL)").executeUpdate(); + + // update a unit + PreparedStatement preparedStatement = remoteConnection.prepareStatement("UPDATE UNIT SET SYMBOL = 'Auncun un?', UPDATE_DATE=? WHERE ID=1"); + preparedStatement.setTimestamp(1, new Timestamp(System.currentTimeMillis())); + preparedStatement.executeUpdate(); + + remoteConnection.commit(); + + fixturesRemote = new DatabaseSynchroFixtures( + helper, remoteSchema, remoteConnection); + + result = service.synchronize(remoteConnectionProperties); + + // same referentiel in both db + Assert.assertNotNull(result); + Assert.assertNull(result.getError()); + + fixturesLocal = new DatabaseSynchroFixtures( + helper, remoteSchema, localConnection); + + // on entities, only updates in table is with no update_date + for (TuttiTable tuttiTable : TuttiTable.getEntityTables()) { + String tableName = tuttiTable.name(); + int nbInserts = result.getNbInserts(tableName); + int nbUpdates = result.getNbUpdates(tableName); + int nbLocal = fixturesLocal.count(tuttiTable); + int nbRemote = fixturesRemote.count(tuttiTable); + int expectedRows = result.getNbRows(tableName); + int expectedInserts = 0; + int expectedUpdates = 0; + TuttiTableMetadata table = remoteSchema.getTable(tableName); + if (!table.isWithUpdateDateColumn()) { + // updates everything + expectedUpdates = nbRemote; + } + + switch (tuttiTable) { + case STATUS: + expectedInserts = 1; + expectedUpdates--; + break; + case UNIT: + + expectedUpdates = 1; + break; + + case AGGREGATION_LEVEL: + expectedInserts = 1; + break; + } + Assert.assertEquals(String.format("Table %s Should found %s rows, but was only %s", tableName, nbRemote, nbLocal), nbRemote, nbLocal, 0); + Assert.assertEquals(String.format("Table %s Should found %s inserts, but was only %s", tableName, expectedInserts, nbInserts), expectedInserts, nbInserts, 0); + Assert.assertEquals(String.format("Table %s Should found %s updates, but was only %s", tableName, expectedUpdates, nbUpdates), expectedUpdates, nbUpdates, 0); + Assert.assertEquals(String.format("Table %s Should found %s rows treated, but was only %s", tableName, expectedRows, nbInserts + nbUpdates), expectedRows, nbInserts + nbUpdates, 0); + } + + // on associations, only inserts + for (TuttiTable tuttiTable : TuttiTable.getAssociationTables()) { + String tableName = tuttiTable.name(); + int nbInserts = result.getNbInserts(tableName); + int nbUpdates = result.getNbUpdates(tableName); + int nbLocal = fixturesLocal.count(tuttiTable); + int nbRemote = fixturesRemote.count(tuttiTable); + int expectedRows = result.getNbRows(tableName); + int expectedInserts = nbRemote; + int expectedUpdates = 0; + Assert.assertEquals(String.format("Table %s Should found %s rows, but was only %s", tableName, nbRemote, nbLocal), nbRemote, nbLocal, 0); + Assert.assertEquals(String.format("Table %s Should found %s inserts, but was only %s", tableName, expectedInserts, nbInserts), expectedInserts, nbInserts, 0); + Assert.assertEquals(String.format("Table %s Should found %s updates, but was only %s", tableName, expectedUpdates, nbUpdates), expectedUpdates, nbUpdates, 0); + Assert.assertEquals(String.format("Table %s Should found %s rows treated, but was only %s", tableName, expectedRows, nbInserts + nbUpdates), expectedRows, nbInserts + nbUpdates, 0); + } + } - } Copied: trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplUpTodateTest.java (from rev 410, trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplTest.java) =================================================================== --- trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplUpTodateTest.java (rev 0) +++ trunk/tutti-persistence/src/test/java/fr/ifremer/tutti/persistence/service/synchro/ReferentialSynchronizeServiceImplUpTodateTest.java 2013-02-14 08:42:09 UTC (rev 411) @@ -0,0 +1,160 @@ +package fr.ifremer.tutti.persistence.service.synchro; + +/* + * #%L + * Tutti :: Persistence API + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2012 - 2013 Ifremer + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import fr.ifremer.tutti.persistence.DatabaseResource; +import fr.ifremer.tutti.persistence.entities.TuttiEntities; +import fr.ifremer.tutti.persistence.service.TuttiPersistenceServiceLocator; +import org.hibernate.dialect.Dialect; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.springframework.jdbc.support.JdbcUtils; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 1.0 + */ +public class ReferentialSynchronizeServiceImplUpTodateTest { + + @ClassRule + public static final DatabaseResource dbResource = DatabaseResource.writeDb(); + + @Rule + public final TestName n = new TestName(); + + protected ReferentialSynchronizeService service; + + protected Properties remoteConnectionProperties; + + protected Properties localConnectionProperties; + + protected Connection remoteConnection; + + protected Connection localConnection; + + private Dialect dialect; + + protected ReferentialSynchronizeHelper helper; + + @Before + public void setUp() throws Exception { + + // create read-only remote db + remoteConnectionProperties = new Properties(); + dbResource.copyDb(n.getMethodName(), false, remoteConnectionProperties); + + // create services + helper = new ReferentialSynchronizeHelper(); + service = TuttiPersistenceServiceLocator.getReferentialSynchronizeService(); + + // create grab local stuff + localConnectionProperties = service.getLocalConnectionProperties(); + dialect = service.getLocalDialect(); + + // open connections + remoteConnection = TuttiEntities.createConnection(remoteConnectionProperties); + localConnection = TuttiEntities.createConnection(localConnectionProperties); + } + + @After + public void tearDown() throws Exception { + service = null; + remoteConnectionProperties = null; + dialect = null; + helper = null; + JdbcUtils.closeConnection(localConnection); + JdbcUtils.closeConnection(remoteConnection); + } + + @Test + public void synchronize() throws SQLException { + + TuttiDatabaseMetadata remoteSchema = + helper.loadDatabaseMetadata(remoteConnection, dialect); + + DatabaseSynchroFixtures fixturesRemote; + DatabaseSynchroFixtures fixturesLocal; + ReferentialSynchronizeResult result; + + fixturesRemote = new DatabaseSynchroFixtures( + helper, remoteSchema, remoteConnection); + + result = service.synchronize(remoteConnectionProperties); + + // same referentiel in both db + Assert.assertNotNull(result); + Assert.assertNull(result.getError()); + + fixturesLocal = new DatabaseSynchroFixtures( + helper, remoteSchema, localConnection); + + // on entities, only updates in table is with no update_date + for (TuttiTable tuttiTable : TuttiTable.getEntityTables()) { + String tableName = tuttiTable.name(); + int nbInserts = result.getNbInserts(tableName); + int nbUpdates = result.getNbUpdates(tableName); + int nbLocal = fixturesLocal.count(tuttiTable); + int nbRemote = fixturesRemote.count(tuttiTable); + int expectedRows = result.getNbRows(tableName); + int expectedInserts = 0; + int expectedUpdates = 0; + TuttiTableMetadata table = remoteSchema.getTable(tableName); + if (!table.isWithUpdateDateColumn()) { + // updates everything + expectedUpdates = nbRemote; + } + + Assert.assertEquals(String.format("Table %s Should found %s rows, but was only %s", tableName, nbRemote, nbLocal), nbRemote, nbLocal, 0); + Assert.assertEquals(String.format("Table %s Should found %s inserts, but was only %s", tableName, expectedInserts, nbInserts), expectedInserts, nbInserts, 0); + Assert.assertEquals(String.format("Table %s Should found %s updates, but was only %s", tableName, expectedUpdates, nbUpdates), expectedUpdates, nbUpdates, 0); + Assert.assertEquals(String.format("Table %s Should found %s rows treated, but was only %s", tableName, expectedRows, nbInserts + nbUpdates), expectedRows, nbInserts + nbUpdates, 0); + } + + // on associations, only inserts + for (TuttiTable tuttiTable : TuttiTable.getAssociationTables()) { + String tableName = tuttiTable.name(); + int nbInserts = result.getNbInserts(tableName); + int nbUpdates = result.getNbUpdates(tableName); + int nbLocal = fixturesLocal.count(tuttiTable); + int nbRemote = fixturesRemote.count(tuttiTable); + int expectedRows = result.getNbRows(tableName); + int expectedInserts = nbRemote; + int expectedUpdates = 0; + Assert.assertEquals(String.format("Table %s Should found %s rows, but was only %s", tableName, nbRemote, nbLocal), nbRemote, nbLocal, 0); + Assert.assertEquals(String.format("Table %s Should found %s inserts, but was only %s", tableName, expectedInserts, nbInserts), expectedInserts, nbInserts, 0); + Assert.assertEquals(String.format("Table %s Should found %s updates, but was only %s", tableName, expectedUpdates, nbUpdates), expectedUpdates, nbUpdates, 0); + Assert.assertEquals(String.format("Table %s Should found %s rows treated, but was only %s", tableName, expectedRows, nbInserts + nbUpdates), expectedRows, nbInserts + nbUpdates, 0); + } + } +}