1.mycat2对longdata报文直接识别为byte[],不再进行转换处理
2.对于prepare statement execute报文中的FIELD_TYPE_VARCHAR,会先使用utf8编码尝试进行转换,然后不是utf8编码,则当做byte[]处理
3.jdbc启用服务器预处理的连接字符串是
jdbc:mysql://localhost:8066/mysql?username=root&password=123456&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useServerPrepStmts=true
4.在不开启服务器预处理的情况,以下情况是不支持
Blob blob = jdbcConnection.createBlob();
blob.setBytes(1,originalBytes);
JdbcUtils.execute(jdbcConnection,"insert db1.testBlob (`blob`) values(?)",Arrays.asList(blob));
Mycat会收到带有二进制数据的sql
为了解决这个问题,有两个办法
办法1
需要开启服务器预处理
开启后,通过网络报文抓包可以观察到blob数据是额外发送的,不再嵌入的sql字符串内
办法2
com.mysql.cj.ParseInfo#ParseInfo(java.lang.String, com.mysql.cj.Session, java.lang.String, boolean)
boolean noBackslashEscapes = session.getServerSession().isNoBackslashEscapesSet();
此处决定jdbc驱动使用binary嵌入二进制还是使用16进制编码嵌入
而isNoBackslashEscapesSet是sql_mode决定的
public boolean isNoBackslashEscapesSet() {
String sqlModeAsString = this.serverVariables.get(“sql_mode”);
return sqlModeAsString != null && sqlModeAsString.indexOf(“NO_BACKSLASH_ESCAPES”) != -1;
}
所以可以设置mycat的原型库的sql_mode,使isNoBackslashEscapesSet返回true
查询现有sql_mode
SELECT @@session.sql_mode;
结果例子
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
SET GLOBAL sql_mode ='NO_BACKSLASH_ESCAPES';
//这里需要把原有的sql_mode添加上NO_BACKSLASH_ESCAPES
SET GLOBAL sql_mode ='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES'
也可以在my.cnf或者my.ini设置
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES
5.而在不开启或者开启服务器预处理的情况下,不设置sql_mode,这个情况都是支持的
byte[] originalBytes = byteArrayOutputStream.toByteArray();
JdbcUtils.execute(mycatConnection,"insert db1.testBlob (`blob`) values(?)",Arrays.asList(originalBytes));
6.如果上述方法都没有解决问题,可以使用base64编码转换byte[]到base64字符串,再插入到数据库