parent
e34af25a83
commit
cb618620e0
@ -19,7 +19,7 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.version>1.2.21</kotlin.version>
|
||||
<kotlin.version>1.2.30</kotlin.version>
|
||||
<org.jooq.version>3.11.0-SNAPSHOT</org.jooq.version>
|
||||
<org.h2.version>1.4.195</org.h2.version>
|
||||
<java.version>1.8</java.version>
|
||||
@ -44,6 +44,14 @@
|
||||
<artifactId>kotlin-stdlib-jre8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Needed for mapping into Kotlin data classes -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
||||
@ -170,6 +170,16 @@ fun main(args: Array<String>) {
|
||||
println("Actor ID ${id - 3}: $first $last wrote ${-count} books")
|
||||
}
|
||||
|
||||
// jOOQ can fetch into Kotlin data classes by attribute name
|
||||
header("Data classes")
|
||||
ctx.select(b.TITLE, a.FIRST_NAME.concat(" ").concat(a.LAST_NAME).`as`("author"), one().`as`("dummy"))
|
||||
.from(a)
|
||||
.join(b).on(a.ID.eq(b.AUTHOR_ID))
|
||||
.fetchInto(Book::class.java)
|
||||
.forEach {
|
||||
println("$it")
|
||||
}
|
||||
|
||||
// Don't we wish for multiline strings in Java?
|
||||
header("Using multiline strings with the plain SQL API")
|
||||
ctx.resultQuery("""
|
||||
@ -234,48 +244,5 @@ inline fun <F : Field<String>> F.ilike(field : Field<String>): Condition {
|
||||
return condition("{0} ilike {1}", this, field);
|
||||
}
|
||||
|
||||
// Destructuring records into sets of local variables
|
||||
// Will be supported by jOOQ out-of-the-box:
|
||||
// https://github.com/jOOQ/jOOQ/issues/6245
|
||||
// --------------------------------------------------
|
||||
operator fun <T, R : Record1<T>> R.component1() : T {
|
||||
return this.value1();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record2<T, *>> R.component1() : T {
|
||||
return this.value1();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record2<*, T>> R.component2() : T {
|
||||
return this.value2();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record3<T, *, *>> R.component1() : T {
|
||||
return this.value1();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record3<*, T, *>> R.component2() : T {
|
||||
return this.value2();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record3<*, *, T>> R.component3() : T {
|
||||
return this.value3();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record4<T, *, *, *>> R.component1() : T {
|
||||
return this.value1();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record4<*, T, *, *>> R.component2() : T {
|
||||
return this.value2();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record4<*, *, T, *>> R.component3() : T {
|
||||
return this.value3();
|
||||
}
|
||||
|
||||
operator fun <T, R : Record4<*, *, *, T>> R.component4() : T {
|
||||
return this.value4();
|
||||
}
|
||||
|
||||
// ... more methods ...
|
||||
// Kotlin data classes can be used with DefaultRecordMapper
|
||||
data class Book(val author: String, val title: String)
|
||||
@ -54,8 +54,16 @@ import static org.jooq.tools.reflect.Reflect.accessible;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.*;
|
||||
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;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -368,36 +376,40 @@ public class DefaultRecordMapper<R extends Record, E> implements RecordMapper<R,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// [#7324] Map immutable Kotlin classes by parameter names if kotlin-reflect is on the classpath
|
||||
try {
|
||||
Reflect JvmClassMappingKt = Reflect.on("kotlin.jvm.JvmClassMappingKt");
|
||||
Object klass = JvmClassMappingKt.call("getKotlinClass", type).get();
|
||||
if (Tools.isKotlinAvailable()) {
|
||||
try {
|
||||
Reflect jvmClassMappingKt = Tools.ktJvmClassMapping();
|
||||
Reflect kClasses = Tools.ktKClasses();
|
||||
|
||||
Reflect KClasses = Reflect.on("kotlin.reflect.full.KClasses");
|
||||
Reflect primaryConstructor = KClasses.call("getPrimaryConstructor", klass);
|
||||
Object klass = jvmClassMappingKt.call("getKotlinClass", type).get();
|
||||
Reflect primaryConstructor = kClasses.call("getPrimaryConstructor", klass);
|
||||
|
||||
if (primaryConstructor.get() != null) {
|
||||
// it is a Kotlin class
|
||||
List parameters = primaryConstructor.call("getParameters").get();
|
||||
Class<?> klassType = Reflect.on("kotlin.reflect.KClass").type();
|
||||
Method getJavaClass = JvmClassMappingKt.type().getMethod("getJavaClass", klassType);
|
||||
// It is a Kotlin class
|
||||
if (primaryConstructor.get() != null) {
|
||||
List<?> parameters = primaryConstructor.call("getParameters").get();
|
||||
Class<?> klassType = Tools.ktKClass().type();
|
||||
Method getJavaClass = jvmClassMappingKt.type().getMethod("getJavaClass", klassType);
|
||||
|
||||
List<String> parameterNames = new ArrayList<>();
|
||||
Class[] parameterTypes = new Class[parameters.size()];
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
Reflect parameter = Reflect.on(parameters.get(i));
|
||||
parameterNames.add(parameter.call("getName").get());
|
||||
Object typeClassifier = parameter.call("getType").call("getClassifier").get();
|
||||
parameterTypes[i] = (Class) getJavaClass.invoke(JvmClassMappingKt.get(), typeClassifier);
|
||||
List<String> parameterNames = new ArrayList<String>();
|
||||
Class<?>[] parameterTypes = new Class[parameters.size()];
|
||||
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
Reflect parameter = Reflect.on(parameters.get(i));
|
||||
parameterNames.add(parameter.call("getName").get());
|
||||
Object typeClassifier = parameter.call("getType").call("getClassifier").get();
|
||||
parameterTypes[i] = (Class<?>) getJavaClass.invoke(jvmClassMappingKt.get(), typeClassifier);
|
||||
}
|
||||
|
||||
Constructor<E> javaConstructor = (Constructor<E>) this.type.getConstructor(parameterTypes);
|
||||
delegate = new ImmutablePOJOMapperWithParameterNames(javaConstructor, parameterNames);
|
||||
return;
|
||||
}
|
||||
|
||||
Constructor<E> javaConstructor = (Constructor<E>) this.type.getConstructor(parameterTypes);
|
||||
delegate = new ImmutablePOJOMapperWithParameterNames(javaConstructor, parameterNames);
|
||||
return;
|
||||
}
|
||||
} catch (ReflectException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
// do nothing
|
||||
catch (ReflectException ignore) {}
|
||||
catch (NoSuchMethodException ignore) {}
|
||||
catch (IllegalAccessException ignore) {}
|
||||
catch (InvocationTargetException ignore) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -238,6 +238,7 @@ import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
import org.jooq.tools.jdbc.JDBCUtils;
|
||||
import org.jooq.tools.reflect.Reflect;
|
||||
import org.jooq.tools.reflect.ReflectException;
|
||||
import org.jooq.types.UByte;
|
||||
import org.jooq.types.UInteger;
|
||||
import org.jooq.types.ULong;
|
||||
@ -513,36 +514,49 @@ final class Tools {
|
||||
* The default escape character for <code>[a] LIKE [b] ESCAPE [...]</code>
|
||||
* clauses.
|
||||
*/
|
||||
static final char ESCAPE = '!';
|
||||
static final char ESCAPE = '!';
|
||||
|
||||
/**
|
||||
* A lock for the initialisation of other static members
|
||||
*/
|
||||
private static final Object initLock = new Object();
|
||||
|
||||
/**
|
||||
* Indicating whether JPA (<code>javax.persistence</code>) is on the
|
||||
* classpath.
|
||||
*/
|
||||
private static Boolean isJPAAvailable;
|
||||
private static volatile Boolean isJPAAvailable;
|
||||
|
||||
/**
|
||||
* Indicating whether Kotlin (<code>kotlin.*</code>) is on the classpath.
|
||||
*/
|
||||
private static volatile Boolean isKotlinAvailable;
|
||||
private static volatile Reflect ktJvmClassMapping;
|
||||
private static volatile Reflect ktKClasses;
|
||||
private static volatile Reflect ktKClass;
|
||||
|
||||
/**
|
||||
* [#3696] The maximum number of consumed exceptions in
|
||||
* {@link #consumeExceptions(Configuration, PreparedStatement, SQLException)}
|
||||
* helps prevent infinite loops and {@link OutOfMemoryError}.
|
||||
*/
|
||||
private static int maxConsumedExceptions = 256;
|
||||
private static int maxConsumedResults = 65536;
|
||||
private static int maxConsumedExceptions = 256;
|
||||
private static int maxConsumedResults = 65536;
|
||||
|
||||
/**
|
||||
* A pattern for the dash line syntax
|
||||
*/
|
||||
private static final Pattern DASH_PATTERN = Pattern.compile("(-+)");
|
||||
private static final Pattern DASH_PATTERN = Pattern.compile("(-+)");
|
||||
|
||||
/**
|
||||
* A pattern for the pipe line syntax
|
||||
*/
|
||||
private static final Pattern PIPE_PATTERN = Pattern.compile("(?<=\\|)([^|]+)(?=\\|)");
|
||||
private static final Pattern PIPE_PATTERN = Pattern.compile("(?<=\\|)([^|]+)(?=\\|)");
|
||||
|
||||
/**
|
||||
* A pattern for the dash line syntax
|
||||
*/
|
||||
private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)");
|
||||
private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)");
|
||||
|
||||
/**
|
||||
* All characters that are matched by Java's interpretation of \s.
|
||||
@ -2825,20 +2839,97 @@ final class Tools {
|
||||
/**
|
||||
* Check if JPA classes can be loaded. This is only done once per JVM!
|
||||
*/
|
||||
private static final boolean isJPAAvailable() {
|
||||
static final boolean isJPAAvailable() {
|
||||
if (isJPAAvailable == null) {
|
||||
try {
|
||||
Class.forName(Column.class.getName());
|
||||
isJPAAvailable = true;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
isJPAAvailable = false;
|
||||
synchronized (initLock) {
|
||||
if (isJPAAvailable == null) {
|
||||
try {
|
||||
Class.forName(Column.class.getName());
|
||||
isJPAAvailable = true;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
isJPAAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isJPAAvailable;
|
||||
}
|
||||
|
||||
static final boolean isKotlinAvailable() {
|
||||
if (isKotlinAvailable == null) {
|
||||
synchronized (initLock) {
|
||||
if (isKotlinAvailable == null) {
|
||||
try {
|
||||
if (ktJvmClassMapping() != null) {
|
||||
if (ktKClasses() != null) {
|
||||
isKotlinAvailable = true;
|
||||
}
|
||||
else {
|
||||
isKotlinAvailable = false;
|
||||
log.info("Kotlin is available, but not kotlin-reflect. Add the kotlin-reflect dependency to better use Kotlin features like data classes");
|
||||
}
|
||||
}
|
||||
else {
|
||||
isKotlinAvailable = false;
|
||||
}
|
||||
}
|
||||
catch (ReflectException e) {
|
||||
isKotlinAvailable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isKotlinAvailable;
|
||||
}
|
||||
|
||||
static final Reflect ktJvmClassMapping() {
|
||||
if (ktJvmClassMapping == null) {
|
||||
synchronized (initLock) {
|
||||
if (ktJvmClassMapping == null) {
|
||||
try {
|
||||
ktJvmClassMapping = Reflect.on("kotlin.jvm.JvmClassMappingKt");
|
||||
}
|
||||
catch (ReflectException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ktJvmClassMapping;
|
||||
}
|
||||
|
||||
static final Reflect ktKClasses() {
|
||||
if (ktKClasses == null) {
|
||||
synchronized (initLock) {
|
||||
if (ktKClasses == null) {
|
||||
try {
|
||||
ktKClasses = Reflect.on("kotlin.reflect.full.KClasses");
|
||||
}
|
||||
catch (ReflectException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ktKClasses;
|
||||
}
|
||||
|
||||
static final Reflect ktKClass() {
|
||||
if (ktKClass == null) {
|
||||
synchronized (initLock) {
|
||||
if (ktKClass == null) {
|
||||
try {
|
||||
ktKClass = Reflect.on("kotlin.reflect.KClass");
|
||||
}
|
||||
catch (ReflectException ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ktKClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether <code>type</code> has any {@link Column} annotated members
|
||||
* or methods
|
||||
|
||||
Loading…
Reference in New Issue
Block a user