0

I have a Kotlin Project using JOOQ to connect with a MySQL database. I have a my_table table with an int int_column and I want to convert it to a MyCustomType class declared on MyTableJooqEntityfile. The configuration is done by using JOOQ forcedTypes specified on build.gradle file. But I am receiving a ClassCastException. What can I be doing wrong?

Now I will show some implementation details. I have the following MySQL table:

CREATE TABLE `my_table`
(
    `id`         int PRIMARY KEY AUTO_INCREMENT  not null,
    `int_column` int                             not null
);

Then I have this JOOQ entity mapping:

@Entity
@Table(name = "my_table")
data class MyTableJooqEntity(@Id
                             @Column(name = "id")
                             var Id: Int = -1,

                             @Column(name = "int_column")
                             var intColumn: MyCustomType)

And I have this build.gradle forcedTypes configuration:

forcedType {
    userType = "co.mycompany.MyCustomType"
    converter = "new co.mycompany.converters.IntegerToMyCustomTypeConverter()"
    includeExpression = "mydatabase.my_table.int_column"
}

And the custom converter:

package co.mycompany.converters

import co.mycompany.MyCustomType
import org.jooq.Converter

class IntegerToMyCustomTypeConverter : Converter<Integer, MyCustomType> {
    override fun from(databaseObject: Integer?): MyCustomType? {
        return MyCustomType.of(databaseObject)
    }

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

    override fun to(userObject: MyCustomType?): Integer? {
        return userObject.toBigDecimal().toInt() as Integer 
       // I need to do some transformations before
       // convert it finally to java.lang.Integer 
       // (or Kotlin Int, but neither is working)
    }

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

Then when I run a select on my JooqDao:

fun selectById(Id: Int): MyTableJooqEntity? =
   withContext {
     val query = it.select(*table.fields())
                .from(table)
                .where(table.ID.eq(Id))
     return query.fetchOneInto(MyTableJooqEntity::class.java)
   }

And I am finally receiving this error, no matter what I try I can't convert int to any custom type:

 java.lang.IllegalArgumentException: argument type mismatch
      at co.mycompany.MyCustomTest.testConvert(MyCustomTest.kt:100)
  Caused by: java.lang.ClassCastException: Cannot cast java.lang.Integer to co.mycompany.MyCustomType
      at co.mycompany.MyCustomTest.testConvert(MyCustomTest.kt:100)
rafalimaz
  • 70
  • 10
  • 1
    What's at line `100` of your `MyCustomTest.kt` test class? Beecause that's where the error happens. – Lukas Eder May 09 '23 at 13:34
  • The function just runs the JooqDao.selectById(Id). The error log is biased. The error actually happens in the end of the selectById function, when fetching results. I could check it on Debug mode. – rafalimaz May 09 '23 at 15:45
  • 1
    Can you show a reproducer that's relevant to the problem, or the actual stack trace, if the one you've shown isn't really useful? Otherwise, it will be hard to help. Maybe, [this resource helps creating a reproducer?](https://stackoverflow.com/help/minimal-reproducible-example) – Lukas Eder May 09 '23 at 17:45
  • For now I cannot provide a reproducer. What I can say is that I am still trying to map the conversion between the int mysql type to my custom int interface. I am looking more deep on the jdbcMapperFactory implementation and trying to add column definition, converter or setter property. Still no luck. – rafalimaz May 10 '23 at 14:04
  • I finally got it working by adding a getter for my custom type to the JdbcMapperFactory. – rafalimaz May 10 '23 at 21:38

1 Answers1

0

The solution was to add a getter for MyCustomType to the JdbcMapperFactory. The forcedType especification is not enough in this case.

fun getJdbcMapperFactory(): JdbcMapperFactory =
        JdbcMapperFactory
            .newInstance()
            .addGetterForType(MyCustomType::class.java) { rs, i -> MyCustomType.of(rs.getInt(i)) }
rafalimaz
  • 70
  • 10
  • 1
    `JdbcMapperFactory` is a class from SimpleFlatMapper, so your question wasn't really about jOOQ... I wonder why you need this third party mapping library at all. In the past, people have used it to map between flat result sets and hierarchies, but since [jOOQ 3.15's `MULTISET` operator and ad-hoc conversion](https://blog.jooq.org/jooq-3-15s-new-multiset-operator-will-change-how-you-think-about-sql/), that shouldn't really be necessary anymore. Chances are high, you can remove the third party dependency. – Lukas Eder May 11 '23 at 07:03
  • The usage of SFM I saw was to convert from resultSet strings to nullabe JSONObjects, and Enums. My company is already using SFM for a long time for these cases. Now I was trying to convert resultSet int to a custom type that I create for my business rule...I really don't know if it's worth to replace all previous SFM code with the JOOQ features you mentioned. But thanks for the suggestion. I will check it deeply with my colleagues. – rafalimaz May 11 '23 at 22:08