[jOOQ/jOOQ#14573] Add Settings.returnDefaultOnUpdatableRecord and Settings.returnComputedOnUpdatableRecord

This commit is contained in:
Lukas Eder 2023-02-15 11:43:39 +01:00
parent e14ebc33d8
commit b7a392066f
7 changed files with 159 additions and 32 deletions

View File

@ -383,6 +383,10 @@ public class Settings
@XmlElement(defaultValue = "true")
protected Boolean returnIdentityOnUpdatableRecord = true;
@XmlElement(defaultValue = "false")
protected Boolean returnDefaultOnUpdatableRecord = false;
@XmlElement(defaultValue = "false")
protected Boolean returnComputedOnUpdatableRecord = false;
@XmlElement(defaultValue = "false")
protected Boolean returnAllOnUpdatableRecord = false;
@XmlElement(defaultValue = "true")
protected Boolean returnRecordToPojo = true;
@ -4612,6 +4616,54 @@ public class Settings
this.returnIdentityOnUpdatableRecord = value;
}
/**
* Whether calls to store(), insert() and update() should return values for columns that are {@link org.jooq.DataType#defaulted()}.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isReturnDefaultOnUpdatableRecord() {
return returnDefaultOnUpdatableRecord;
}
/**
* Sets the value of the returnDefaultOnUpdatableRecord property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setReturnDefaultOnUpdatableRecord(Boolean value) {
this.returnDefaultOnUpdatableRecord = value;
}
/**
* Whether calls to store(), insert() and update() should return values for columns that are {@link org.jooq.DataType#computed()}.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isReturnComputedOnUpdatableRecord() {
return returnComputedOnUpdatableRecord;
}
/**
* Sets the value of the returnComputedOnUpdatableRecord property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setReturnComputedOnUpdatableRecord(Boolean value) {
this.returnComputedOnUpdatableRecord = value;
}
/**
* Whether calls to store(), insert() and update() should return all columns, not just identity columns.
* <p>
@ -6840,6 +6892,16 @@ public class Settings
return this;
}
public Settings withReturnDefaultOnUpdatableRecord(Boolean value) {
setReturnDefaultOnUpdatableRecord(value);
return this;
}
public Settings withReturnComputedOnUpdatableRecord(Boolean value) {
setReturnComputedOnUpdatableRecord(value);
return this;
}
public Settings withReturnAllOnUpdatableRecord(Boolean value) {
setReturnAllOnUpdatableRecord(value);
return this;
@ -7473,6 +7535,8 @@ public class Settings
builder.append("fetchWarnings", fetchWarnings);
builder.append("fetchServerOutputSize", fetchServerOutputSize);
builder.append("returnIdentityOnUpdatableRecord", returnIdentityOnUpdatableRecord);
builder.append("returnDefaultOnUpdatableRecord", returnDefaultOnUpdatableRecord);
builder.append("returnComputedOnUpdatableRecord", returnComputedOnUpdatableRecord);
builder.append("returnAllOnUpdatableRecord", returnAllOnUpdatableRecord);
builder.append("returnRecordToPojo", returnRecordToPojo);
builder.append("mapJPAAnnotations", mapJPAAnnotations);
@ -8938,6 +9002,24 @@ public class Settings
return false;
}
}
if (returnDefaultOnUpdatableRecord == null) {
if (other.returnDefaultOnUpdatableRecord!= null) {
return false;
}
} else {
if (!returnDefaultOnUpdatableRecord.equals(other.returnDefaultOnUpdatableRecord)) {
return false;
}
}
if (returnComputedOnUpdatableRecord == null) {
if (other.returnComputedOnUpdatableRecord!= null) {
return false;
}
} else {
if (!returnComputedOnUpdatableRecord.equals(other.returnComputedOnUpdatableRecord)) {
return false;
}
}
if (returnAllOnUpdatableRecord == null) {
if (other.returnAllOnUpdatableRecord!= null) {
return false;
@ -9621,6 +9703,8 @@ public class Settings
result = ((prime*result)+((fetchWarnings == null)? 0 :fetchWarnings.hashCode()));
result = ((prime*result)+((fetchServerOutputSize == null)? 0 :fetchServerOutputSize.hashCode()));
result = ((prime*result)+((returnIdentityOnUpdatableRecord == null)? 0 :returnIdentityOnUpdatableRecord.hashCode()));
result = ((prime*result)+((returnDefaultOnUpdatableRecord == null)? 0 :returnDefaultOnUpdatableRecord.hashCode()));
result = ((prime*result)+((returnComputedOnUpdatableRecord == null)? 0 :returnComputedOnUpdatableRecord.hashCode()));
result = ((prime*result)+((returnAllOnUpdatableRecord == null)? 0 :returnAllOnUpdatableRecord.hashCode()));
result = ((prime*result)+((returnRecordToPojo == null)? 0 :returnRecordToPojo.hashCode()));
result = ((prime*result)+((mapJPAAnnotations == null)? 0 :mapJPAAnnotations.hashCode()));

View File

@ -37,6 +37,8 @@
*/
package org.jooq.conf;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static org.jooq.conf.FetchIntermediateResult.WHEN_EXECUTE_LISTENERS_PRESENT;
import static org.jooq.conf.FetchIntermediateResult.WHEN_RESULT_REQUESTED;
import static org.jooq.conf.ParamType.INDEXED;
@ -54,6 +56,7 @@ import java.util.ArrayList;
import java.util.Locale;
import org.jooq.Configuration;
import org.jooq.UpdatableRecord;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jooq.util.jaxb.tools.MiniJAXB;
@ -465,4 +468,15 @@ public final class SettingsTools {
? settings.getFetchServerOutputSize()
: 0;
}
/**
* Whether any value should be returned on an {@link UpdatableRecord}
* operation.
*/
public static final boolean returnAnyOnUpdatableRecord(Settings settings) {
return !FALSE.equals(settings.isReturnIdentityOnUpdatableRecord())
|| TRUE.equals(settings.isReturnAllOnUpdatableRecord())
|| TRUE.equals(settings.isReturnDefaultOnUpdatableRecord())
|| TRUE.equals(settings.isReturnComputedOnUpdatableRecord());
}
}

