带有 MockResult 的 Jooq 自定义类型抛出 DataTypeException

Posted

技术标签:

【中文标题】带有 MockResult 的 Jooq 自定义类型抛出 DataTypeException【英文标题】:Jooq custom type with MockResult throwing DataTypeException 【发布时间】:2014-04-07 23:27:28 【问题描述】:

尝试使用带有自定义类型(Joda Time)的 Jooq 模拟设置单元测试时出错。我在代码生成期间注册了一个转换器。看起来 Jooq 几乎找不到/没有看到转换器,并试图退回到 ConvertAll,这不起作用。

查询模拟结果会抛出异常(下)。

转换器:

import org.joda.time.DateTime;
import java.sql.Timestamp;

import org.joda.time.DateTimeZone;
import org.jooq.Converter;

public class DateTimeConverter implements Converter<Timestamp, DateTime> 
    @Override
    public DateTime from(Timestamp databaseObject) 
        return new DateTime(databaseObject.getTime()).withZone(DateTimeZone.UTC);
    

    @Override
    public Timestamp to(DateTime userObject) 
        return new Timestamp(userObject.getMillis());
    

    @Override
    public Class<Timestamp> fromType() 
        return Timestamp.class;
    

    @Override
    public Class<DateTime> toType() 
        return DateTime.class;
    

类生成成功:

/**
 * This class is generated by jOOQ
 */
package redacted.generated.jooq.tables;

/**
 * This class is generated by jOOQ.
 */
@javax.annotation.Generated(value    = "http://www.jooq.org", "3.0.0",
                            comments = "This class is generated by jOOQ")
@java.lang.SuppressWarnings( "all", "unchecked" )
public class Bug extends org.jooq.impl.TableImpl<redacted.generated.jooq.tables.records.BugRecord> 

    private static final long serialVersionUID = 1992533553;

    /**
     * The singleton instance of <code>public.bug</code>
     */
    public static final redacted.generated.jooq.tables.Bug BUG = new redacted.generated.jooq.tables.Bug();

    /**
     * The class holding records for this type
     */
    @Override
    public java.lang.Class<redacted.generated.jooq.tables.records.BugRecord> getRecordType() 
        return redacted.generated.jooq.tables.records.BugRecord.class;
    

    /**
     * The column <code>public.bug.testdate</code>. 
     */
    public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, org.joda.time.DateTime> TESTDATE = createField("testdate", org.jooq.impl.SQLDataType.TIMESTAMP.asConvertedDataType(new name.benjaminAbbitt.jooqJodaTime.DateTimeConverter()), this);

    /**
     * The column <code>public.bug.id</code>. 
     */
    public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> ID = createField("id", org.jooq.impl.SQLDataType.INTEGER, this);

    /**
     * Create a <code>public.bug</code> table reference
     */
    public Bug() 
        super("bug", redacted.generated.jooq.Public.PUBLIC);
    

    /**
     * Create an aliased <code>public.bug</code> table reference
     */
    public Bug(java.lang.String alias) 
        super(alias, redacted.generated.jooq.Public.PUBLIC, redacted.generated.jooq.tables.Bug.BUG);
    

    /**
     * @inheritDoc
     */
    @Override
    public org.jooq.Identity<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> getIdentity() 
        return redacted.generated.jooq.Keys.IDENTITY_BUG;
    

    /**
     * @inheritDoc
     */
    @Override
    public java.util.List<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>> getKeys() 
        return java.util.Arrays.<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>>asList(redacted.generated.jooq.Keys.BUG_ID_KEY);
    

    /**
     * @inheritDoc
     */
    @Override
    public redacted.generated.jooq.tables.Bug as(java.lang.String alias) 
        return new redacted.generated.jooq.tables.Bug(alias);
    

例外是:

