ResultSet.updateRow() 产生“用于操作'<=>'的排序规则(latin1_bin,IMPLICIT)和(utf8_general_ci,COERCIBLE)的非法混合”
Posted
技术标签:
【中文标题】ResultSet.updateRow() 产生“用于操作\'<=>\'的排序规则(latin1_bin,IMPLICIT)和(utf8_general_ci,COERCIBLE)的非法混合”【英文标题】:ResultSet.updateRow() produces "Illegal mix of collations (latin1_bin,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '<=>'"ResultSet.updateRow() 产生“用于操作'<=>'的排序规则(latin1_bin,IMPLICIT)和(utf8_general_ci,COERCIBLE)的非法混合” 【发布时间】:2016-06-14 06:56:02 【问题描述】:我有下表,name
为 LATIN1,其余为 UTF8。
CREATE TABLE `test_names` (
`name` varchar(500) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`other_stuff_1` int DEFAULT NULL,
`other_stuff_2` varchar(45) DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
我在Java中遇到以下问题:
我SELECT ... FOR UPDATE
。然后我在它的 ResultSet 上调用updateInt(2, 1)
和updateRow()
并得到Illegal mix of collations (latin1_bin,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '<=>'
。
如何在不更改表/连接字符集的情况下完成这项工作?
非常感谢。
--- 更新 ---
我使用SELECT name, other_stuff_1 FROM test_names LIMIT 1 FOR UPDATE;
,连接字符串是DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + db + "?allowMultiQueries=true", user, password);
。
确切的堆栈跟踪是:
java.sql.SQLException: Illegal mix of collations (latin1_bin,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '<=>'
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4169)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2617)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2834)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2441)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2366)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2350)
at com.mysql.jdbc.UpdatableResultSet.updateRow(UpdatableResultSet.java:2405)
【问题讨论】:
请提供完整的SELECT
语句,以及交易中的其他语句。请提供连接MySQL时使用的连接参数。
已更新。谢谢你的帮助,@RickJames。
将SELECT
语句更改为SELECT name, other_stuff_1 FROM test_names LIMIT 1 ORDER BY name COLLATE latin1_bin FOR UPDATE
。我没有测试这个语句,它需要 MySQL 5.7 或更高版本。
非常感谢您的回答,@beckyang。我试过ORDER BY name COLLATE latin1_bin LIMIT 1 FOR UPDATE
(ORDER BY 在 LIMIT 前面),但不幸的是仍然收到java.sql.SQLException: Illegal mix of collations (latin1_bin,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '<=>'
。
可能是 Connector/J 中的一个错误——您使用的似乎是 v5.1.29,而当前的 GA 版本是 v5.1.38。您可以尝试使用最新版本吗?
【参考方案1】:
从我这边,我可以给你一些建议
-
首先更新您的 Connector/J 最新版本。
运行此查询
SET NAMES='utf8'
将&characterEncoding=UTF-8
添加到您的 JDBC 连接字符串中。希望你已经做到了。
使用convert() for insert or update
和cast() for select
查询。更多详情http://dev.mysql.com/doc/refman/5.7/en/charset-convert.html
【讨论】:
【参考方案2】:问题是您试图从不同列上具有不同字符集的表中进行选择。
您必须使用CONVERT()
将查询中具有不同字符集的列转换为正确的字符集。
http://dev.mysql.com/doc/refman/5.7/en/charset-convert.html
在您的情况下,您应该将查询修改为:
SELECT CONVERT(name USING latin1), other_stuff_1 FROM test_names LIMIT 1 FOR UPDATE;
【讨论】:
非常感谢您的回答。使用CONVERT
不起作用,因为它使ResultSet
不可更新;我不能再更新other_stuff_1
。错误消息为com.mysql.jdbc.NotUpdatable: Result Set not updatable (references computed values or doesn't reference any columns or tables).This result set must come from a statement that was created with a result set type of ResultSet.CONCUR_UPDATABLE, the query must select only one table, can not use functions and must select all primary keys from that table. See the JDBC 2.1 API Specification, section 5.6 for more details.
ResultSet
是用ResultSet.TYPE_FORWARD_ONLY
和ResultSet.CONCUR_UPDATABLE
创建的。由于转换而出现错误。但这绝对是正确的做法。我不知何故需要告诉 Java 这个数据库中的 PK 是 LATIN1。【参考方案3】:
恕我直言,在这种情况下,当表中的编码不一致时(例如,主键在 latin1 中,而其他列在 utf8 中),您不能使用可更新的结果集。
正如 Andras 所指出的,您可以在 sql 端转换编码,但您将无法更新结果集。
您为什么不简单地使用 executeUpdate(...) 更新表?您可以通过简单的选择缩小目标行,然后遍历生成的主键列表并调用 executeUpdate。
对我来说,这适用于混合编码列。只是一个例子:
conn.setAutoCommit(false);
ResultSet rs = st.executeQuery("SELECT name other_stuff_1 FROM test_names");
List<String> keys = new ArrayList<String>();
while(rs.next())
keys.add(rs.getString(1));
rs.close();
for(String key : keys)
st.executeUpdate("update test_names set other_stuff_1='"+key.length()+"', other_stuff_2='" + key.toUpperCase() + "' where name='" + key + "'");
conn.commit();
【讨论】:
以上是关于ResultSet.updateRow() 产生“用于操作'<=>'的排序规则(latin1_bin,IMPLICIT)和(utf8_general_ci,COERCIBLE)的非法混合”的主要内容,如果未能解决你的问题,请参考以下文章