在进行批量处理的时候,在后台发现报错,日志部分如下:
bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Syntax error:
出现了badSQlgrammar错误,在(某一个表中)出现了delete错误。
由于在批量操作的时候,我们是通过生成多条SQL语句,然后进行拼接,再用Mybatis进行处理,但是遇到了问题。
<update id="setWeiboEmotionByList" parameterType="java.util.List">
<foreach collection="list" item="item" open="" close="" separator=";">
UPDATE weibo_content
SET
EMOTION = #{item.emotion}
WHERE
WEIBO_ID = #{item.weibo_id}
</foreach>
</update>
类似上面的操作方式,使用foreach来对list中的语句进行拼接和遍历最后多条SQL语句进行执行
由上图所示,但是却触发了JDBC4 的MySQLSyntaxErrorException,经过资料查询,观察MySQLSyntaxErrorException实例:
public void create(Project project) throws IllegalArgumentException, DAOException {
if (project.getProjectId() != 0) {
throw new IllegalArgumentException("Project is already created, the project ID is not null.");
}
Object[] values = { project.getProjectNumber(), project.getProjectName(),
project.getActivity(), project.getPerNumber(),
toSqlDate(project.getDate()), project.getTime()};
try (Connection connection = daoFactory.getConnection();
PreparedStatement statement = prepareStatement(connection,
SQL_INSERT_PROJECT, true, values);) {
int affectedRows = statement.executeUpdate();
if (affectedRows == 0) {
throw new DAOException("Creating user failed, no rows affected.");
}
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
project.setProjectId(generatedKeys.getLong(1));
} else {
throw new DAOException("Creating user failed, no generated key obtained.");
}
}
} catch (SQLException e) {
throw new DAOException(e);
}
}
可以看到,出错原因有大概如下原因:
- 1、其中SQL语句有的出错导致。
- 2、Mybatis中字段部分与SQl关键字冲突。
- 3、由于是多条执行(delete xxx……;delete xxx……; delete xxx……;),可能Mybatis不能支持这种批量方式。
- 4、对于参数传值的时候,使用"#{}“和使用”${}"传值时,会出现不同的问题。**
对这四种错误进行排查:
- 通过在Navicat中执行每条SQL,发现并无报错,应该不是SQL语句有问题。
- 在其他单条SQL语句处理和批量查找、批量更新都发现没有此问题。
- Mybatis是默认不支持这种操作的,但是我们可以通过在mysql配置链接里加上allowMultiQueries参数并置为true。但是还是报同样的错误(也可能和服务器端的配置有关,需要进一步查看)。
- 很明显全部使用的"#{}",当使用$时是默认无防注入的。其中#{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
表示拼接sql串,通过{}可以将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换,$ {}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,$ {}括号中只能是value。
究其原因(一般原因):
经过查找资料,发现大多数问题都是处在第三条上——对与adllowMultiQueries参数没有置为true,
但是,我们使用的是OceanBase的数据库,虽然理论上支持MySQL中的所有语句,但是在解决问题的两天后,我在阿里巴巴官方文档中看到了:
确实,这个问题解决了,但是下一个问题出现了:如何换一种方案呢
经过大佬们的商议,决定根据偏移量来进行Mybatis分段的批量操作,
后面继续谈。