[jOOQ/jOOQ#7585] Move java.desktop related logic of DefaultRecordMapper

into a new jooq-beans-extensions module
This commit is contained in:
Lukas Eder 2024-05-27 14:25:29 +02:00
parent 6626120db8
commit a5be8cd89d
20 changed files with 535 additions and 28 deletions

3
jOOQ-beans-extensions/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
/.idea
/*.iml

View File

@ -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

View File

@ -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/).

View File

@ -0,0 +1,67 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jooq</groupId>
<artifactId>jooq-parent</artifactId>
<version>3.20.0-SNAPSHOT</version>
</parent>
<artifactId>jooq-beans-extensions</artifactId>
<name>jOOQ Beans Extensions Beans</name>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.jooq.org/inc/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.jooq.beans.extensions</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

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

View File

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

View File

@ -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

View File

@ -0,0 +1,2 @@
Thanks for downloading jOOQ.
Please visit http://www.jooq.org for more information.

View File

@ -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

View File

@ -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.
* <p>
* This method is not thread-safe and should not be used in globally
* available <code>Configuration</code> 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.
* <p>
@ -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.
* <p>

View File

@ -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 <code>java.beans</code> style constructor
* properties to the {@link DefaultRecordMapper}.
* <p>
* Starting with jOOQ 3.20 and <a href=
* "https://github.com/jOOQ/jOOQ/issues/7585">https://github.com/jOOQ/jOOQ/issues/7585</a>,
* the {@link java.beans.ConstructorProperties} annotation support has been
* moved out of the core library to the
* <code>jOOQ-mapper-extensions-beans</code> module, to allow for:
* <p>
* <ul>
* <li>Removal of the dependency on the <code>java.beans</code> JDK module from
* the core library</li>
* <li>Maintenance of backwards compatibility for those users using the
* annotation</li>
* </ul>
* <p>
* 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()}.
* <p>
*
* @return The constructor properties, if applicable, or <code>null</code>
* if the argument {@link Constructor} does not expose any
* constructor properties.
*/
@Nullable
public String @Nullable [] properties(Constructor<?> constructor);
}

View File

@ -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 <code>jOOQ-beans-extensions</code> 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 <code>jOOQ-beans-extensions</code> 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 <code>jOOQ-beans-extensions</code> module) should be considered by the DefaultRecordMapper.
*
*/
public Settings withMapConstructorPropertiesParameterNames(Boolean value) {

View File

@ -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) {

View File

@ -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<T> extends AbstractXMLBinding<T> {
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<T> extends AbstractXMLBinding<T> {
}
}
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();
}

View File

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

View File

@ -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 <code>RecordMapper</code> types, which
* applies to {@link Record#into(Class)}, {@link Result#into(Class)}, and
@ -224,8 +218,11 @@ import org.jooq.tools.reflect.ReflectException;
* used</h5>
* <p>
* <ul>
* <li>The standard JavaBeans {@link ConstructorProperties} annotation is used
* to match constructor arguments against POJO members or getters.</li>
* <li>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 <code>jOOQ-mapper-extensions-beans</code> module, or
* if you provide your own.</li>
* <li>If the property names provided to the constructor match the record's
* columns via the aforementioned naming conventions, that information is used.
* </li>
@ -440,11 +437,13 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
// [#1837] [#10349] [#11123] If any java.beans.ConstructorProperties annotations are
// present use those rather than matching constructors by the number of arguments
if (debugCPSettings = !FALSE.equals(configuration.settings().isMapConstructorPropertiesParameterNames())) {
ConstructorPropertiesProvider cpp = configuration.constructorPropertiesProvider();
for (Constructor<E> 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<R extends Record, E> implements RecordMapper<R,
return debug == null ? "check skipped" : debug.toString();
}
private List<String> collectParameterNames(Parameter[] parameters) {
private static final List<String> collectParameterNames(Parameter[] parameters) {
return Arrays.stream(parameters).map(Parameter::getName).collect(Collectors.toList());
}
@ -1211,7 +1210,7 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
}
}
private static <E> E attach(E attachable, Record record) {
private static final <E> 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)

View File

@ -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 <code>java.beans</code> 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<Annotation> P;
static final Method V;
static {
Class<Annotation> 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<Annotation>) 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);
}
}

View File

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

View File

@ -1409,7 +1409,7 @@ IDENTITY values, and if {@link #returnAllOnUpdatableRecord} is active, also othe
</element>
<element name="mapConstructorPropertiesParameterNames" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether constructor parameter names obtained from the {@link java.beans.ConstructorProperties} annotation should be considered by the DefaultRecordMapper.]]></jxb:javadoc></jxb:property></appinfo></annotation>
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether constructor parameter names obtained from the {@link org.jooq.ConstructorPropertiesProvider} SPI (default implementation in the <code>jOOQ-beans-extensions</code> module) should be considered by the DefaultRecordMapper.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="mapConstructorParameterNames" type="boolean" minOccurs="0" maxOccurs="1" default="false">

View File

@ -114,6 +114,11 @@
<artifactId>jooq-checker</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-beans-extensions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-postgres-extensions</artifactId>
@ -841,6 +846,7 @@
<module>jOOQ-checker</module>
<module>jOOQ-jackson-extensions</module>
<module>jOOQ-postgres-extensions</module>
<module>jOOQ-beans-extensions</module>
<module>jOOQ-meta</module>
<module>jOOQ-meta-extensions</module>