[jOOQ/jOOQ#12494] Add additional Record.modified() methods

This commit is contained in:
Lukas Eder 2024-09-12 16:16:43 +02:00
parent dce730d397
commit 34b560bf82
3 changed files with 162 additions and 38 deletions

View File

@ -592,6 +592,9 @@ public interface Record extends Fields, Attachable, Comparable<Record>, Formatta
/**
* Check if this record has been touched since it was created or fetched
* from the database.
* <p>
* 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<Record>, 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.
* <p>
* 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<Record>, 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.
* <p>
* 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<Record>, 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.
* <p>
* 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<Record>, 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.
* <p>
* 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<Record>, Formatta
* <p>
* If the <code>touched</code> argument is <code>false</code>, the
* {@link #original()} values will be reset to the corresponding "current"
* values as well
* values as well.
* <p>
* 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<Record>, Formatta
* <p>
* If the <code>touched</code> argument is <code>false</code>, the
* {@link #original(Field)} value will be reset to the corresponding
* "current" value as well
* "current" value as well.
* <p>
* 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<Record>, Formatta
* <p>
* If the <code>touched</code> argument is <code>false</code>, the
* {@link #original(int)} value will be reset to the corresponding "current"
* value as well
* value as well.
* <p>
* 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<Record>, Formatta
* <p>
* If the <code>touched</code> argument is <code>false</code>, the
* {@link #touched(String)} value will be reset to the corresponding
* "current" value as well
* "current" value as well.
* <p>
* 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<Record>, Formatta
* <p>
* If the <code>touched</code> argument is <code>false</code>, the
* {@link #original(Name)} value will be reset to the corresponding
* "current" value as well
* "current" value as well.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <code>false</code>.

View File

@ -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();

View File

@ -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);
}
}