"Class cannot be cast to class ParameterizedType" while mapping

2,540 views
Skip to first unread message

Maurizio

unread,
Oct 30, 2020, 5:19:48 AM10/30/20
to jOOQ User Group
Hello.

After a refactoring session, which included moving some classes like BeatsId in another module, this code

fun DSLContext.load(beatsId: BeatsId): BeatsPojo =
selectFrom(BEATS).where(BEATS.BEATS_ID.eq(beatsId))
.fetchOneInto(BeatsPojo::class.java)


causes

org.jooq.exception.MappingException: An error ocurred when mapping record to class x.y.z..jooq.tables.pojos.Beats

java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')

Where BeatsPojo is an alias for the pojo generated by jooq (x.y.z..jooq.tables.pojos.Beats).

However, this code runs successfully:

selectFrom(BEATS).where(BEATS.BEATS_ID.eq(beatsId))
.fetchOne().map {
BeatsPojo(
it[BEATS.BEATS_ID], it[BEATS.PARENT_BEATS_ID],
it[BEATS.TRACK_ID],
it[BEATS.FRAME_INDICES],
it[BEATS.CREATOR_USER_ID], it[BEATS.CREATION_TIMESTAMP]
)
I assume this is some kind of reflection problem. What can be the missing type? The dependencies look OK, to me, but I have no other ideas beyond that.

Other pojos work just fine.

Only two pojos cause this kind of problem and both have a field of array type. In both cases the type of the element of the array is converted using a org.jooq.Converter. This is not changed during the refactoring.

Could you help me understand what is happening?

Thanks,

Maurizio.

Maurizio

unread,
Oct 30, 2020, 8:02:56 AM10/30/20
to jOOQ User Group
Hello, again.

I have some more insight.

The exception is thrown here:

if (value instanceof Collection && List.class.isAssignableFrom(mType)) {
    Class componentType = (Class) ((ParameterizedType) member.getGenericType()).getActualTypeArguments()[0];
    member.set(result, Convert.convert((Collection) value, componentType));
}


in DefaultRecordMapper.java.

member is of an interface type which also implements a List type.  member.getGenericType() returns the non-generic interface type which has no actual type arguments, and this causes the exception. I have no idea why this didn't happen before the refactoring. As the same interface with the same declaration was in use for some time.

I still don't know how to fix the issue, though.

Maurizio.

Lukas Eder

unread,
Nov 5, 2020, 9:16:21 AM11/5/20
to jOOQ User Group
Thanks again for your report. I can't reproduce this. As mentioned in the other issue, can you please provide an MCVE (https://github.com/jOOQ/jOOQ-mcve). Also, jOOQ 3.14 has a KotlinGenerator, but I'm assuming you're not using that, because in the other issue, you're on jOOQ 3.13 and you're probably using the JavaGenerator.

Thanks,
Lukas

--
You received this message because you are subscribed to the Google Groups "jOOQ User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jooq-user+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jooq-user/82f39041-7e0d-4eab-9a20-2b426a0fedb3n%40googlegroups.com.

Lukas Eder

unread,
Nov 5, 2020, 9:17:25 AM11/5/20
to jOOQ User Group
My assumption here was that by "alias" you meant "typealias" like this:

public typealias X = Pojo

Maurizio

unread,
Nov 6, 2020, 10:37:18 AM11/6/20
to jOOQ User Group
I'm just using import alias:

import x.y.z.jooq.tables.pojos.Beats as BeatsPojo

because I didn't learn how to configure the code generation, yet, and I already planned to switch to the Kotlin code generation.

I should really use typealiases, though. You are right.

Maurizio

unread,
Nov 6, 2020, 10:38:46 AM11/6/20
to jOOQ User Group
I'll make a MCVE of this issue, too. I expect this one should be easier, because I think I understand what is happening, at least partially.

Thanks,

Maurizio.

Lukas Eder

unread,
Nov 6, 2020, 11:09:20 AM11/6/20
to jOOQ User Group
Thanks a lot. I'll look into the MCVE as soon as possible.

Maurizio

unread,
Nov 7, 2020, 1:52:24 PM11/7/20
to jOOQ User Group
This is not a proper MCVE, but it should be enough. If it's not, I'll do better.

Create a table with a field of type "integer array", like this:

CREATE TABLE public.test_table_1
(
    column1 integer[] NOT NULL
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE public.test_table_1
    OWNER to postgres;

In Kotlin, define this interface:

interface IThing : List<String>

Notice how this interface inherits from List<T>. This is important.
Here's an implementation:

class Thing(val items: List<String>) : IThing, List<String> by items

Here's a converter between Array<Int> and the offending interface.

class IntArrayIThingConverter : Converter<Array<Int>, IThing> {
override fun from(databaseObject: Array<Int>?): IThing? =
databaseObject?.let Thing(it.map { it.toString() }}

override fun to(userObject: IThing?): Array<Int>? =
userObject?.map { it.toInt() }?.toTypedArray()

override fun fromType(): Class<Array<Int>> =
Array<Int>::class.java

override fun toType(): Class<IThing> =
IThing::class.java
}

The converter is used to force the type: 

forcedType {
userType = 'x.y.z.IThing'
converter = '
x.y.z.IntArrayIThingConverter'

includeExpression = '.*\\..*column1.*'
}

If you now fetch a row, like with

fun DSLContext.fetchTestTable_1() : List<TestTable_1Pojo> =
  selectFrom(TEST_TABLE_1).fetchInto(TestTable_1Pojo::class.java)

This codein DefaultRecordMapper.java is executed:

// [#3082] Map nested collection types

if (value instanceof Collection && List.class.isAssignableFrom(mType)) {
Class componentType = (Class) ((ParameterizedType) method.getGenericParameterTypes()[0]).getActualTypeArguments()[0];
method.invoke(result, Convert.convert((Collection) value, componentType));
}

This expression:

((ParameterizedType) method.getGenericParameterTypes()[0]).

evaluates to the interface type IThing, which implements List but has no actual type arguments.

If List.class.isAssignableFrom(mType) is true, you should check the inheritance hierarchy of the type, to find the actual List type.

Please, let me know if this is enough.

Regards,

Maurizio.

Lukas Eder

unread,
Nov 9, 2020, 6:45:32 AM11/9/20
to jOOQ User Group
Thanks a lot, Maurizio. 

On Sat, Nov 7, 2020 at 7:52 PM Maurizio <maurizi...@gmail.com> wrote:
interface IThing : List<String>

I think that might have been the missing piece. I'll investigate this soon and will get back to you.

Lukas Eder

unread,
Nov 10, 2020, 10:37:13 AM11/10/20
to jOOQ User Group
I'm sorry, I cannot reproduce this minimally.

You seem to already have most of the work done on your end to create a minimal case, but I don't have access to it. I'm obviously still missing a piece here (kotlin version, JVM version, DDL detail, import, etc.). May be just a copy paste error, or a nullability token that I've overlooked, etc. etc. It's always hard to glue an example from various snippets distributed through emails, which is why this template is so helpful: https://github.com/jOOQ/jOOQ-mcve. git clone, mvn clean install, and the problem can be reproduced immediately.

I'll be happy to take another look at an MCVE if you can provide one.
Thanks,
Lukas

Maurizio

unread,
Nov 11, 2020, 2:28:08 AM11/11/20
to jOOQ User Group
I'm sorry you could not reproduce the bug. I thought this was easy.
As promised, I'll try and package the code as a fork of the jOOQ-mcve repository. I delayed because I'm not familiar with Maven, and I'm a bit cognitively overwhelmed, currently, but I should be able to do it.

Regards,
Maurizio.

Lukas Eder

unread,
Nov 11, 2020, 3:28:32 AM11/11/20
to jOOQ User Group
On Wed, Nov 11, 2020 at 8:28 AM Maurizio <maurizi...@gmail.com> wrote:
I'm sorry you could not reproduce the bug. I thought this was easy.

It probably is, but after several emails, I still couldn't get it to reproduce, while my queue is also quite full at the moment. The Kotlin integration is quite new and there are a few more obvious and pressing bugs than this one. I still want to try to reproduce and fix it, but I need to be able to reproduce it first.
 
As promised, I'll try and package the code as a fork of the jOOQ-mcve repository. I delayed because I'm not familiar with Maven, and I'm a bit cognitively overwhelmed, currently, but I should be able to do it.

So, it's a stalemate :) The template allows for immediate reproduction, so from experience, I tend to think it's worth it. This is already the 12th email in this thread (and there are other threads). When bugs reported through the template usually require a single email or github issue. I believe that while it may take some time to get into, it does save time on both sides (and it's also often helped find out user errors, which aren't actual bugs, which I don't think is the case in your case).

Anyway, how can we have improved the template for you? One thing I will do immediately is configure also KotlinGenerator and ScalaGenerator to work out of the box, to get help prevent having to set up all the compiler and library dependencies

Lukas Eder

unread,
Nov 11, 2020, 4:51:42 AM11/11/20
to jOOQ User Group
OK, so I could manage to reproduce the issue with the updated MCVE:

I got:

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 1.008 sec <<< FAILURE!
mcveTest(org.jooq.mcve.test.kotlin.KotlinTest)  Time elapsed: 0.95 sec  <<< ERROR!
org.jooq.exception.MappingException: An error ocurred when mapping record to class org.jooq.mcve.kotlin.tables.pojos.TestTable_1
        at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:877)
        at org.jooq.impl.DefaultRecordMapper.map(DefaultRecordMapper.java:543)
        at org.jooq.impl.ResultImpl.into(ResultImpl.java:1285)
        at org.jooq.impl.AbstractResultQuery.fetchInto(AbstractResultQuery.java:1545)
        at org.jooq.impl.SelectImpl.fetchInto(SelectImpl.java:3936)
        at org.jooq.mcve.test.kotlin.KotlinTest.mcveTest(KotlinTest.kt:20)
Caused by: java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
        at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:909)
        at org.jooq.impl.DefaultRecordMapper$MutablePOJOMapper.map(DefaultRecordMapper.java:829)
        ... 39 more

I've created https://github.com/jOOQ/jOOQ/issues/10910 for this. I'll make further comments directly on that list.

Lukas Eder

unread,
Nov 11, 2020, 4:51:55 AM11/11/20
to jOOQ User Group
... directly on that github issue, I meant

Lukas Eder

unread,
Nov 11, 2020, 5:45:01 AM11/11/20
to jOOQ User Group
So, https://github.com/jOOQ/jOOQ/issues/10910 will be fixed in 3.15.0 and 3.14.4. Thanks again for reporting it!

Some final observations: This wasn't related to using kotlin, or to the array type used in PostgreSQL. The main thing here was really the fact that a user-defined type extending List was used with a custom converter.

The reason why I couldn't reproduce the issue was that (obviously in hindsight, but I overlooked the detail because the emails here didn't include an insert statement), there was no data in the table, so the mapping logic was never invoked... A great example why MCVE's are so useful, because of the C (as in "complete") :)

Cheers,
Lukas

Maurizio

unread,
Nov 11, 2020, 3:57:39 PM11/11/20
to jOOQ User Group
> Some final observations: This wasn't related to using kotlin, or to the array type used in PostgreSQL. The main thing here was really the fact that a user-defined type extending List was used with a custom converter.

Yes, I actually wrote that in the second message of this thread:

> I have some more insight.
>
>The exception is thrown here:
>
>if (value instanceof Collection && List.class.isAssignableFrom(mType)) {
>    Class componentType = (Class) ((ParameterizedType) member.getGenericType()).getActualTypeArguments()[0];
>    member.set(result, Convert.convert((Collection) value, componentType));
>}
>
>in DefaultRecordMapper.java.
>
> member is of an interface type which also implements a List type.  member.getGenericType() returns the non-generic interface type which has no actual type arguments, and this causes the exception.

I should have used more words. Sorry.

Thanks for fixing the bug.

Maurizio

Reply all
Reply to author
Forward
0 new messages