public void run() {
		jdbcValueReader = jdbcValueReaderProvider.getValueReaderFor(desc
				.getTargetTable());

		final List<String> sourceColumns = allSourceColumns();
		FutureTask<TableHash> futureSourceHash = futureSourceHash(sourceColumns);
		FutureTask<TableHash> futureTargetHash = futureTargetHash(sourceColumns);

		PreparedStatement deleteStatement = makeDeleteStatement();
		PreparedStatement insertStatement = makeInsertStatement();
		PreparedStatement updateStatement = makeUpdateStatement();

		try {
			TableHash sourceHash = futureSourceHash.get();
			TableHash targetHash = futureTargetHash.get();

			for (String pkHash : sourceHash.pks()) {
				if (targetHash.hasPk(pkHash)) {
					if (Objects.equals(sourceHash.dataHashFor(pkHash),
							targetHash.dataHashFor(pkHash))) {
						// same => skip
						continue;
					}
					updateByHash(updateStatement, sourceHash, pkHash);
				} else {
					// missing in target => create in target
					insertByHash(insertStatement, sourceHash, pkHash);
				}
			}

			for (String pkHash : targetHash.pks()) {
				if (!sourceHash.hasPk(pkHash)) {
					// missing in source => delete in target
					deleteByHash(deleteStatement, targetHash, pkHash);
				}
			}

			sourceHash.close();
			targetHash.close();

			targetDbAgent.commit();
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			jdbcValueReader = null;
			sourceDbAgent.cleanup();
			targetDbAgent.cleanup();
		}
	}