diff --git a/jOOQ-beans-extensions/.gitignore b/jOOQ-beans-extensions/.gitignore
new file mode 100644
index 0000000000..4b664f2ce0
--- /dev/null
+++ b/jOOQ-beans-extensions/.gitignore
@@ -0,0 +1,3 @@
+/target
+/.idea
+/*.iml
diff --git a/jOOQ-beans-extensions/LICENSE.txt b/jOOQ-beans-extensions/LICENSE.txt
new file mode 100644
index 0000000000..84f48a6df9
--- /dev/null
+++ b/jOOQ-beans-extensions/LICENSE.txt
@@ -0,0 +1,19 @@
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Other licenses:
+-----------------------------------------------------------------------------
+Commercial licenses for this work are available. These replace the above
+ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+database integrations.
+
+For more information, please visit: https://www.jooq.org/legal/licensing
\ No newline at end of file
diff --git a/jOOQ-beans-extensions/NOTICE.txt b/jOOQ-beans-extensions/NOTICE.txt
new file mode 100644
index 0000000000..698edf47ee
--- /dev/null
+++ b/jOOQ-beans-extensions/NOTICE.txt
@@ -0,0 +1,10 @@
+Third party NOTICE.txt contents
+===============================
+
+Contents of https://github.com/apache/commons-lang/blob/master/NOTICE.txt
+-------------------------------------------------------------------------
+Apache Commons Lang
+Copyright 2001-2019 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
\ No newline at end of file
diff --git a/jOOQ-beans-extensions/pom.xml b/jOOQ-beans-extensions/pom.xml
new file mode 100644
index 0000000000..1a42baae23
--- /dev/null
+++ b/jOOQ-beans-extensions/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+
+ org.jooq
+ jooq-parent
+ 3.20.0-SNAPSHOT
+
+
+ jooq-beans-extensions
+ jOOQ Beans Extensions Beans
+
+
+
+ Apache License, Version 2.0
+ https://www.jooq.org/inc/LICENSE.txt
+ repo
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.jooq.beans.extensions
+
+
+
+
+
+
+
+
+
+ org.jooq
+ jooq
+
+
+ org.jetbrains
+ annotations
+ provided
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jOOQ-beans-extensions/src/main/java/module-info.java b/jOOQ-beans-extensions/src/main/java/module-info.java
new file mode 100644
index 0000000000..a251760033
--- /dev/null
+++ b/jOOQ-beans-extensions/src/main/java/module-info.java
@@ -0,0 +1,15 @@
+/**
+ * The jOOQ beans extensions module.
+ */
+module org.jooq.beans.extensions {
+
+ // Other jOOQ modules
+ requires transitive org.jooq;
+
+ // Nullability annotations for better Kotlin interop
+ requires static org.jetbrains.annotations;
+
+ // The DefaultRecordMapper makes use of JavaBeans utilities, including:
+ // - Support for ConstructorProperties
+ requires transitive java.desktop;
+}
diff --git a/jOOQ-beans-extensions/src/main/java/org/jooq/beans/extensions/DefaultConstructorPropertiesProvider.java b/jOOQ-beans-extensions/src/main/java/org/jooq/beans/extensions/DefaultConstructorPropertiesProvider.java
new file mode 100644
index 0000000000..4ed796f2ff
--- /dev/null
+++ b/jOOQ-beans-extensions/src/main/java/org/jooq/beans/extensions/DefaultConstructorPropertiesProvider.java
@@ -0,0 +1,22 @@
+package org.jooq.beans.extensions;
+
+import java.beans.ConstructorProperties;
+import java.lang.reflect.Constructor;
+
+import org.jooq.ConstructorPropertiesProvider;
+
+/**
+ * The default {@link ConstructorPropertiesProvider} implementation that looks
+ * up constructor properties from {@link ConstructorProperties}.
+ *
+ * @author Lukas Eder
+ */
+public class DefaultConstructorPropertiesProvider implements ConstructorPropertiesProvider {
+
+ @Override
+ public String[] properties(Constructor> constructor) {
+ ConstructorProperties cp = constructor.getAnnotation(ConstructorProperties.class);
+
+ return cp != null ? cp.value() : null;
+ }
+}
diff --git a/jOOQ-beans-extensions/src/main/resources/META-INF/LICENSE.txt b/jOOQ-beans-extensions/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 0000000000..84f48a6df9
--- /dev/null
+++ b/jOOQ-beans-extensions/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,19 @@
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Other licenses:
+-----------------------------------------------------------------------------
+Commercial licenses for this work are available. These replace the above
+ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+database integrations.
+
+For more information, please visit: https://www.jooq.org/legal/licensing
\ No newline at end of file
diff --git a/jOOQ-beans-extensions/src/main/resources/META-INF/README.txt b/jOOQ-beans-extensions/src/main/resources/META-INF/README.txt
new file mode 100644
index 0000000000..3e6e227e05
--- /dev/null
+++ b/jOOQ-beans-extensions/src/main/resources/META-INF/README.txt
@@ -0,0 +1,2 @@
+Thanks for downloading jOOQ.
+Please visit http://www.jooq.org for more information.
\ No newline at end of file
diff --git a/jOOQ/src/main/java/module-info.java b/jOOQ/src/main/java/module-info.java
index 9ead22c4a9..25bc39afb6 100644
--- a/jOOQ/src/main/java/module-info.java
+++ b/jOOQ/src/main/java/module-info.java
@@ -14,10 +14,6 @@ module org.jooq {
// - InformationSchema (org.jooq.util.xml.jaxb)
requires static jakarta.xml.bind;
- // The DefaultRecordMapper makes use of JavaBeans utilities, including:
- // - Support for ConstructorProperties
- requires static java.desktop;
-
// Various utilities can make use of JPA annotations, when present, including:
// - The DefaultRecordMapper
// - The JPADatabase in the code generator
diff --git a/jOOQ/src/main/java/org/jooq/Configuration.java b/jOOQ/src/main/java/org/jooq/Configuration.java
index d8f00bace8..cb2b56a716 100644
--- a/jOOQ/src/main/java/org/jooq/Configuration.java
+++ b/jOOQ/src/main/java/org/jooq/Configuration.java
@@ -414,6 +414,12 @@ public interface Configuration extends Serializable {
@NotNull
CharsetProvider charsetProvider();
+ /**
+ * Get this configuration's underlying constructor properties provider.
+ */
+ @NotNull
+ ConstructorPropertiesProvider constructorPropertiesProvider();
+
/**
* Get this configuration's underlying record mapper provider.
*/
@@ -760,6 +766,19 @@ public interface Configuration extends Serializable {
@NotNull
Configuration set(TransactionProvider newTransactionProvider);
+ /**
+ * Change this configuration to hold a new constructor properties provider.
+ *
+ * This method is not thread-safe and should not be used in globally
+ * available Configuration objects.
+ *
+ * @param newConstructorPropertiesProvider The new constructor properties
+ * provider to be contained in the changed configuration.
+ * @return The changed configuration.
+ */
+ @NotNull
+ Configuration set(ConstructorPropertiesProvider newConstructorPropertiesProvider);
+
/**
* Change this configuration to hold a new record mapper.
*
@@ -1500,6 +1519,17 @@ public interface Configuration extends Serializable {
@NotNull
Configuration derive(TransactionProvider newTransactionProvider);
+ /**
+ * Create a derived configuration from this one, with a new constructor
+ * properties provider.
+ *
+ * @param newConstructorPropertiesProvider The new constructor properties
+ * provider to be contained in the derived configuration.
+ * @return The derived configuration.
+ */
+ @NotNull
+ Configuration derive(ConstructorPropertiesProvider newConstructorPropertiesProvider);
+
/**
* Create a derived configuration from this one, with a new record mapper.
*
diff --git a/jOOQ/src/main/java/org/jooq/ConstructorPropertiesProvider.java b/jOOQ/src/main/java/org/jooq/ConstructorPropertiesProvider.java
new file mode 100644
index 0000000000..b4f504630b
--- /dev/null
+++ b/jOOQ/src/main/java/org/jooq/ConstructorPropertiesProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Other licenses:
+ * -----------------------------------------------------------------------------
+ * Commercial licenses for this work are available. These replace the above
+ * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+ * database integrations.
+ *
+ * For more information, please visit: https://www.jooq.org/legal/licensing
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+package org.jooq;
+
+import java.lang.reflect.Constructor;
+
+import org.jooq.impl.DefaultRecordMapper;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * An SPI that can be used to provider java.beans style constructor
+ * properties to the {@link DefaultRecordMapper}.
+ *
+ * Starting with jOOQ 3.20 and https://github.com/jOOQ/jOOQ/issues/7585,
+ * the {@link java.beans.ConstructorProperties} annotation support has been
+ * moved out of the core library to the
+ * jOOQ-mapper-extensions-beans module, to allow for:
+ *
+ *
+ * - Removal of the dependency on the
java.beans JDK module from
+ * the core library
+ * - Maintenance of backwards compatibility for those users using the
+ * annotation
+ *
+ *
+ * Implementations can be provided to
+ *
+ * @author Lukas Eder
+ */
+@FunctionalInterface
+public interface ConstructorPropertiesProvider {
+
+ /**
+ * Provide the constructor properties on the argument constructor, like
+ * {@link java.beans.ConstructorProperties#value()}.
+ *
+ *
+ * @return The constructor properties, if applicable, or null
+ * if the argument {@link Constructor} does not expose any
+ * constructor properties.
+ */
+ @Nullable
+ public String @Nullable [] properties(Constructor> constructor);
+}
diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java
index dda3d3e3f7..2d73a21b13 100644
--- a/jOOQ/src/main/java/org/jooq/conf/Settings.java
+++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java
@@ -5578,7 +5578,7 @@ public class Settings
}
/**
- * Whether constructor parameter names obtained from the {@link java.beans.ConstructorProperties} annotation should be considered by the DefaultRecordMapper.
+ * Whether constructor parameter names obtained from the {@link org.jooq.ConstructorPropertiesProvider} SPI (default implementation in the jOOQ-beans-extensions module) should be considered by the DefaultRecordMapper.
*
* @return
* possible object is
@@ -5590,7 +5590,7 @@ public class Settings
}
/**
- * Whether constructor parameter names obtained from the {@link java.beans.ConstructorProperties} annotation should be considered by the DefaultRecordMapper.
+ * Whether constructor parameter names obtained from the {@link org.jooq.ConstructorPropertiesProvider} SPI (default implementation in the jOOQ-beans-extensions module) should be considered by the DefaultRecordMapper.
*
* @param value
* allowed object is
@@ -8981,7 +8981,7 @@ public class Settings
}
/**
- * Whether constructor parameter names obtained from the {@link java.beans.ConstructorProperties} annotation should be considered by the DefaultRecordMapper.
+ * Whether constructor parameter names obtained from the {@link org.jooq.ConstructorPropertiesProvider} SPI (default implementation in the jOOQ-beans-extensions module) should be considered by the DefaultRecordMapper.
*
*/
public Settings withMapConstructorPropertiesParameterNames(Boolean value) {
diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/AbstractConfiguration.java
index 921ad81e40..2df304a60f 100644
--- a/jOOQ/src/main/java/org/jooq/impl/AbstractConfiguration.java
+++ b/jOOQ/src/main/java/org/jooq/impl/AbstractConfiguration.java
@@ -68,7 +68,8 @@ import org.jooq.tools.JooqLogger;
* @author Lukas Eder
*/
public abstract class AbstractConfiguration implements Configuration {
- private static final JooqLogger log = JooqLogger.getLogger(AbstractConfiguration.class);
+
+ private static final JooqLogger log = JooqLogger.getLogger(AbstractConfiguration.class);
@Override
public final Configuration set(RecordListener... newRecordListeners) {
diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java b/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java
index 4301ccb775..232ceb2d12 100644
--- a/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java
+++ b/jOOQ/src/main/java/org/jooq/impl/AbstractXMLasObjectBinding.java
@@ -37,8 +37,6 @@
*/
package org.jooq.impl;
-import static java.beans.Introspector.decapitalize;
-
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -58,6 +56,7 @@ import javax.xml.namespace.QName;
import org.jooq.Converter;
import org.jooq.ConverterContext;
import org.jooq.XML;
+import org.jooq.tools.StringUtils;
/**
* A binding that binds JAXB-annotated {@link Object} types to {@link SQLXML}
@@ -120,7 +119,7 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding {
Object o = u;
if (root == null)
- o = new JAXBElement<>(new QName(decapitalize(toType().getSimpleName())), toType(), u);
+ o = new JAXBElement<>(new QName(toLCNoAcronyms(toType().getSimpleName())), toType(), u);
Marshaller m = ctx.createMarshaller();
m.setProperty(Marshaller.JAXB_FRAGMENT, true);
@@ -132,6 +131,17 @@ public class AbstractXMLasObjectBinding extends AbstractXMLBinding {
}
}
+ private static final String toLCNoAcronyms(String s) {
+ if (StringUtils.isEmpty(s))
+ return s;
+
+ // [#7585] See also java.beans.Introspector::decapitalize
+ else if (s.length() > 1 && Character.isUpperCase(s.charAt(0)) && Character.isUpperCase(s.charAt(1)))
+ return s;
+ else
+ return StringUtils.toLC(s);
+ }
+
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java
index e43873bce2..47149691b9 100644
--- a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java
+++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java
@@ -60,6 +60,7 @@ import org.jooq.CharsetProvider;
import org.jooq.CommitProvider;
import org.jooq.Configuration;
import org.jooq.ConnectionProvider;
+import org.jooq.ConstructorPropertiesProvider;
import org.jooq.ConverterProvider;
import org.jooq.DSLContext;
import org.jooq.DiagnosticsListener;
@@ -126,6 +127,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
private transient ExecutorProvider executorProvider;
private transient CacheProvider cacheProvider;
private transient TransactionProvider transactionProvider;
+ private transient ConstructorPropertiesProvider constructorPropertiesProvider;
private transient RecordMapperProvider recordMapperProvider;
private transient RecordUnmapperProvider recordUnmapperProvider;
private transient RecordListenerProvider[] recordListenerProviders;
@@ -205,6 +207,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
null,
null,
null,
+ null,
@@ -237,6 +240,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
configuration.executorProvider,
configuration.cacheProvider,
configuration.transactionProvider,
+ configuration.constructorPropertiesProvider,
configuration.recordMapperProvider,
configuration.recordUnmapperProvider,
configuration.recordListenerProviders,
@@ -280,6 +284,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
ExecutorProvider executorProvider,
CacheProvider cacheProvider,
TransactionProvider transactionProvider,
+ ConstructorPropertiesProvider constructorPropertiesProvider,
RecordMapperProvider recordMapperProvider,
RecordUnmapperProvider recordUnmapperProvider,
RecordListenerProvider[] recordListenerProviders,
@@ -312,6 +317,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
set(executorProvider);
set(cacheProvider);
set(transactionProvider);
+ set(constructorPropertiesProvider);
set(recordMapperProvider);
set(recordUnmapperProvider);
set(recordListenerProviders);
@@ -379,6 +385,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -416,6 +423,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -453,6 +461,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -490,6 +499,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -532,6 +542,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
newExecutorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -569,6 +580,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
newCacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -606,6 +618,45 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
newTransactionProvider,
+ constructorPropertiesProvider,
+ recordMapperProvider,
+ recordUnmapperProvider,
+ recordListenerProviders,
+ executeListenerProviders,
+ migrationListenerProviders,
+ visitListenerProviders,
+ transactionListenerProviders,
+ diagnosticsListenerProviders,
+ unwrapperProvider,
+ charsetProvider,
+ converterProvider,
+ formattingProvider,
+
+
+
+
+
+
+ clock,
+ dialect,
+ settings,
+ data
+ );
+ }
+
+ @Override
+ public final Configuration derive(ConstructorPropertiesProvider newConstructorPropertiesProvider) {
+ return new DefaultConfiguration(
+ connectionProvider,
+ interpreterConnectionProvider,
+ systemConnectionProvider,
+ connectionFactory,
+ metaProvider,
+ commitProvider,
+ executorProvider,
+ cacheProvider,
+ transactionProvider,
+ newConstructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -648,6 +699,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
newRecordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -690,6 +742,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
newRecordUnmapperProvider,
recordListenerProviders,
@@ -727,6 +780,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
newRecordListenerProviders,
@@ -764,6 +818,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -801,6 +856,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -838,6 +894,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -875,6 +932,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -912,6 +970,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -954,6 +1013,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -991,6 +1051,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1028,6 +1089,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1065,6 +1127,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1233,6 +1296,10 @@ public class DefaultConfiguration extends AbstractConfiguration {
+
+
+
+
@@ -1250,6 +1317,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1287,6 +1355,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1324,6 +1393,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
executorProvider,
cacheProvider,
transactionProvider,
+ constructorPropertiesProvider,
recordMapperProvider,
recordUnmapperProvider,
recordListenerProviders,
@@ -1436,6 +1506,12 @@ public class DefaultConfiguration extends AbstractConfiguration {
return this;
}
+ @Override
+ public final Configuration set(ConstructorPropertiesProvider newConstructorPropertiesProvider) {
+ this.constructorPropertiesProvider = newConstructorPropertiesProvider;
+ return this;
+ }
+
@Override
public final Configuration set(RecordMapper, ?> newRecordMapper) {
return set(new RecordMapperWrapper(newRecordMapper));
@@ -1696,6 +1772,13 @@ public class DefaultConfiguration extends AbstractConfiguration {
set(newTransactionProvider);
}
+ /**
+ * @see #set(ConstructorPropertiesProvider)
+ */
+ public final void setConstructorPropertiesProvider(ConstructorPropertiesProvider newConstructorPropertiesProvider) {
+ set(newConstructorPropertiesProvider);
+ }
+
/**
* @see #set(RecordMapper)
*/
@@ -1954,6 +2037,13 @@ public class DefaultConfiguration extends AbstractConfiguration {
return transactionProvider;
}
+ @Override
+ public final ConstructorPropertiesProvider constructorPropertiesProvider() {
+ return constructorPropertiesProvider != null
+ ? constructorPropertiesProvider
+ : new LegacyConstructorPropertiesProvider();
+ }
+
@Override
public final RecordMapperProvider recordMapperProvider() {
@@ -2133,6 +2223,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
oos.writeObject(serializableOrNull(metaProvider));
oos.writeObject(serializableOrNull(commitProvider));
oos.writeObject(serializableOrNull(transactionProvider));
+ oos.writeObject(serializableOrNull(constructorPropertiesProvider));
oos.writeObject(serializableOrNull(recordMapperProvider));
oos.writeObject(serializableOrNull(recordUnmapperProvider));
oos.writeObject(cloneSerializables(executeListenerProviders));
@@ -2189,6 +2280,7 @@ public class DefaultConfiguration extends AbstractConfiguration {
metaProvider = (MetaProvider) ois.readObject();
commitProvider = (CommitProvider) ois.readObject();
transactionProvider = (TransactionProvider) ois.readObject();
+ constructorPropertiesProvider = (ConstructorPropertiesProvider) ois.readObject();
recordMapperProvider = (RecordMapperProvider) ois.readObject();
recordUnmapperProvider = (RecordUnmapperProvider) ois.readObject();
executeListenerProviders = (ExecuteListenerProvider[]) ois.readObject();
diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java
index f6c1faefe5..c54197fbf7 100644
--- a/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java
+++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRecordMapper.java
@@ -52,19 +52,14 @@ import static org.jooq.impl.Tools.getMatchingMembers;
import static org.jooq.impl.Tools.getMatchingSetters;
import static org.jooq.impl.Tools.getPropertyName;
import static org.jooq.impl.Tools.hasColumnAnnotations;
-import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.newRecord;
import static org.jooq.impl.Tools.recordType;
import static org.jooq.impl.Tools.row0;
import static org.jooq.tools.reflect.Reflect.accessible;
-import java.beans.ConstructorProperties;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -73,7 +68,6 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.Type;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -84,23 +78,21 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import jakarta.persistence.Column;
-
import org.jooq.Attachable;
import org.jooq.Configuration;
+import org.jooq.ConstructorPropertiesProvider;
import org.jooq.Converter;
import org.jooq.ConverterProvider;
import org.jooq.Field;
import org.jooq.JSON;
import org.jooq.JSONB;
import org.jooq.Record;
-import org.jooq.Record1;
import org.jooq.RecordMapper;
import org.jooq.RecordMapperProvider;
import org.jooq.RecordType;
@@ -115,6 +107,8 @@ import org.jooq.tools.StringUtils;
import org.jooq.tools.reflect.Reflect;
import org.jooq.tools.reflect.ReflectException;
+import jakarta.persistence.Column;
+
/**
* This is the default implementation for RecordMapper types, which
* applies to {@link Record#into(Class)}, {@link Result#into(Class)}, and
@@ -224,8 +218,11 @@ import org.jooq.tools.reflect.ReflectException;
* used
*
*
- * - The standard JavaBeans {@link ConstructorProperties} annotation is used
- * to match constructor arguments against POJO members or getters.
+ * - The standard JavaBeans {@link java.beans.ConstructorProperties}
+ * annotation is used to match constructor arguments against POJO members or
+ * getters, if the default {@link ConstructorPropertiesProvider} can look up the
+ * implementation from the
jOOQ-mapper-extensions-beans module, or
+ * if you provide your own.
* - If the property names provided to the constructor match the record's
* columns via the aforementioned naming conventions, that information is used.
*
@@ -440,11 +437,13 @@ public class DefaultRecordMapper implements RecordMapper constructor : constructors) {
- ConstructorProperties properties = constructor.getAnnotation(ConstructorProperties.class);
+ String[] properties = cpp.properties(constructor);
if (properties != null) {
- delegate = new ImmutablePOJOMapper(constructor, constructor.getParameterTypes(), Arrays.asList(properties.value()), true);
+ delegate = new ImmutablePOJOMapper(constructor, constructor.getParameterTypes(), Arrays.asList(properties), true);
return;
}
}
@@ -635,7 +634,7 @@ public class DefaultRecordMapper implements RecordMapper collectParameterNames(Parameter[] parameters) {
+ private static final List collectParameterNames(Parameter[] parameters) {
return Arrays.stream(parameters).map(Parameter::getName).collect(Collectors.toList());
}
@@ -1211,7 +1210,7 @@ public class DefaultRecordMapper implements RecordMapper E attach(E attachable, Record record) {
+ private static final E attach(E attachable, Record record) {
// [#2869] Attach the mapped outcome if it is Attachable and if the context's
// Settings.attachRecords flag is set
if (attachable instanceof Attachable a)
diff --git a/jOOQ/src/main/java/org/jooq/impl/LegacyConstructorPropertiesProvider.java b/jOOQ/src/main/java/org/jooq/impl/LegacyConstructorPropertiesProvider.java
new file mode 100644
index 0000000000..a96571baa4
--- /dev/null
+++ b/jOOQ/src/main/java/org/jooq/impl/LegacyConstructorPropertiesProvider.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Other licenses:
+ * -----------------------------------------------------------------------------
+ * Commercial licenses for this work are available. These replace the above
+ * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+ * database integrations.
+ *
+ * For more information, please visit: https://www.jooq.org/legal/licensing
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+package org.jooq.impl;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.jooq.ConstructorPropertiesProvider;
+import org.jooq.tools.JooqLogger;
+
+/**
+ * Class maintaining backwards compatible behaviour, without formal declaration
+ * of java.beans dependency in module info.
+ *
+ * @author Lukas Eder
+ */
+@SuppressWarnings("unchecked")
+final class LegacyConstructorPropertiesProvider implements ConstructorPropertiesProvider {
+
+ static final JooqLogger log = JooqLogger.getLogger(LegacyConstructorPropertiesProvider.class, 1);
+ static final ConstructorPropertiesProvider DELEGATE;
+ static final Class P;
+ static final Method V;
+
+ static {
+ Class p;
+ Method v;
+
+ try {
+
+ // [#14180] Break the maven-bundle-plugin class analyser, to prevent
+ // adding a package import to MANIFEST.MF for this lookup
+ p = (Class) Class.forName(new String("java.beans.") + new String("ConstructorProperties"));
+ v = p.getMethod("value");
+ }
+ catch (Exception e) {
+ p = null;
+ v = null;
+ }
+
+ P = p;
+ V = v;
+
+ DELEGATE = c -> {
+ if (V != null) {
+ Annotation a = c.getAnnotation(P);
+
+ try {
+ if (a != null) {
+ log.warn("ConstructorProperties", """
+ No explicit ConstructorPropertiesProvider configuration present.
+
+ ConstructorProperties annotation is present on POJO {pojo}
+ without any explicit ConstructorPropertiesProvider configuration.
+
+ Starting from jOOQ 3.20, the Configuration.constructorPropertiesProvider() SPI is required for the
+ DefaultRecordMapper to map a Record to a POJO that is annotated with ConstructorProperties. For
+ backwards compatibility, This LegacyConstructorPropertiesProvider keeps the historic behaviour in
+ place using reflection. This implementation is due for removal in a future version of jOOQ.
+
+ If you wish to continue working with the java.beans.ConstructorProperties annotation, use the
+ jOOQ-beans-extensions module and its DefaultConstructorPropertiesProvider implementation
+ """.replace("{pojo}", c.getDeclaringClass().getName())
+ );
+
+ return (String[]) V.invoke(a);
+ }
+ }
+ catch (Exception e) {
+ log.error(e);
+ }
+ }
+
+ return null;
+ };
+ }
+
+ @Override
+ public String[] properties(Constructor> constructor) {
+ return DELEGATE.properties(constructor);
+ }
+}
diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java
index 85f7cab44a..9ab65c0b83 100644
--- a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java
+++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java
@@ -51,6 +51,7 @@ import org.jooq.CharsetProvider;
import org.jooq.CommitProvider;
import org.jooq.Configuration;
import org.jooq.ConnectionProvider;
+import org.jooq.ConstructorPropertiesProvider;
import org.jooq.ConverterProvider;
import org.jooq.DSLContext;
import org.jooq.DiagnosticsListenerProvider;
@@ -169,6 +170,11 @@ public class MockConfiguration extends AbstractConfiguration {
return delegate.transactionProvider();
}
+ @Override
+ public ConstructorPropertiesProvider constructorPropertiesProvider() {
+ return delegate.constructorPropertiesProvider();
+ }
+
@Override
public RecordMapperProvider recordMapperProvider() {
return delegate.recordMapperProvider();
@@ -340,6 +346,12 @@ public class MockConfiguration extends AbstractConfiguration {
return this;
}
+ @Override
+ public Configuration set(ConstructorPropertiesProvider newConstructorPropertiesProvider) {
+ delegate.set(newConstructorPropertiesProvider);
+ return this;
+ }
+
@Override
public Configuration set(RecordMapper, ?> newRecordMapper) {
delegate.set(newRecordMapper);
@@ -531,6 +543,11 @@ public class MockConfiguration extends AbstractConfiguration {
return new MockConfiguration(delegate.derive(newTransactionProvider), provider);
}
+ @Override
+ public Configuration derive(ConstructorPropertiesProvider newConstructorPropertiesProvider) {
+ return new MockConfiguration(delegate.derive(newConstructorPropertiesProvider), provider);
+ }
+
@Override
public Configuration derive(RecordMapper, ?> newRecordMapper) {
return new MockConfiguration(delegate.derive(newRecordMapper), provider);
diff --git a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.20.0.xsd b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.20.0.xsd
index 5848e33b3e..91ae51143a 100644
--- a/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.20.0.xsd
+++ b/jOOQ/src/main/resources/org/jooq/xsd/jooq-runtime-3.20.0.xsd
@@ -1409,7 +1409,7 @@ IDENTITY values, and if {@link #returnAllOnUpdatableRecord} is active, also othe
-
+ jOOQ-beans-extensions module) should be considered by the DefaultRecordMapper.]]>
diff --git a/pom.xml b/pom.xml
index b7ffeec57f..7ba2510fe4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -114,6 +114,11 @@
jooq-checker
${project.version}
+
+ org.jooq
+ jooq-beans-extensions
+ ${project.version}
+
org.jooq
jooq-postgres-extensions
@@ -841,6 +846,7 @@
jOOQ-checker
jOOQ-jackson-extensions
jOOQ-postgres-extensions
+ jOOQ-beans-extensions
jOOQ-meta
jOOQ-meta-extensions