diff --git a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java index 3f7a395f64..b8a55063d5 100644 --- a/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java +++ b/jOOQ-test/src/org/jooq/test/jOOQAbstractTest.java @@ -179,6 +179,7 @@ public abstract class jOOQAbstractTest< protected static boolean initialised; protected static boolean reset; protected static Connection connection; + protected static boolean autocommit; protected static String jdbcURL; protected static String jdbcSchema; protected static Map scripts = new HashMap(); @@ -290,6 +291,7 @@ public abstract class jOOQAbstractTest< @Before public void setUp() throws Exception { connection = getConnection(); + autocommit = connection.getAutoCommit(); if (!initialised) { initialised = true; @@ -304,6 +306,7 @@ public abstract class jOOQAbstractTest< @After public void tearDown() throws Exception { + connection.setAutoCommit(autocommit); } protected final void register(final Configuration configuration) { @@ -6574,6 +6577,8 @@ public abstract class jOOQAbstractTest< @Test public void testLoader() throws Exception { + connection.setAutoCommit(false); + Field count = create().count(); // Empty CSV file @@ -6773,10 +6778,75 @@ public abstract class jOOQAbstractTest< assertEquals("Frisch", result.getValue(1, TAuthor_LAST_NAME())); assertEquals("George", result.getValue(0, TAuthor_FIRST_NAME())); assertEquals(null, result.getValue(1, TAuthor_FIRST_NAME())); + + assertEquals(1, create().delete(TAuthor()).where(TAuthor_ID().in(7)).execute()); } } - // TODO Add commit / rollback tests + // Rollback on duplicate keys + // -------------------------- + loader = + create().loadInto(TAuthor()) + .commitAll() + .onDuplicateKeyError() + .onErrorAbort() + .loadCSV( + "\"ID\",\"First Name\",\"Last Name\"\r" + + "8,Hermann,Hesse\n" + + "1,\"Max\",Frisch\n" + + "2,Friedrich,Dürrenmatt") + .fields(TAuthor_ID(), null, TAuthor_LAST_NAME()) + .execute(); + + assertEquals(2, loader.processed()); + assertEquals(0, loader.stored()); + assertEquals(1, loader.ignored()); + assertEquals(1, loader.errors().size()); + assertEquals(1, loader.errors().get(0).rowIndex()); + assertEquals( + Arrays.asList("1", "Max", "Frisch"), + Arrays.asList(loader.errors().get(0).row())); + + result = + create().selectFrom(TAuthor()) + .where(TAuthor_ID().in(8)) + .orderBy(TAuthor_ID()) + .fetch(); + + assertEquals(0, result.size()); + + // Commit and ignore duplicates + // ---------------------------- + loader = + create().loadInto(TAuthor()) + .commitAll() + .onDuplicateKeyIgnore() + .onErrorAbort() + .loadCSV( + "\"ID\",\"First Name\",\"Last Name\"\r" + + "8,Hermann,Hesse\n" + + "1,\"Max\",Frisch\n" + + "2,Friedrich,Dürrenmatt") + .fields(TAuthor_ID(), null, TAuthor_LAST_NAME()) + .execute(); + + assertEquals(3, loader.processed()); + assertEquals(1, loader.stored()); + assertEquals(2, loader.ignored()); + assertEquals(0, loader.errors().size()); + + result = + create().selectFrom(TAuthor()) + .where(TAuthor_ID().in(1, 2, 8)) + .orderBy(TAuthor_ID()) + .fetch(); + + assertEquals(3, result.size()); + assertEquals(8, (int) result.getValue(2, TAuthor_ID())); + assertNull(result.getValue(2, TAuthor_FIRST_NAME())); + assertEquals("Hesse", result.getValue(2, TAuthor_LAST_NAME())); + assertEquals("Coelho", result.getValue(1, TAuthor_LAST_NAME())); + assertEquals("Orwell", result.getValue(0, TAuthor_LAST_NAME())); } /** diff --git a/jOOQ/src/main/java/org/jooq/LoaderOptionsStep.java b/jOOQ/src/main/java/org/jooq/LoaderOptionsStep.java index 5b14d85c79..5e60e57d50 100644 --- a/jOOQ/src/main/java/org/jooq/LoaderOptionsStep.java +++ b/jOOQ/src/main/java/org/jooq/LoaderOptionsStep.java @@ -35,6 +35,8 @@ */ package org.jooq; +import java.sql.Connection; + /** * The Loader API is used for configuring data loads. *

@@ -80,9 +82,9 @@ public interface LoaderOptionsStep> extends LoaderSourc * in a later step of Loader, then loading is rollbacked on * abort. *

- * If you don't specify a behaviour, this will be - * the default. This cannot be combined with {@link #onDuplicateKeyIgnore()} - * or {@link #onDuplicateUpdate()} + * If you don't specify a behaviour, this will be the default. This cannot + * be combined with {@link #onDuplicateKeyIgnore()} or + * {@link #onDuplicateUpdate()} */ LoaderOptionsStep onDuplicateKeyError(); @@ -121,7 +123,10 @@ public interface LoaderOptionsStep> extends LoaderSourc *

* The COMMIT OPTIONS might be useful for fine-tuning performance behaviour * in some RDBMS, where large commits lead to a high level of concurrency in - * the database. + * the database. Use this on fresh transactions only. Commits/Rollbacks are + * executed directly upon {@link Configuration#getConnection()}. This might + * not work with container-managed transactions, or when + * {@link Connection#getAutoCommit()} is set to true. *

* If you don't specify a COMMIT OPTION, {@link #commitNone()} will be the * default, leaving transaction handling up to you. @@ -138,7 +143,10 @@ public interface LoaderOptionsStep> extends LoaderSourc *

* The COMMIT OPTIONS might be useful for fine-tuning performance behaviour * in some RDBMS, where large commits lead to a high level of concurrency in - * the database. + * the database. Use this on fresh transactions only. Commits/Rollbacks are + * executed directly upon {@link Configuration#getConnection()}. This might + * not work with container-managed transactions, or when + * {@link Connection#getAutoCommit()} is set to true. *

* If you don't specify a COMMIT OPTION, {@link #commitNone()} will be the * default, leaving transaction handling up to you. @@ -155,7 +163,10 @@ public interface LoaderOptionsStep> extends LoaderSourc *

* The COMMIT OPTIONS might be useful for fine-tuning performance behaviour * in some RDBMS, where large commits lead to a high level of concurrency in - * the database. + * the database. Use this on fresh transactions only. Commits/Rollbacks are + * executed directly upon {@link Configuration#getConnection()}. This might + * not work with container-managed transactions, or when + * {@link Connection#getAutoCommit()} is set to true. *

* If you don't specify a COMMIT OPTION, {@link #commitNone()} will be the * default, leaving transaction handling up to you. @@ -170,7 +181,9 @@ public interface LoaderOptionsStep> extends LoaderSourc * the database. *

* If you don't specify a COMMIT OPTION, this will be the default, leaving - * transaction handling up to you. + * transaction handling up to you. This should be your choice, when you use + * container-managed transactions, too, or your + * {@link Connection#getAutoCommit()} value is set to true. */ LoaderOptionsStep commitNone(); diff --git a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java index 354bd6b5d4..804d2c368c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/LoaderImpl.java @@ -409,6 +409,7 @@ class LoaderImpl> implements try { if (commit == COMMIT_ALL) { if (!errors.isEmpty()) { + stored = 0; create.getConnection().rollback(); } else {