【发布时间】:2017-05-24 20:23:44
【问题描述】:
背景与问题
我公司决定从 MySQL 驱动改为 MariaDB 驱动/连接器,仍然使用 MySQL Server DB 作为 Spring App 的数据库服务器。
在此迁移过程中,我发现了与驱动程序/连接器如何处理日期相关的问题,这导致了以下错误:
Illegal mix of collations for operation '<='
在运行以下查询时会发生这种情况,请注意查询最后一行的日期比较。
String sql2 = "select coalesce(sum(b.value), 0) " +
" from acc_sub_account_booking b " +
" join acc_sub_account sa ON sa.id = b.subAccount_id " +
" join km_cash_bond cb ON cb.virtualPayInAccountNumber = sa.iban " +
" join km_rented_object ro ON ro.id = cb.rentedObject_id " +
" where b.bookingType in ('PAY_IN', 'PAY_OUT') " +
" and ro.id = :rentedObjectId " +
" and b.valueDate <= :today";
Object sum1 = entityManager.createNativeQuery(sql).
setParameter("rentedObjectId", pRentedObject.getId()).
setParameter("today", new LocalDateTime(d.getYear(), d.getMonthOfYear(), d.getDayOfMonth(), 23, 59)).
getSingleResult();
版本:
- Java:7
- MySQL:5.5
- 玛丽亚驱动程序:1.4.6
了解
看了MySQL documentation之后提到:
MySQL Connector/J 在处理 MySQL 数据类型和 Java 数据类型之间的转换方面非常灵活。
一般来说,任何 MySQL 数据类型都可以转换为 java.lang.String,任何数字类型都可以转换为任何 Java 数字类型,尽管可能会出现舍入、溢出或精度损失。
也许之前没有发生该错误,因为 MySQL 连接器处理了 String 和 Date 之间的转换,而现在该错误是由于尝试比较两种不同的数据类型而导致的。
使用 MariaDB 作为服务器时不会出现此错误,可能是 Maria 中的转换处理是在数据库级别而不是在连接器中完成的。
测试
我认为是编码/字符集问题,将所有表和列都更改为utf8_general_ci。 这并没有解决问题。
我运行了一些测试 - 在通过应用程序运行查询时始终使用 MariaDB 驱动程序 - 并得到以下结果:
虽然使用 MariaDB 时测试通过,但这不是一种选择。
如果查询参数转换为日期:
... and b.valueDate <= DATE(:today)";,则测试 #3 和 4 有效,但这意味着对代码进行许多更改。测试#2 是(唯一失败的并且)我想遵循的选项,因为它意味着更改量较少。但是,我似乎无法让它工作。
?问题?
有没有办法使用 MySQL 和 MariaDB 连接器而不会导致此问题?
有没有比将
DATE(:today)的所有参数强制转换为迄今为止更好的选择?另一种解决方案?谢谢。
更新:
这些是代码中设置的数据源属性:
dataSource.setJdbcUrl("jdbc:mysql://"+ hostname + ":3306/"
+ databaseName +
"?useUnicode=true&" +
"characterEncoding=utf-8");
更新 #2:
更多信息:
还执行了以下集合:
ALTER DATABASE km CHARACTER SET utf8 COLLATE utf8_general_ci;
SET collation_connection = 'utf8_general_ci';
SET collation_server = 'utf8_general_ci';
另外,每个INFORMATION_SCHEMA.COLUMNS 和INFORMATION_SCHEMA.TABLES 都是CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;。
@elenst 解决步骤:
- 启用general_log;
- 确认使用 MySQL 连接器时
NAMES和character_set_results的值相同(latin1 和 NULL); - 切换回 MariaDB 并设置这些值
- 使用应用程序/查询运行,错误仍然存在。
- 我也试过
?sessionVariables=character_set_client=latin1和?sessionVariables=character_set_client=utf8,结果是一样的。
@DiegoDupin 无法将Timestamp.valueOf() 应用于Joda Time LocalDateTime。你可以把它包装起来:
-
setParameter("today", Timestamp.valueOf(String.valueOf(new LocalDateTime(d.getYear(), d.getMonthOfYear(), d.getDayOfMonth(), 23, 59))))但会导致时间戳格式错误。 -
不过
setParameter("today", new LocalDateTime(d.getYear(), d.getMonthOfYear(), d.getDayOfMonth(), 23, 59).toDate())会工作。
问题依然存在,系统的其他部分,由于驱动的变化,仍然可以分担这个故障。
@RickJames:SHOW CREATE TABLE acc_sub_account_booking。比较是在 valueDate 字段上完成的,输入 date,尽管在另一个(类似)查询中存在 datetime 字段也会发生同样的情况。
| acc_sub_account_booking | CREATE TABLE `acc_sub_account_booking` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`creationDate` datetime DEFAULT NULL,
`lastModifiedDate` datetime DEFAULT NULL,
`bookingText` varchar(255) DEFAULT NULL,
`bookingType` varchar(255) DEFAULT NULL,
`uuid` varchar(255) DEFAULT NULL,
`value` decimal(19,2) DEFAULT NULL,
`valueDate` date DEFAULT NULL,
`zkaGVC` int(4) NOT NULL,
`subAccount_id` bigint(20) DEFAULT NULL,
`bankStatementDate` date DEFAULT NULL,
`counterpartHolder` varchar(255) DEFAULT NULL,
`counterpartIban` varchar(255) DEFAULT NULL,
`customerReference` varchar(255) DEFAULT NULL,
`endToEndReference` varchar(255) DEFAULT NULL,
`returnReason` varchar(255) DEFAULT NULL,
`customerSpecificInformations` text,
`counterpartBic` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `FK_SAB_SA` (`subAccount_id`),
CONSTRAINT `FK_SAB_SA` FOREIGN KEY (`subAccount_id`) REFERENCES `acc_sub_account` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3407 DEFAULT CHARSET=utf8 |
最终更新:
这里列出的问题可以通过转换为Java Date 对象来解决,而不是直接使用JodaTime:
setParameter("today", new LocalDateTime(d.getYear(), d.getMonthOfYear(), d.getDayOfMonth(), 23, 59).toDate())
尽管如此,还是发现了其他问题,最后我公司决定恢复使用 MariaDB 驱动程序的决定。
非常感谢所有愿意花时间提供帮助的人。
【问题讨论】:
-
valueDate的数据类型是什么? (或SHOW CREATE TABLE):today被替换后的sql可以显示吗? -
您能否启用查询日志并查看服务器端实际收到的内容? LocalDateTime 没有在驱动程序中处理,也许它是由 Spring 以某种方式处理的,对此并不熟悉。但也许你最好使用 java.sql.Timestamp 或类似的东西。
-
@VladislavVaintroub 查询日志未显示“已翻译”查询:15 准备 select coalesce(sum(u.betrag), 0) from km_rented_object ro join km_cash_bond cb ON cb.rentedObject_id = ro。 id join konto k ON k.uuid = cb.truster_account_uuid join umsatz u ON u.konto_id = k.id where ro.id = ?和 u.vorfallKennung 在 (100, 200) 和 u.buchungszeitpunkt
-
好吧,那么它是准备好的语句,服务器端。您可以在 JDBC URL 中使用 useServerPrepStmts=false 来确保客户端准备。但它可能会向您显示序列化的二进制 LocalDateTime。因为这就是此驱动程序中的 setObject 对未知类型的工作方式。
-
buchungszeitpunkt?或b.valueDate??
标签: java mysql jdbc database-connection mariadb