Mybatis 有一些奇怪的东西,并且在 Sybase 的 BigDecimal 上是空的
Something strange with mybatis and isnull on BigDecimal with Sybase
几天前,我在添加新的接口实现后发现代码中出现了一些奇怪的行为。
我看到mybatis总是更新return-1,table没有更新
日志告诉我一些有趣的行为:
- 创建新的 SqlSession
- SqlSession [...] 未注册同步,因为同步未激活
- JDBC 连接 [...] 将不会由 Spring
管理
- ==> 准备:更新 Card set amount=isnull(?, amount), name=isnull(?, name), balanceTime=isnull(?, balanceTime) where number=?和 clientId=?.
- ==> 参数:0.00(BigDecimal), Test Card(String), 2017-02-22 09:05:24.78(Timestamp), 0000000000000000(String), 111000(Long)
- 正在关闭非事务性 SqlSession [...]
向服务器发送参数后没有任何反应。
这发生在我的 DAO 被重构之后:
public class DBaseCard{
private long id;
private String number;
private String name;
private BigDecimal amount; // was Double
.....
}
在生产中我使用 Sybase IQ。对于测试,我使用 H2 db 并且一切正常。
如果我将 "amount" 字段类型更改为 Double,一切正常。
如果我保留 BigDecimal 类型并将 "amount=isnull(#{card.amount}, amount)" 替换为 "amount=#{card.amount}" 或 "amount=isnull(0.00, amount)" 之类的东西,一切正常,在日志中我看到:
- ==> 参数:0.0(BigDecimal), ....
- <== 更新:1
请帮我理解为什么会这样。
这可能与 Sybase 中的列类型有关,H2 可能具有更宽松的行为。
查看 Mybatis default type handlers 上的文档:
使用 BigDecimal:
BigDecimalTypeHandler java.math.BigDecimal Any compatible
NUMERIC or DECIMAL
加倍:
DoubleTypeHandler java.lang.Double, double Any compatible
NUMERIC or DOUBLE
它可能来自 isnull 函数。您可以尝试替换为 coalesce.
即使在日志中,您看到 0.00(BigDecimal)
,0.00
只是 Java BigDecimal 的字符串表示形式,驱动程序实际绑定的值可能与 null 同质例如 0
,有时 0==null
而 0!==null
,这可以解释为什么硬编码 0.00
按预期工作。
编写存储过程是一种选择。
你也可以考虑使用Mybatis dynamic SQL(trim,where,set)来构建SET,这样你就不用依赖在 isnull 上:
UPDATE
<set>
<if test="amount!= null">amount=#{amount},</if>
<if test="name!= null">name=#{name},</if>
<if test="balanceTime!= null">balanceTime=#{balanceTime} </if>
</set>
WHERE number=#{number} AND clientId=#{clientId}
如果你不能保证 SET 中会有一些东西(至少 1 个不是空参数),<set>
必须包含(在最后一行)一些中性的东西(没有实际更新),比如:id=id
.
我找到了我的问题的主要案例。在查看 mybatis 源代码一段时间后,我明白了,在我的情况下,Mybatis 或 SQL 本身没有任何内容。 SQL 从 ui 客户端请求工作,并且不能通过代码工作。我创建了一个简单的测试:
Connection conn = DriverManager.getConnection("conn_string", "login", "pass");
PreparedStatement ps = conn.prepareStatement("update Card set amount=isnull(?, amount), name=isnull(?, name), balanceTime=isnull(?, balanceTime) where number=? and clientId=?");
//ps.setDouble(1, 12.23);
ps.setBigDecimal(1, new BigDecimal("155.0"));
ps.setString(2, "Test test");
ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
ps.setString(4, "00000000000000");
ps.setLong(5, 111010111L);
LOG.info("execute update result: {}", ps.executeUpdate());
conn.close();
我为 'conn_string' 尝试了一些变体:“jdbc:jtds:sybase://...”和“ jdbc:sybase:Tds://...”。 jConnect 驱动程序 return 1 更新,而不是 jTDS 驱动程序 returns 0。
看起来像 jTDS 驱动程序中的错误?
几天前,我在添加新的接口实现后发现代码中出现了一些奇怪的行为。
我看到mybatis总是更新return-1,table没有更新
日志告诉我一些有趣的行为:- 创建新的 SqlSession
- SqlSession [...] 未注册同步,因为同步未激活
- JDBC 连接 [...] 将不会由 Spring 管理
- ==> 准备:更新 Card set amount=isnull(?, amount), name=isnull(?, name), balanceTime=isnull(?, balanceTime) where number=?和 clientId=?.
- ==> 参数:0.00(BigDecimal), Test Card(String), 2017-02-22 09:05:24.78(Timestamp), 0000000000000000(String), 111000(Long)
- 正在关闭非事务性 SqlSession [...]
向服务器发送参数后没有任何反应。 这发生在我的 DAO 被重构之后:
public class DBaseCard{
private long id;
private String number;
private String name;
private BigDecimal amount; // was Double
.....
}
在生产中我使用 Sybase IQ。对于测试,我使用 H2 db 并且一切正常。
如果我将 "amount" 字段类型更改为 Double,一切正常。
如果我保留 BigDecimal 类型并将 "amount=isnull(#{card.amount}, amount)" 替换为 "amount=#{card.amount}" 或 "amount=isnull(0.00, amount)" 之类的东西,一切正常,在日志中我看到:
- ==> 参数:0.0(BigDecimal), ....
- <== 更新:1
请帮我理解为什么会这样。
这可能与 Sybase 中的列类型有关,H2 可能具有更宽松的行为。
查看 Mybatis default type handlers 上的文档:
使用 BigDecimal:
BigDecimalTypeHandler java.math.BigDecimal Any compatible NUMERIC or DECIMAL
加倍:
DoubleTypeHandler java.lang.Double, double Any compatible NUMERIC or DOUBLE
它可能来自 isnull 函数。您可以尝试替换为 coalesce.
即使在日志中,您看到 0.00(BigDecimal)
,0.00
只是 Java BigDecimal 的字符串表示形式,驱动程序实际绑定的值可能与 null 同质例如 0
,有时 0==null
而 0!==null
,这可以解释为什么硬编码 0.00
按预期工作。
编写存储过程是一种选择。
你也可以考虑使用Mybatis dynamic SQL(trim,where,set)来构建SET,这样你就不用依赖在 isnull 上:
UPDATE
<set>
<if test="amount!= null">amount=#{amount},</if>
<if test="name!= null">name=#{name},</if>
<if test="balanceTime!= null">balanceTime=#{balanceTime} </if>
</set>
WHERE number=#{number} AND clientId=#{clientId}
如果你不能保证 SET 中会有一些东西(至少 1 个不是空参数),<set>
必须包含(在最后一行)一些中性的东西(没有实际更新),比如:id=id
.
我找到了我的问题的主要案例。在查看 mybatis 源代码一段时间后,我明白了,在我的情况下,Mybatis 或 SQL 本身没有任何内容。 SQL 从 ui 客户端请求工作,并且不能通过代码工作。我创建了一个简单的测试:
Connection conn = DriverManager.getConnection("conn_string", "login", "pass");
PreparedStatement ps = conn.prepareStatement("update Card set amount=isnull(?, amount), name=isnull(?, name), balanceTime=isnull(?, balanceTime) where number=? and clientId=?");
//ps.setDouble(1, 12.23);
ps.setBigDecimal(1, new BigDecimal("155.0"));
ps.setString(2, "Test test");
ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
ps.setString(4, "00000000000000");
ps.setLong(5, 111010111L);
LOG.info("execute update result: {}", ps.executeUpdate());
conn.close();
我为 'conn_string' 尝试了一些变体:“jdbc:jtds:sybase://...”和“ jdbc:sybase:Tds://...”。 jConnect 驱动程序 return 1 更新,而不是 jTDS 驱动程序 returns 0。
看起来像 jTDS 驱动程序中的错误?