0

旧版 mysql 数据库表有一个 id 列,它是非人类可读的原始 varbinary(不要问我为什么:P)

CREATE TABLE IF NOT EXISTS `tbl_portfolio` (
    `id` varbinary(16) NOT NULL,
    `name` varchar(128) NOT NULL,
    ...
    PRIMARY KEY (`id`)
);

我需要根据 java.util.UUID 选择它

jdbiReader
    .withHandle<PortfolioData, JdbiException> { handle ->
        handle
            .createQuery(
                """
                    SELECT *
                    FROM tbl_portfolio
                    WHERE id = :id
                    """
            )
            .bind("id", uuid) //mapping this uuid into the varbinary
                              //id db column is the problem
            .mapTo(PortfolioData::class.java) //the mapper out does work
            .firstOrNull()
    }

以防万一有人想看到它,这是映射器输出(但同样,映射器输出不是问题 - 将 uuid 绑定到 varbinary id db 列是)

class PortfolioDataMapper : RowMapper<PortfolioData> {

    override fun map(
        rs: ResultSet,
        ctx: StatementContext
    ): PortfolioData = PortfolioData(
        fromBytes(rs.getBytes("id")),
        rs.getString("name"),
        rs.getString("portfolio_idempotent_key")
    )

    private fun fromBytes(bytes: ByteArray): UUID {
        val byteBuff = ByteBuffer.wrap(bytes)
        val first = byteBuff.long
        val second = byteBuff.long
        return UUID(first, second)
    }

}

我已经尝试了各种方法来使绑定起作用,但没有成功 - 任何建议都非常感谢!

4

1 回答 1

0

终于让它工作了,部分归功于https://jdbi.org/#_argumentfactory它实际上专门处理 UUID 但我不知何故错过了几个小时的 JDBI 文档,哦,好吧

查询可以保持这样

jdbiReader
    .withHandle<PortfolioData, JdbiException> { handle ->
        handle
            .createQuery(
                """
                    SELECT *
                    FROM tbl_portfolio
                    WHERE id = :id
                    """
            )
            .bind("id", uuid)
            .mapTo(PortfolioData::class.java)
            .firstOrNull()
    }

但是jdbi需要注册一个UUIDArgumentFactory

jdbi.registerArgument(UUIDArgumentFactory(VARBINARY))

在哪里

class UUIDArgumentFactory(sqlType: Int) : AbstractArgumentFactory<UUID>(sqlType) {

    override fun build(
        value: UUID,
        config: ConfigRegistry?
    ): Argument {
        return UUIDArgument(value)
    }

}

在哪里

class UUIDArgument(private val value: UUID) : Argument {

    companion object {
        private const val UUID_SIZE = 16
    }

    @Throws(SQLException::class)
    override fun apply(
        position: Int,
        statement: PreparedStatement,
        ctx: StatementContext
    ) {
        val bb = ByteBuffer.wrap(ByteArray(UUID_SIZE))
        bb.putLong(value.mostSignificantBits)
        bb.putLong(value.leastSignificantBits)
        statement.setBytes(position, bb.array())
    }
    
}

请注意,像这样在整个 jdbi 实例上注册 ArgumentFactory 将使发送到 .bind 的所有 UUID 类型参数映射到可能不是您想要的字节,以防您的代码库中的其他地方有存储在 mysql 上的其他 UUID 参数以除 VARBINARY 以外的其他内容结束 - 例如,您可能有另一个表,其中有一列您的 JVM UUID 实际上存储为 VARCHAR 或其他内容,在这种情况下,您必须这样做,而不是在整个 jdbi 实例上注册 UUID ArgumentFactory,仅在适当的情况下对单个查询临时使用它。

于 2021-09-02T02:35:08.577 回答