View File

@ -95,8 +95,10 @@ final class BatchCRUD extends AbstractBatch {
// [#1529] Avoid DEBUG logging of single INSERT / UPDATE statements
.withExecuteLogging(false)
// [#3327] [#11509] We can't return generated keys from batches (yet)
// [#3327] [#11509] [#14573] We can't return generated keys from batches (yet)
.withReturnAllOnUpdatableRecord(false)
.withReturnDefaultOnUpdatableRecord(false)
.withReturnComputedOnUpdatableRecord(false)
.withReturnIdentityOnUpdatableRecord(false);
return local;

View File

@ -69,6 +69,7 @@ import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.UpdatableRecord;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
/**
* A common base implementation for generated {@link DAO}.
@ -198,8 +199,7 @@ public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO
if (objects.size() > 1)
// [#2536] [#3327] We cannot batch UPDATE RETURNING calls yet
if (!FALSE.equals(settings().isReturnRecordToPojo()) &&
TRUE.equals(settings().isReturnAllOnUpdatableRecord()))
if (returnAnyOnUpdatableRecord())
for (R record : records(objects, true))
record.update();
else
@ -228,8 +228,7 @@ public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO
if (objects.size() > 1)
// [#2536] [#3327] We cannot batch MERGE RETURNING calls yet
if (!FALSE.equals(settings().isReturnRecordToPojo()) &&
TRUE.equals(settings().isReturnAllOnUpdatableRecord()))
if (returnAnyOnUpdatableRecord())
for (R record : records(objects, false))
record.merge();
else
@ -258,8 +257,7 @@ public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO
if (objects.size() > 1)
// [#2536] [#3327] We cannot batch DELETE RETURNING calls yet
if (!FALSE.equals(settings().isReturnRecordToPojo()) &&
TRUE.equals(settings().isReturnAllOnUpdatableRecord()))
if (returnAnyOnUpdatableRecord())
for (R record : records(objects, true))
record.delete();
else
@ -510,4 +508,9 @@ public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO
return result;
}
private final boolean returnAnyOnUpdatableRecord() {
return !FALSE.equals(settings().isReturnRecordToPojo())
&& SettingsTools.returnAnyOnUpdatableRecord(settings());
}
}

View File

@ -49,6 +49,7 @@ import static org.jooq.SQLDialect.MARIADB;
import static org.jooq.SQLDialect.MYSQL;
// ...
// ...
import static org.jooq.conf.SettingsTools.returnAnyOnUpdatableRecord;
import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.conf.WriteIfReadonly.IGNORE;
import static org.jooq.conf.WriteIfReadonly.THROW;
@ -59,6 +60,8 @@ import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.collect;
import static org.jooq.impl.Tools.filter;
import static org.jooq.impl.Tools.indexOrFail;
import static org.jooq.impl.Tools.isEmpty;
import static org.jooq.impl.Tools.let;
import static org.jooq.impl.Tools.settings;
import static org.jooq.tools.StringUtils.defaultIfNull;
@ -90,6 +93,7 @@ import org.jooq.UniqueKey;
import org.jooq.UpdatableRecord;
import org.jooq.Update;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.conf.WriteIfReadonly;
import org.jooq.exception.DataTypeException;
import org.jooq.tools.JooqLogger;
@ -208,7 +212,7 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractQualified
}
final void getReturningIfNeeded(StoreQuery<R> query, Collection<Field<?>> key) {
if (key != null && !key.isEmpty()) {
if (!isEmpty(key)) {
R record = query.getReturnedRecord();
if (record != null) {
@ -223,7 +227,7 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractQualified
// [#1859] In some databases, not all fields can be fetched via getGeneratedKeys()
Configuration c = configuration();
if (TRUE.equals(c.settings().isReturnAllOnUpdatableRecord())
if (returnAnyOnUpdatableRecord(c.settings())
// [#11620] Refresh only if the RETURNING clause didn't run
// E.g. in MySQL when there was no identity column
@ -243,21 +247,12 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractQualified
final Collection<Field<?>> setReturningIfNeeded(StoreQuery<R> query) {
Collection<Field<?>> key = null;
if (configuration() != null)
if (configuration() != null && returnAnyOnUpdatableRecord(configuration().settings())) {
key = getReturning(query);
// [#7966] Allow users to turning off the returning clause entirely
if (!FALSE.equals(configuration().settings().isReturnIdentityOnUpdatableRecord()))
// [#1859] Return also non-key columns
if (TRUE.equals(configuration().settings().isReturnAllOnUpdatableRecord()))
key = Arrays.asList(fields());
// [#5940] Getting the primary key mostly doesn't make sense on UPDATE statements
else if (query instanceof InsertQuery || updatablePrimaryKeys(settings(this)))
key = getReturning();
if (key != null)
query.setReturning(key);
if (!isEmpty(key))
query.setReturning(key);
}
return key;
}
@ -430,16 +425,35 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractQualified
|| getTable().getRecordVersion() != null && isUpdateRecordVersion();
}
final Collection<Field<?>> getReturning() {
final Collection<Field<?>> getReturning(StoreQuery<R> query) {
Settings s = configuration().settings();
// [#1859] Returning all columns if requested explicitly
if (TRUE.equals(s.isReturnAllOnUpdatableRecord()))
return asList(fields());
Collection<Field<?>> result = new LinkedHashSet<>();
Identity<R, ?> identity = getTable().getIdentity();
if (identity != null)
result.add(identity.getField());
// [#7966] Allow users to turning off the returning clause entirely
if (!FALSE.equals(s.isReturnIdentityOnUpdatableRecord())
UniqueKey<?> key = getPrimaryKey();
if (key != null)
result.addAll(key.getFields());
// [#5940] Getting the primary key mostly doesn't make sense on UPDATE statements
&& (query instanceof InsertQuery || updatablePrimaryKeys(s))
) {
let(getTable().getIdentity(), i -> result.add(i.getField()));
let(getPrimaryKey(), k -> result.addAll(k.getFields()));
}
// [#14573] Return also non-key columns
if (TRUE.equals(s.isReturnDefaultOnUpdatableRecord()))
for (Field<?> f : fields())
if (f.getDataType().defaulted())
result.add(f);
if (TRUE.equals(s.isReturnComputedOnUpdatableRecord()))
for (Field<?> f : fields())
if (f.getDataType().computed())
result.add(f);
return result;
}

View File

@ -2175,8 +2175,10 @@ final class Tools {
return t == null ? s.get() : t;
}
static final <T> T let(T t, Consumer<? super T> consumer) {
consumer.accept(t);
static final <T> T let(@Nullable T t, Consumer<? super @NotNull T> consumer) {
if (t != null)
consumer.accept(t);
return t;
}

View File

@ -1305,6 +1305,14 @@ UpdatableRecord.store() and UpdatableRecord.update().]]></jxb:javadoc></jxb:prop
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether calls to store(), insert() and update() should return the identity column.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="returnDefaultOnUpdatableRecord" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether calls to store(), insert() and update() should return values for columns that are {@link org.jooq.DataType#defaulted()}.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="returnComputedOnUpdatableRecord" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether calls to store(), insert() and update() should return values for columns that are {@link org.jooq.DataType#computed()}.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="returnAllOnUpdatableRecord" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether calls to store(), insert() and update() should return all columns, not just identity columns.
<p>