diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/CRUDTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/CRUDTests.java index 81281aa5e5..bba3d2b8f2 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/CRUDTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/CRUDTests.java @@ -61,9 +61,11 @@ import org.jooq.TableRecord; import org.jooq.UDTRecord; import org.jooq.UpdatableRecord; import org.jooq.UpdatableTable; +import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataChangedException; import org.jooq.exception.InvalidResultException; +import org.jooq.impl.Factory; import org.jooq.test.BaseTest; import org.jooq.test.jOOQAbstractTest; @@ -590,80 +592,82 @@ extends BaseTest> void testStoreLocked0( UpdatableTable table, TableField id, TableField string) throws Exception { + Factory create = create(new Settings().withExecuteWithOptimisticLocking(true)); + // Storing without changing shouldn't execute any queries - R record1 = create().fetchOne(table, id.equal(1)); - assertEquals(0, record1.storeLocked()); - assertEquals(0, record1.storeLocked()); + R record1 = create.fetchOne(table, id.equal(1)); + assertEquals(0, record1.store()); + assertEquals(0, record1.store()); // Succeed if there are no concurrency issues record1.setValue(string, "New Title 1"); - assertEquals(1, record1.storeLocked()); - assertEquals("New Title 1", create().fetchOne(table, id.equal(1)).getValue(string)); + assertEquals(1, record1.store()); + assertEquals("New Title 1", create.fetchOne(table, id.equal(1)).getValue(string)); // Get new books - R record2 = create().fetchOne(table, id.equal(1)); - R record3 = create().fetchOne(table, id.equal(1)); + R record2 = create.fetchOne(table, id.equal(1)); + R record3 = create.fetchOne(table, id.equal(1)); // Still won't fail, but this will cause record3 to be stale record2.setValue(string, "New Title 2"); - assertEquals(1, record2.storeLocked()); - assertEquals("New Title 2", create().fetchOne(table, id.equal(1)).getValue(string)); + assertEquals(1, record2.store()); + assertEquals("New Title 2", create.fetchOne(table, id.equal(1)).getValue(string)); // Storing without changing shouldn't execute any queries - assertEquals(0, record3.storeLocked()); + assertEquals(0, record3.store()); // This should fail as record3 is stale record3.setValue(string, "New Title 3"); try { - record3.storeLocked(); + record3.store(); fail(); } catch (DataChangedException expected) {} - assertEquals("New Title 2", create().fetchOne(table, id.equal(1)).getValue(string)); + assertEquals("New Title 2", create.fetchOne(table, id.equal(1)).getValue(string)); // Refreshing first will work, though record3.refresh(); record3.setValue(string, "New Title 3"); - assertEquals(1, record3.storeLocked()); - assertEquals("New Title 3", create().fetchOne(table, id.equal(1)).getValue(string)); + assertEquals(1, record3.store()); + assertEquals("New Title 3", create.fetchOne(table, id.equal(1)).getValue(string)); // Get new books - R record4 = create().fetchOne(table, id.equal(1)); - R record5 = create().fetchOne(table, id.equal(1)); + R record4 = create.fetchOne(table, id.equal(1)); + R record5 = create.fetchOne(table, id.equal(1)); // Delete the book - assertEquals(1, record4.deleteLocked()); + assertEquals(1, record4.delete()); // Storing without changing shouldn't execute any queries - assertEquals(0, record5.storeLocked()); + assertEquals(0, record5.store()); // This should fail, as the database record no longer exists record5.setValue(string, "New Title 5"); try { - record5.storeLocked(); + record5.store(); fail(); } catch (DataChangedException expected) {} // Restore the book, then it should work - assertEquals(1, record4.storeLocked()); - assertEquals(1, record5.storeLocked()); - assertEquals("New Title 5", create().fetchOne(table, id.equal(1)).getValue(string)); + assertEquals(1, record4.store()); + assertEquals(1, record5.store()); + assertEquals("New Title 5", create.fetchOne(table, id.equal(1)).getValue(string)); // Deleting the original should no longer be possible try { - record4.deleteLocked(); + record4.delete(); fail(); } catch (DataChangedException expected) {} // Refreshing and deleting should work record4.refresh(); - assertEquals(1, record4.deleteLocked()); + assertEquals(1, record4.delete()); // Now the other record cannot be deleted anymore try { - record5.deleteLocked(); + record5.delete(); fail(); } catch (DataChangedException expected) {} diff --git a/jOOQ/pom.xml b/jOOQ/pom.xml index 043e7c91d8..6c16481cc6 100644 --- a/jOOQ/pom.xml +++ b/jOOQ/pom.xml @@ -9,7 +9,7 @@ jooq-parent 2.5.0-SNAPSHOT - + org.jooq jooq jOOQ @@ -38,7 +38,7 @@ false src/main/resources/xsd - jooq-runtime-2.3.0.xsd + jooq-runtime-2.5.0.xsd org.jooq.conf diff --git a/jOOQ/src/main/java/org/jooq/TableRecord.java b/jOOQ/src/main/java/org/jooq/TableRecord.java index 625a8e29a0..26b4dad9cf 100644 --- a/jOOQ/src/main/java/org/jooq/TableRecord.java +++ b/jOOQ/src/main/java/org/jooq/TableRecord.java @@ -38,6 +38,7 @@ package org.jooq; import java.sql.ResultSet; import java.sql.Statement; +import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataChangedException; @@ -71,6 +72,25 @@ public interface TableRecord> extends Record { * record. *
  • If this record was loaded by jOOQ, and the provided keys' value was * not changed, an UPDATE statement is executed.
  • + *
  • If an UPDATE statement is executed and + * {@link Settings#isExecuteWithOptimisticLocking()} is set to + * true, then this record will first be compared with the + * latest state in the database. In order to compare this record with the + * latest state, the database record will be locked pessimistically using a + * SELECT .. FOR UPDATE statement. Not all databases support + * the FOR UPDATE clause natively. Namely, the following + * databases will show slightly different behaviour: + *
      + *
    • {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will + * try to lock the database record using JDBC's + * {@link ResultSet#TYPE_SCROLL_SENSITIVE} and + * {@link ResultSet#CONCUR_UPDATABLE}.
    • + *
    • {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client + * code must assure that no race-conditions can occur between jOOQ's + * checking of database record state and the actual UPDATE
    • + *
    + *

    + * See {@link LockProvider#setForUpdate(boolean)} for more details

  • * *

    * In either statement, only those fields are inserted/updated, which had @@ -106,49 +126,10 @@ public interface TableRecord> extends Record { * WHERE clause. * @return The number of stored records. * @throws DataAccessException if something went wrong executing the query + * @throws DataChangedException If optimistic locking is enabled and the + * record has already been changed/deleted in the database */ - int storeUsing(TableField... keys) throws DataAccessException; - - /** - * Store this record back to the database assuming an optimistic lock. - *

    - * This performs the same action as {@link #storeUsing(TableField...)}, - * except that if an UPDATE is performed, this record will - * first be compared with the latest state in the database. - *

    - * Note that in order to compare this record with the latest state, the - * database record will be locked pessimistically using a - * SELECT .. FOR UPDATE statement. Not all databases support - * the FOR UPDATE clause natively. Namely, the following - * databases will show slightly different behaviour: - *

      - *
    • {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will - * try to lock the database record using JDBC's - * {@link ResultSet#TYPE_SCROLL_SENSITIVE} and - * {@link ResultSet#CONCUR_UPDATABLE}.
    • - *
    • {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client - * code must assure that no race-conditions can occur between jOOQ's - * checking of database record state and the actual UPDATE
    • - *
    - *

    - * See {@link LockProvider#setForUpdate(boolean)} for more details - *

    - * Unlike {@link #storeUsing(TableField...)}, this will fail if several - * records are concerned. - * - * @param keys The key fields used for deciding whether to execute an - * INSERT or UPDATE statement. If an - * UPDATE statement is executed, they are also the - * key fields for the UPDATE statement's - * WHERE clause. - * @return The number of stored records. - * @throws DataAccessException if something went wrong executing the query - * @throws DataChangedException if the record has already been changed in - * the database - * @see #storeUsing(TableField...) - * @see LockProvider#setForUpdate(boolean) - */ - int storeLockedUsing(TableField... keys) throws DataAccessException, DataChangedException; + int storeUsing(TableField... keys) throws DataAccessException, DataChangedException; /** * Deletes this record from the database, based on the value of the provided @@ -157,23 +138,11 @@ public interface TableRecord> extends Record { * The executed statement is

          * DELETE FROM [table]
          * WHERE [key fields = key values]
    - * - * @param keys The key fields for the DELETE statement's - * WHERE clause. - * @return The number of deleted records. - * @throws DataAccessException if something went wrong executing the query - */ - int deleteUsing(TableField... keys) throws DataAccessException; - - /** - * Deletes this record from the database assuming an optimistic lock. *

    - * This performs the same action as {@link #deleteUsing(TableField...)}, - * except that this record will first be compared with the latest state in - * the database. - *

    - * Note that in order to compare this record with the latest state, the - * database record will be locked pessimistically using a + * If {@link Settings#isExecuteWithOptimisticLocking()} is set to + * true, then this record will first be compared with the + * latest state in the database. In order to compare this record with the + * latest state, the database record will be locked pessimistically using a * SELECT .. FOR UPDATE statement. Not all databases support * the FOR UPDATE clause natively. Namely, the following * databases will show slightly different behaviour: @@ -188,20 +157,15 @@ public interface TableRecord> extends Record { * *

    * See {@link LockProvider#setForUpdate(boolean)} for more details - *

    - * Unlike {@link #deleteUsing(TableField...)}, this will fail if several - * records are concerned. * * @param keys The key fields for the DELETE statement's * WHERE clause. * @return The number of deleted records. * @throws DataAccessException if something went wrong executing the query - * @throws DataChangedException if the record has already been changed in - * the database - * @see #deleteUsing(TableField...) - * @see LockProvider#setForUpdate(boolean) + * @throws DataChangedException If optimistic locking is enabled and the + * record has already been changed/deleted in the database */ - int deleteLockedUsing(TableField... keys) throws DataAccessException, DataChangedException; + int deleteUsing(TableField... keys) throws DataAccessException, DataChangedException; /** * Refresh this record from the database, based on the value of the provided diff --git a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java index 249c69ee8d..92b479716f 100644 --- a/jOOQ/src/main/java/org/jooq/UpdatableRecord.java +++ b/jOOQ/src/main/java/org/jooq/UpdatableRecord.java @@ -38,6 +38,7 @@ package org.jooq; import java.sql.ResultSet; import java.sql.Statement; +import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataChangedException; @@ -97,13 +98,33 @@ public interface UpdatableRecord> extends Updatable * record. *

  • If this record was loaded by jOOQ, and the primary key value was not * changed, an UPDATE statement is executed.
  • + *
  • If an UPDATE statement is executed and + * {@link Settings#isExecuteWithOptimisticLocking()} is set to + * true, then this record will first be compared with the + * latest state in the database. In order to compare this record with the + * latest state, the database record will be locked pessimistically using a + * SELECT .. FOR UPDATE statement. Not all databases support + * the FOR UPDATE clause natively. Namely, the following + * databases will show slightly different behaviour: + *
      + *
    • {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will + * try to lock the database record using JDBC's + * {@link ResultSet#TYPE_SCROLL_SENSITIVE} and + * {@link ResultSet#CONCUR_UPDATABLE}.
    • + *
    • {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client + * code must assure that no race-conditions can occur between jOOQ's + * checking of database record state and the actual UPDATE
    • *
    *

    - * In either statement, only those fields are inserted/updated, which had - * been explicitly set by client code, in order to allow for - * DEFAULT values to be applied by the underlying RDBMS. If no - * fields were modified, neither an UPDATE nor an - * INSERT will be executed. Possible statements are + * See {@link LockProvider#setForUpdate(boolean)} for more details

  • + * + *

    + * In either INSERT or UPDATE statement, only + * those fields are inserted/updated, which had been explicitly set by + * client code, in order to allow for DEFAULT values to be + * applied by the underlying RDBMS. If no fields were modified, neither an + * UPDATE nor an INSERT will be executed. Possible + * statements are *

      *
    • *
      @@ -132,44 +153,11 @@ public interface UpdatableRecord> extends Updatable
            * @return 1 if the record was stored to the database. 0
            *          if storing was not necessary.
            * @throws DataAccessException if something went wrong executing the query
      +     * @throws DataChangedException If optimistic locking is enabled and the
      +     *             record has already been changed/deleted in the database
            * @see #storeUsing(TableField...)
            */
      -    int store() throws DataAccessException;
      -
      -    /**
      -     * Store this record back to the database assuming an optimistic lock.
      -     * 

      - * This performs the same action as {@link #store()}, except that if an - * UPDATE is performed, this record will first be compared with - * the latest state in the database. - *

      - * Note that in order to compare this record with the latest state, the - * database record will be locked pessimistically using a - * SELECT .. FOR UPDATE statement. Not all databases support - * the FOR UPDATE clause natively. Namely, the following - * databases will show slightly different behaviour: - *

        - *
      • {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will - * try to lock the database record using JDBC's - * {@link ResultSet#TYPE_SCROLL_SENSITIVE} and - * {@link ResultSet#CONCUR_UPDATABLE}.
      • - *
      • {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client - * code must assure that no race-conditions can occur between jOOQ's - * checking of database record state and the actual UPDATE
      • - *
      - *

      - * See {@link LockProvider#setForUpdate(boolean)} for more details - * - * @return 1 if the record was stored to the database. 0 - * if storing was not necessary. - * @throws DataAccessException if something went wrong executing the query - * @throws DataChangedException if the record has already been - * changed/deleted in the database - * @see #store() - * @see #storeLockedUsing(TableField...) - * @see LockProvider#setForUpdate(boolean) - */ - int storeLocked() throws DataAccessException, DataChangedException; + int store() throws DataAccessException, DataChangedException; /** * Deletes this record from the database, based on the value of the primary @@ -179,24 +167,10 @@ public interface UpdatableRecord> extends Updatable * DELETE FROM [table] * WHERE [main key fields = main key values]

      *

      - * This is in fact the same as calling - * delete(getTable().getMainKey().getFieldsArray()) - * - * @return 1 if the record was deleted from the database. - * 0 if deletion was not necessary. - * @throws DataAccessException if something went wrong executing the query - * @see #deleteUsing(TableField...) - */ - int delete() throws DataAccessException; - - /** - * Deletes this record from the database assuming an optimistic lock. - *

      - * This performs the same action as {@link #delete()}, except that this - * record will first be compared with the latest state in the database. - *

      - * Note that in order to compare this record with the latest state, the - * database record will be locked pessimistically using a + * If {@link Settings#isExecuteWithOptimisticLocking()} is set to + * true, then this record will first be compared with the + * latest state in the database. In order to compare this record with the + * latest state, the database record will be locked pessimistically using a * SELECT .. FOR UPDATE statement. Not all databases support * the FOR UPDATE clause natively. Namely, the following * databases will show slightly different behaviour: @@ -211,17 +185,18 @@ public interface UpdatableRecord> extends Updatable *

    *

    * See {@link LockProvider#setForUpdate(boolean)} for more details + *

    + * This is in fact the same as calling + * delete(getTable().getMainKey().getFieldsArray()) * * @return 1 if the record was deleted from the database. * 0 if deletion was not necessary. * @throws DataAccessException if something went wrong executing the query - * @throws DataChangedException if the record has already been - * changed/deleted in the database - * @see #delete() + * @throws DataChangedException If optimistic locking is enabled and the + * record has already been changed/deleted in the database * @see #deleteUsing(TableField...) - * @see LockProvider#setForUpdate(boolean) */ - int deleteLocked() throws DataAccessException, DataChangedException; + int delete() throws DataAccessException, DataChangedException; /** * Refresh this record from the database, based on the value of the primary diff --git a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java index 48abd937f6..f4cc37c63d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java @@ -42,6 +42,7 @@ import java.util.Collection; import java.util.LinkedHashSet; import org.jooq.ConditionProvider; +import org.jooq.Configuration; import org.jooq.DeleteQuery; import org.jooq.Field; import org.jooq.Identity; @@ -95,15 +96,7 @@ public class TableRecordImpl> extends AbstractRecord im @Override public final int storeUsing(TableField... keys) { - return storeUsing0(keys, false); - } - - @Override - public final int storeLockedUsing(TableField... keys) { - return storeUsing0(keys, true); - } - - private final int storeUsing0(TableField[] keys, boolean checkIfChanged) { + boolean checkIfChanged = isExecuteWithOptimisticLocking(); boolean executeUpdate = false; for (TableField field : keys) { @@ -133,6 +126,16 @@ public class TableRecordImpl> extends AbstractRecord im return result; } + private final boolean isExecuteWithOptimisticLocking() { + Configuration configuration = getConfiguration(); + + if (configuration != null) { + return TRUE.equals(configuration.getSettings().isExecuteWithOptimisticLocking()); + } + + return false; + } + @SuppressWarnings("unchecked") private final int storeInsert() { Factory create = create(); @@ -239,15 +242,8 @@ public class TableRecordImpl> extends AbstractRecord im @Override public final int deleteUsing(TableField... keys) { - return deleteUsing0(keys, false); - } + boolean checkIfChanged = isExecuteWithOptimisticLocking(); - @Override - public final int deleteLockedUsing(TableField... keys) { - return deleteUsing0(keys, true); - } - - private int deleteUsing0(TableField[] keys, boolean checkIfChanged) { try { DeleteQuery delete = create().deleteQuery(getTable()); diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java index 924fa2b5fc..6e036f5c0f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdatableRecordImpl.java @@ -76,21 +76,11 @@ public class UpdatableRecordImpl> extends TableReco return storeUsing(getMainKey().getFieldsArray()); } - @Override - public final int storeLocked() { - return storeLockedUsing(getMainKey().getFieldsArray()); - } - @Override public final int delete() { return deleteUsing(getMainKey().getFieldsArray()); } - @Override - public final int deleteLocked() { - return deleteLockedUsing(getMainKey().getFieldsArray()); - } - @Override public final void refresh() { refreshUsing(getMainKey().getFieldsArray()); diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-2.5.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-2.5.0.xsd new file mode 100644 index 0000000000..80ac9733ad --- /dev/null +++ b/jOOQ/src/main/resources/xsd/jooq-runtime-2.5.0.xsd @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file