spring mybatis insert selectkey 获取不到主键值

又是一个让同学们纠结一阵的问题,当然包括我。其实这个问题很好解决,没有技术含量,网上一大把。但,在某种特殊情况下,我的小伙伴们会惊呆了。

最终还是被我,找出破绽,解决。

先上个配置示例,跟网上其他人贴的一样

  <insert id="insert" parameterType="com.xxx.Book" useGeneratedKeys="true" keyProperty="id">
    insert into t_books(name)
    values (#{name,jdbcType=VARCHAR})
    <selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id" >
       SELECT LAST_INSERT_ID()
    </selectKey>
  </insert>
问题不在配置这里,而在使用的环境。

我使用的环境是struts2+spring3+mybatis3.2.2,很典型的三层开发架构,action,service,dao,事务控制在service层。
在action中执行以下代码,完全可获取id
BookAction:
bookService.insert(book);
System.out.println("book id = "+book.getId());     //打出正确的id

在service中执行以下代码,将获取不到id
BookServiceImpl:
bookDao.insert(book);
System.out.println("book id = "+book.getId());     //打出null
神奇吧,我不知道你们的是不是这样,总之,我这个是了。

于是开始断点调试+追踪sql执行日志,发现了端倪。当执行完 bookDao.insert(book);这行代码后,控制台打出insert 语句,
但未见select last_insert_id()执行,再执行到下一行,打印book id时,结果为null
于是继续执行到action中的下一行(println()那一行),此时见控制台,神奇的出现了select last_insert_id()。
有点明白了,应该是当事务提交时,才会去执行select last_insert_id()。因为事务在service层,只要没有离开service,事务一直有效,一旦离开service,事务立即提交。此时select last_insert_id()才会执行,并将id填充进book对象中。

这个问题造成的后果就是,在service中插入主从表关联数据时,由于不能立即获取主表id,导致插入从表数据时,不能填充主表id(有点拗口,自己体会)。
这个也就是我现在项目遇到的问题,也就是我花时间写这篇文章分享的原因。

解决办法
1.将主从表插入操作分成两个service方法,一个方法插入主表,一个方法插入从表。这样在action中,先插入主表后,就能拿到id,再传给从表。这种解决方法其实将一个事务完成的操作,拆分成两个事务完成,有数据完整性风险。
2.照样是一个service方法,完成主从表操作,但不要为此service设置事务,mybatis默认为每条执行的sql都单独开一个事务,语句执行完后,事务就提交,select last_insert_id()也会立即执行。 在我这里只要将service类的 @Transactional 注解去掉就可以了。

时间仓促,暂时没有一个好的解决方法,这里只是记录此种事件的原因,供参考!

另:再解释一下selectKey 的 order="BEFORE"时,表示  selectKey  中的语句 先执行,接着再执行insert语句,这用于oracle的sequence方式获取主键。

附:以下是我的log4j配置
log4j.rootLogger  =  debug, stdout
log4j.appender.stdout  =  org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout  =  org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern  =  %d %5p [%c] - %m%n

log4j.logger.org.apache.ibatis  =  DEBUG
log4j.logger.org.mybatis.spring  =  DEBUG
log4j.logger.org.mybatis.spring.SqlSessionUtils  =  WARN


你可能感兴趣的:(spring,mybatis)