diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index 721552371e..eadc3a1b38 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -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. *

@@ -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())); diff --git a/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java b/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java index 3de56c3c0d..362fc001fc 100644 --- a/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java +++ b/jOOQ/src/main/java/org/jooq/conf/SettingsTools.java @@ -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()); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/BatchCRUD.java b/jOOQ/src/main/java/org/jooq/impl/BatchCRUD.java index 4a83e6cc25..40ec51a3aa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BatchCRUD.java +++ b/jOOQ/src/main/java/org/jooq/impl/BatchCRUD.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java index cd3dde5947..d8a06e26ae 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DAOImpl.java @@ -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, 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, 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, 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, P, T> implements DAO return result; } + + private final boolean returnAnyOnUpdatableRecord() { + return !FALSE.equals(settings().isReturnRecordToPojo()) + && SettingsTools.returnAnyOnUpdatableRecord(settings()); + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java index 875e13c5cc..d6fcc747cb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableRecordImpl.java @@ -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> extends AbstractQualified } final void getReturningIfNeeded(StoreQuery query, Collection> key) { - if (key != null && !key.isEmpty()) { + if (!isEmpty(key)) { R record = query.getReturnedRecord(); if (record != null) { @@ -223,7 +227,7 @@ public class TableRecordImpl> 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> extends AbstractQualified final Collection> setReturningIfNeeded(StoreQuery query) { Collection> 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> extends AbstractQualified || getTable().getRecordVersion() != null && isUpdateRecordVersion(); } - final Collection> getReturning() { + final Collection> getReturning(StoreQuery query) { + Settings s = configuration().settings(); + + // [#1859] Returning all columns if requested explicitly + if (TRUE.equals(s.isReturnAllOnUpdatableRecord())) + return asList(fields()); + Collection> result = new LinkedHashSet<>(); - Identity 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; } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 0ea8b5be85..4d75aebc76 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -2175,8 +2175,10 @@ final class Tools { return t == null ? s.get() : t; } - static final T let(T t, Consumer consumer) { - consumer.accept(t); + static final T let(@Nullable T t, Consumer consumer) { + if (t != null) + consumer.accept(t); + return t; } diff --git a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.18.0.xsd b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.18.0.xsd index e33a60f9b7..a8ab7be1a8 100644 --- a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.18.0.xsd +++ b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.18.0.xsd @@ -1305,6 +1305,14 @@ UpdatableRecord.store() and UpdatableRecord.update().]]> + + + + + + + +