问题背景
我们在写业务需求的时候遇到这样一个场景:批量插⼊⼀批数据到⼀张表中,然后获取到插⼊的Id,并将这些Id返回。
⾸先mybatis是⽀持单个数据插⼊返回Id的,实际上,mybatis也⽀持批量插⼊返回Id
<!-- 批量新增 -->
<insert id="batchInsert" parameterType="java.util.List"
useGeneratedKeys="true" keyProperty="id" >
INSERT INTO
<include refid="t_shop_resource" />
(relation_id, summary_id, relation_type)
VALUES
<foreach collection="list" index="index" item="shopResource"
separator=",">
(
#{shopResource.relationId}, #{shopResource.summaryId}, #
{shopResource.relationType}
)
</foreach>
</insert>
复制代码
如果要使用批量插入返回id,需要升级Mybatis版本到3.3.1。因为官⽅在这个版本中加⼊了批量新增返回主键id的功能。
但是升级mybatis版本需要升级对应的mybatis-spring版本。否则会出现版本兼容问题。修改pom⽂件
<mybatis.version>3.4.0</mybatis.version>
<mybatis-spring.version>1.3.0</mybatis-spring.version>
复制代码
结果发现启动的时候报错,提示:
Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
复制代码
问题原因
排查思路
-
根据错误⽇志Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required的提示 定位到SqlSessionDaoSupport类。
-
我们使⽤的项⽬是基于GenericDAO的⽼项⽬,所有的Dao实现类都继承GenericDAOImpl,⽽GenericDAOImpl⼜继承SqlSessionDaoSupport类。
-
SqlSessionDaoSupport类的实现如下所示:
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
private boolean externalSqlSession;
public SqlSessionDaoSupport() {
}
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
public SqlSession getSqlSession() {
return this.sqlSession;
}
protected void checkDaoConfig() {
Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or
'sqlSessionTemplate' are required");
}
}
复制代码
- 可以看到报错是在checkDaoConfig⾥报的。SqlSessionDaoSupport继承了DaoSupport,DaoSupport继承了InitializingBean,在初始化之后,会去调⽤checkDaoConfig⽅法,结果由于我们没有注⼊SqlSessionFactory,因此就没有校验通过。
public abstract class DaoSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
public DaoSupport() {
}
public final void afterPropertiesSet() throws IllegalArgumentException,
BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO
failed", var2);
}
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;
protected void initDao() throws Exception {
}
}
复制代码
那么为什么⽼版本没有问题呢,再次将版本回退,再次进⼊该类观察:
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSession sqlSession;
private boolean externalSqlSession;
public SqlSessionDaoSupport() {
}
@Autowired(
required = false
)
public final void setSqlSessionFactory(SqlSessionFactory
sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
@Autowired(
required = false
)
public final void setSqlSessionTemplate(SqlSessionTemplate
sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
}
public final SqlSession getSqlSession() {
return this.sqlSession;
}
protected void checkDaoConfig() {
Assert.notNull(this.sqlSession, "Property 'sqlSessionFactory' or
'sqlSessionTemplate' are required");
}
}
复制代码
- 可以看到在⽼版本中,是会⾃动注⼊sqlSessionFactory和sqlSessionTemplate的,⽽新版本取消了这⼀做法,需要我们⼿动注⼊。(mybatis-spring 1.2.0后取消)。
解决方法
1、新写⼀个公共⼦类,然后所有的Dao继承这个公共⼦类,公共⼦类继承GenericDAOImpl,⼿动注⼊SqlSessionFactory
public class CommonDao extends GenericDAOImpl {
@Resource
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){
super.setSqlSessionFactory(sqlSessionFactory);
}
}
复制代码
所有的dao实现类继承CommonDao。
@Repository
class UserDaoImpl implements UserDao extends CommonDao {
}
复制代码
2、但是基于CommonDao的思路,改动量过多,影响范围不可控制,因此最终选择不升级mybatis版本。批量插⼊返回Id,采⽤先插⼊再根据唯⼀主键查询的⽅式。 由于Dao继承GenericDao的⽅式代码冗余过多且不够灵活,因此新项⽬⼀般使⽤mybatis-plus。