org.jooq.exception.DataTypeException: Cannot convert from 2014-03-05T17:57:24.668Z (class org.joda.time.DateTime) to class java.sql.Timestamp
at org.jooq.tools.Convert$ConvertAll.fail(Convert.java:809)
at org.jooq.tools.Convert$ConvertAll.from(Convert.java:747)
at org.jooq.tools.Convert.convert0(Convert.java:296)
at org.jooq.tools.Convert.convert(Convert.java:288)
at org.jooq.tools.Convert.convert(Convert.java:349)
at org.jooq.impl.AbstractRecord.getValue(AbstractRecord.java:219)
at org.jooq.tools.jdbc.MockResultSet.getValue(MockResultSet.java:383)
at org.jooq.tools.jdbc.MockResultSet.getTimestamp(MockResultSet.java:566)
at org.jooq.impl.Utils.getTimestamp(Utils.java:2195)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1952)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1881)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.setValue(CursorImpl.java:1464)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1447)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1439)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:119)
at org.jooq.impl.CursorImpl$CursorIterator.fetchOne(CursorImpl.java:1412)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1389)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1353)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:202)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:176)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:268)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:321)
at org.jooq.impl.AbstractResultQuery.fetch(AbstractResultQuery.java:324)
at org.jooq.impl.SelectImpl.fetch(SelectImpl.java:1034)
at org.jooq.ResultQuery$fetch.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at redacted.bug.BugTest.getTest(BugTest.groovy:47)

BugTest.groovy:

public class BugTest 
    @Test
    public void getTest() 

        DSLContext testContext = setupDSL(new MockDataProvider() 
            @Override
            public MockResult[] execute(MockExecuteContext ctx) throws SQLException 
                DSLContext create = DSL.using(SQLDialect.POSTGRES)
                def result = create.newResult(BUG)

                result.add(create.newRecord(BUG, [id: 0, testdate: new DateTime()]))

                [new MockResult(result.size(), result)]
            
        )

        testContext.select().from(BUG).fetch()  //this line fails
    

    private DSLContext setupDSL(MockDataProvider provider) 
        MockConnection connection = new MockConnection(provider)
        return DSL.using(connection, SQLDialect.POSTGRES)
    

【问题讨论】:

这很好奇。它看起来确实像一个错误。会回复你... 我已注册#3114 来跟踪此问题,但我无法立即重现它。这是我正在使用的测试用例:github.com/jOOQ/jOOQ/commit/…。你用的是最新的 jOOQ 版本吗? 版本 3.2.0。我将更新到 3.3.1。没有意识到我落后了多少。 更新到 3.3.1。仍然遇到问题。我会尝试将其打包为一个单独的项目,并在明天(3/11)发布在 Github 上。感谢卢卡斯的帮助。 @LukasEder 我从 GitHub 问题/提交中获取了 org.jooq.tools.jdbc.MockResultSet 的修补版本(在上面的评论中提到)并将其放在我的类路径中。我的单元测试现在运行良好。当 3.9.1 版本发布后,我将再次删除它。我认为您的快速反应和反馈将大大有助于扩大 Jooq 的知名度,再次感谢您。 【参考方案1】:

这是bug #5771。从 jOOQ 3.9.0 开始,MockResultSet 不使用您的转换器将您的用户定义类型 &lt;U&gt; (DateTime) 还原为 JDBC/数据库类型 &lt;T&gt; (Timestamp),然后再通过它公开JDBC API。如果不使用您的转换器,jOOQ 不知道如何执行此转换,因此例外。

正确的行为应该是:

您使用&lt;U&gt; (DateTime) 值创建结果。 jOOQ 的 MockResultSet 使用您的转换器将 &lt;U&gt; 转换为 &lt;T&gt; (Timestamp),然后再通过 JDBC API 传递该值 jOOQ 的 ResultQuery 从 JDBC API 获取 &lt;T&gt; jOOQ 使用您的转换器将&lt;T&gt; 再次转换回&lt;U&gt;

解决方法:

目前,您不应将任何用户定义类型放入您的 MockResult,而应仅使用 JDBC Timestamp 类型(并且不生成引用您的用户定义类型的列)

【讨论】:

以上是关于带有 MockResult 的 Jooq 自定义类型抛出 DataTypeException的主要内容,如果未能解决你的问题,请参考以下文章

jOOQ 不使用自定义数据绑定

在 jooq 的条件表达式中使用自定义数据类型

Jooq 与 POJO 转换器

JOOQ多字段自定义类型转换器

JOOQ - 如何将 array_agg 与自定义类型字段一起使用

jOOQ 自定义 Pojo 和 DAO 生成