diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java index e78af4c..fd85ce3 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java @@ -16,6 +16,8 @@ */ package org.apache.jackrabbit.oak.plugins.document.rdb; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Sets.newHashSet; import static org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.CHAR2OCTETRATIO; import static org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentStore.asBytes; import static org.apache.jackrabbit.oak.plugins.document.rdb.RDBJDBCTools.closeResultSet; @@ -31,6 +33,7 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -266,8 +269,9 @@ public class RDBDocumentStoreJDBC { "insert into " + tmd.getName() + "(ID, MODIFIED, HASBINARY, DELETEDONCE, MODCOUNT, CMODCOUNT, DSIZE, DATA, BDATA) " + "values (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + List sortedDocs = sortDocuments(documents); try { - for (T document : documents) { + for (T document : sortedDocs) { String data = this.ser.asString(document); String id = document.getId(); Number hasBinary = (Number) document.get(NodeDocument.HAS_BINARY_FLAG); @@ -296,12 +300,12 @@ public class RDBDocumentStoreJDBC { int[] results = stmt.executeBatch(); Set succesfullyInserted = new HashSet(); - for (int i = 0; i < documents.size(); i++) { + for (int i = 0; i < sortedDocs.size(); i++) { int result = results[i]; if (result != 1 && result != Statement.SUCCESS_NO_INFO) { - LOG.error("DB insert failed for {}: {}", tmd.getName(), documents.get(i).getId()); + LOG.error("DB insert failed for {}: {}", tmd.getName(), sortedDocs.get(i).getId()); } else { - succesfullyInserted.add(documents.get(i).getId()); + succesfullyInserted.add(sortedDocs.get(i).getId()); } } return succesfullyInserted; @@ -318,6 +322,9 @@ public class RDBDocumentStoreJDBC { *

* If the {@code upsert} parameter is set to true, the method will also try to insert new documents, those * which modcount equals to 1. + *

+ * The order of applying updates will be different than order of the passed list, so there shouldn't be two + * updates related to the same document. An {@link IllegalArgumentException} will be thrown if there are. * * @param connection JDBC connection * @param tmd Table metadata @@ -328,13 +335,15 @@ public class RDBDocumentStoreJDBC { */ public Set update(Connection connection, RDBTableMetaData tmd, List documents, boolean upsert) throws SQLException { + assertNoDuplicatedIds(documents); + Set successfulUpdates = new HashSet(); PreparedStatement stmt = connection.prepareStatement("update " + tmd.getName() + " set MODIFIED = ?, HASBINARY = ?, DELETEDONCE = ?, MODCOUNT = ?, CMODCOUNT = ?, DSIZE = ?, DATA = ?, BDATA = ? where ID = ? and MODCOUNT = ?"); try { List updatedKeys = new ArrayList(); - for (T document : documents) { + for (T document : sortDocuments(documents)) { Long modcount = (Long) document.get(MODCOUNT); if (modcount == 1) { continue; // This is a new document. We'll deal with the inserts later. @@ -430,6 +439,12 @@ public class RDBDocumentStoreJDBC { return successfulUpdates; } + private static void assertNoDuplicatedIds(List documents) { + if (newHashSet(transform(documents, idExtractor)).size() < documents.size()) { + throw new IllegalArgumentException("There are duplicated ids in the document list"); + } + } + private final static Map INDEXED_PROP_MAPPING; static { Map tmp = new HashMap(); @@ -753,6 +768,17 @@ public class RDBDocumentStoreJDBC { } } + private static List sortDocuments(Collection documents) { + List result = new ArrayList(documents); + Collections.sort(result, new Comparator() { + @Override + public int compare(T o1, T o2) { + return o1.getId().compareTo(o2.getId()); + } + }); + return result; + } + private static final Function idExtractor = new Function() { @Override public String apply(Document input) {