diff --git a/jOOQ/src/main/java/org/jooq/Record.java b/jOOQ/src/main/java/org/jooq/Record.java index 6e121e2298..47af0159d6 100644 --- a/jOOQ/src/main/java/org/jooq/Record.java +++ b/jOOQ/src/main/java/org/jooq/Record.java @@ -592,6 +592,9 @@ public interface Record extends Fields, Attachable, Comparable, Formatta /** * Check if this record has been touched since it was created or fetched * from the database. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #original() * @see #touched(Field) @@ -603,6 +606,9 @@ public interface Record extends Fields, Attachable, Comparable, Formatta /** * Check if a field's value has been touched since the record was created or * fetched from the database, using {@link #field(Field)} for lookup. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #original(Field) @@ -612,6 +618,9 @@ public interface Record extends Fields, Attachable, Comparable, Formatta /** * Check if a field's value has been touched since the record was created or * fetched from the database, using {@link #field(int)} for lookup. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @param fieldIndex The 0-based field index in this record. * @see #touched() @@ -622,6 +631,9 @@ public interface Record extends Fields, Attachable, Comparable, Formatta /** * Check if a field's value has been touched since the record was created or * fetched from the database, using {@link #field(String)} for lookup. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #original(String) @@ -631,6 +643,9 @@ public interface Record extends Fields, Attachable, Comparable, Formatta /** * Check if a field's value has been touched since the record was created or * fetched from the database, using {@link #field(Name)} for lookup. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #original(Name) @@ -642,7 +657,10 @@ public interface Record extends Fields, Attachable, Comparable, Formatta *

* If the touched argument is false, the * {@link #original()} values will be reset to the corresponding "current" - * values as well + * values as well. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #touched(Field, boolean) @@ -657,7 +675,10 @@ public interface Record extends Fields, Attachable, Comparable, Formatta *

* If the touched argument is false, the * {@link #original(Field)} value will be reset to the corresponding - * "current" value as well + * "current" value as well. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #touched(Field) @@ -670,7 +691,10 @@ public interface Record extends Fields, Attachable, Comparable, Formatta *

* If the touched argument is false, the * {@link #original(int)} value will be reset to the corresponding "current" - * value as well + * value as well. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @param fieldIndex The 0-based field index in this record. * @see #touched() @@ -684,7 +708,10 @@ public interface Record extends Fields, Attachable, Comparable, Formatta *

* If the touched argument is false, the * {@link #touched(String)} value will be reset to the corresponding - * "current" value as well + * "current" value as well. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #touched(String) @@ -697,13 +724,79 @@ public interface Record extends Fields, Attachable, Comparable, Formatta *

* If the touched argument is false, the * {@link #original(Name)} value will be reset to the corresponding - * "current" value as well + * "current" value as well. + *

+ * A record may have been {@link #touched()} (a setter was called) without + * having been {@link #modified()} (a value was changed). * * @see #touched() * @see #touched(Name) */ void touched(Name fieldName, boolean touched); + /** + * Check if this record has been modified since it was created or fetched + * from the database. + *

+ * When a record is {@link #modified()}, then it has always been + * {@link #touched()} as well. + * + * @see #original() + * @see #modified(Field) + * @see #modified(int) + * @see #modified(String) + */ + boolean modified(); + + /** + * Check if a field's value has been modified since the record was created or + * fetched from the database, using {@link #field(Field)} for lookup. + *

+ * When a record is {@link #modified()}, then it has always been + * {@link #touched()} as well. + * + * @see #modified() + * @see #original(Field) + */ + boolean modified(Field field); + + /** + * Check if a field's value has been modified since the record was created or + * fetched from the database, using {@link #field(int)} for lookup. + *

+ * When a record is {@link #modified()}, then it has always been + * {@link #touched()} as well. + * + * @param fieldIndex The 0-based field index in this record. + * @see #modified() + * @see #original(int) + */ + boolean modified(int fieldIndex); + + /** + * Check if a field's value has been modified since the record was created or + * fetched from the database, using {@link #field(String)} for lookup. + *

+ * When a record is {@link #modified()}, then it has always been + * {@link #touched()} as well. + * + * @see #modified() + * @see #original(String) + */ + boolean modified(String fieldName); + + /** + * Check if a field's value has been modified since the record was created or + * fetched from the database, using {@link #field(Name)} for lookup. + *

+ * When a record is {@link #modified()}, then it has always been + * {@link #touched()} as well. + * + * @see #modified() + * @see #original(Name) + */ + boolean modified(Name fieldName); + /** * Reset all values to their {@link #original()} values and all * {@link #touched()} flags to false. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java index 301eff0cbc..c7237693e6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRecord.java @@ -552,6 +552,37 @@ implements touched(indexOrFail(fields, fieldName), c); } + @Override + public final boolean modified() { + for (int i = 0; i < size(); i++) + if (modified(i)) + return true; + + + return false; + } + + @Override + public final boolean modified(Field field) { + return modified(indexOrFail(fields, field)); + } + + @Override + public final boolean modified(int fieldIndex) { + int i = safeIndex(fieldIndex); + return touched.get(i) && !deepEqual(values[i], originals[i]); + } + + @Override + public final boolean modified(String fieldName) { + return modified(indexOrFail(fields, fieldName)); + } + + @Override + public final boolean modified(Name fieldName) { + return modified(indexOrFail(fields, fieldName)); + } + @Override public final void reset() { touched.clear(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java index be7eda3c66..cc37588d9e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStore.java @@ -139,41 +139,10 @@ abstract class AbstractStore extends AbstractFormattable { // Note: keep this implementation in-sync with AbstractRecord.compareTo()! if (obj instanceof AbstractStore that) { if (size() == that.size()) { - for (int i = 0; i < size(); i++) { - final Object thisValue = get(i); - final Object thatValue = that.get(i); - - // [#1850] Only return false early. In all other cases, - // continue checking the remaining fields - if (thisValue == null && thatValue == null) - continue; - - else if (thisValue == null || thatValue == null) + for (int i = 0; i < size(); i++) + if (!deepEqual(get(i), that.get(i))) return false; - // [#985] Compare arrays too. - else if (thisValue.getClass().isArray() && thatValue.getClass().isArray()) { - - // Might be byte[] - if (thisValue.getClass() == byte[].class && thatValue.getClass() == byte[].class) { - if (!Arrays.equals((byte[]) thisValue, (byte[]) thatValue)) - return false; - } - - // Other primitive types are not expected - else if (!thisValue.getClass().getComponentType().isPrimitive() && - !thatValue.getClass().getComponentType().isPrimitive()) { - if (!Arrays.deepEquals((Object[]) thisValue, (Object[]) thatValue)) - return false; - } - - else - return false; - } - else if (!thisValue.equals(thatValue)) - return false; - } - // If we got through the above loop, the two records are equal return true; } @@ -181,4 +150,35 @@ abstract class AbstractStore extends AbstractFormattable { return false; } + + static final boolean deepEqual(Object thisValue, Object thatValue) { + + // [#1850] Only return false early. In all other cases, + // continue checking the remaining fields + if (thisValue == null && thatValue == null) + return true; + + else if (thisValue == null || thatValue == null) + return false; + + // [#985] Compare arrays too. + else if (thisValue.getClass().isArray() && thatValue.getClass().isArray()) { + + // Might be byte[] + if (thisValue.getClass() == byte[].class && thatValue.getClass() == byte[].class) { + return Arrays.equals((byte[]) thisValue, (byte[]) thatValue); + } + + // Other primitive types are not expected + else if (!thisValue.getClass().getComponentType().isPrimitive() && + !thatValue.getClass().getComponentType().isPrimitive()) { + return Arrays.deepEquals((Object[]) thisValue, (Object[]) thatValue); + } + + else + return false; + } + else + return thisValue.equals(thatValue); + } }