/** * Licensed to JumpMind Inc under one or more contributor * license agreements. See the NOTICE file distributed * with this work for additional information regarding * copyright ownership. JumpMind Inc licenses this file * to you under the GNU General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jumpmind.symmetric.io.data.writer; import java.sql.Timestamp; import java.sql.Types; import java.util.Date; import java.util.Map; import java.util.TimeZone; import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Table; import org.jumpmind.db.model.TypeMap; import org.jumpmind.db.platform.IDatabasePlatform; import org.jumpmind.db.sql.DmlStatement; import org.jumpmind.db.sql.DmlStatement.DmlType; import org.jumpmind.exception.ParseException; import org.jumpmind.symmetric.io.data.CsvData; import org.jumpmind.util.FormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultDatabaseWriterConflictResolver extends AbstractDatabaseWriterConflictResolver { protected static final Logger log = LoggerFactory.getLogger(DefaultDatabaseWriterConflictResolver.class); protected boolean isTimestampNewer(Conflict conflict, AbstractDatabaseWriter writer, CsvData data) { DefaultDatabaseWriter databaseWriter = (DefaultDatabaseWriter)writer; IDatabasePlatform platform = databaseWriter.getPlatform(); String columnName = conflict.getDetectExpression(); Table targetTable = writer.getTargetTable(); Table sourceTable = writer.getSourceTable(); String[] pkData = data.getPkData(targetTable); Object[] objectValues = databaseWriter.getPlatform().getObjectValues( writer.getBatch().getBinaryEncoding(), pkData, targetTable.getPrimaryKeyColumns()); DmlStatement stmt = databaseWriter.getPlatform().createDmlStatement(DmlType.FROM, targetTable , writer.getWriterSettings().getTextColumnExpression()); Column column = targetTable.getColumnWithName(columnName); if (column == null) { throw new RuntimeException(String.format("Could not find a timestamp column with a name of %s on the table %s. " + "Please check your conflict resolution configuration", columnName, targetTable.getQualifiedTableName())); } String sql = stmt.getColumnsSql(new Column[] { column }); Map<String, String> newData = data.toColumnNameValuePairs(sourceTable.getColumnNames(), CsvData.ROW_DATA); String loadingStr = newData.get(columnName); Date loadingTs = null; Date existingTs = null; if (column.isTimestampWithTimezone()) { // Get the existingTs with timezone String existingStr = databaseWriter.getTransaction().queryForObject(sql, String.class, objectValues); // If you are in this situation because of an instance where the conflict exists // because the row doesn't exist, then existing simply needs to be null if (existingStr != null) { int split = existingStr.lastIndexOf(" "); existingTs = FormatUtils.parseDate(existingStr.substring(0, split).trim(), FormatUtils.TIMESTAMP_PATTERNS, TimeZone.getTimeZone(existingStr.substring(split).trim())); } // Get the loadingTs with timezone int split = loadingStr.lastIndexOf(" "); loadingTs = FormatUtils.parseDate(loadingStr.substring(0, split).trim(), FormatUtils.TIMESTAMP_PATTERNS, TimeZone.getTimeZone(loadingStr.substring(split).trim())); } else { // Get the existingTs existingTs = databaseWriter.getTransaction().queryForObject(sql, Timestamp.class, objectValues); // Get the loadingTs Object[] values = platform.getObjectValues(writer.getBatch().getBinaryEncoding(), new String[] { loadingStr }, new Column[] { column }); if (values[0] instanceof Date) { loadingTs = (Date) values[0]; } else if (values[0] instanceof String && column.getJdbcTypeName().equalsIgnoreCase(TypeMap.DATETIME2)) { // SQL Server DateTime2 type is treated as a string internally. loadingTs = databaseWriter.getPlatform().parseDate(Types.VARCHAR, (String)values[0], false); } else { throw new ParseException("Could not parse " + columnName + " with a value of " + loadingStr + " for purposes of conflict detection"); } } return existingTs == null || loadingTs.compareTo(existingTs) > 0; } protected boolean isVersionNewer(Conflict conflict, AbstractDatabaseWriter writer, CsvData data) { DefaultDatabaseWriter databaseWriter = (DefaultDatabaseWriter)writer; String columnName = conflict.getDetectExpression(); Table targetTable = writer.getTargetTable(); Table sourceTable = writer.getSourceTable(); String[] pkData = data.getPkData(targetTable); Object[] objectValues = databaseWriter.getPlatform().getObjectValues( writer.getBatch().getBinaryEncoding(), pkData, targetTable.getPrimaryKeyColumns()); DmlStatement stmt = databaseWriter.getPlatform().createDmlStatement(DmlType.FROM, targetTable , writer.getWriterSettings().getTextColumnExpression()); String sql = stmt.getColumnsSql(new Column[] { targetTable.getColumnWithName(columnName) }); Long existingVersion = databaseWriter.getTransaction() .queryForObject(sql, Long.class, objectValues); if (existingVersion == null) { return true; } else { Map<String, String> newData = data.toColumnNameValuePairs(sourceTable.getColumnNames(), CsvData.ROW_DATA); Long loadingVersion = Long.valueOf(newData.get(columnName)); return loadingVersion > existingVersion; } } }