使用数据库:sqlServer
开发语言:Java
场景:原本使用正常运行的mybaits获取
<insert id="insert" parameterType="com.sl.pt.base.model.Customer" >
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
SELECT @@IDENTITY
</selectKey>
insert into dbo.BASE_CUSTOMER (...)
values(...)
</insert>
但是客户提出需要添加一个触发器,新增的时候同时添加另一个表的数据,写好后发现在控制台运行没有问题,触发器什么都正常运行,但是在程序中每次都报错。
解决步骤
我是在网上不断的找各种资料,发现一位大佬说的蛮好的
COPE_IDENTITY 和 @@IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回插入到当前作用域中的值;@@IDENTITY 不受限于特定的作用域。例如,有两个表 T1 和 T2,在 T1 上定义了一个 INSERT 触发器。当将某行插入 T1 时,触发器被激发,并在 T2 中插入一行。此例说明了两个作用域:一个是在 T1 上的插入,另一个是作为触发器的结果在 T2 上的插入。 假设 T1 和 T2 都有 IDENTITY 列,@@IDENTITY 和 SCOPE_IDENTITY 将在 T1 上的 INSERT 语句的最后返回不同的值。 @@IDENTITY 返回插入到当前会话中任何作用域内的最后一个 IDENTITY 列值,该值是插入 T2 中的值。SCOPE_IDENTITY() 返回插入 T1 中的 IDENTITY 值,该值是发生在相同作用域中的最后一个 INSERT。如果在作用域中发生插入语句到标识列之前唤醒调用 SCOPE_IDENTITY() 函数,则该函数将返回 NULL 值。
然后他给出了个他的(正确)方法
<insert id="addUser" parameterClass="user">
select 1;
<selectKey resultClass="int" keyProperty="id" type="post">
INSERT INTO t_user (name,sex)VALUES (#name#,#sex#)
SELECT SCOPE_IDENTITY() AS ID
</selectKey>
</insert>
网址(建议查看源代码的方式看这篇文章):https://www.iteye.com/problems/20809
我也是照个这个他这个思路做的,把insert放到<selectKey>标签里面,insert后面放入个SELECT SCOPE_IDENTITY() ,这样SCOPE_IDENTITY()就和insert在同一个域里面了,返回的就是控制,而外面(<insert>标签内)则放入select 1,也有人说什么都不放,就空着,但是我测试发现总是报错。
于是想到<insert><selectKey></selectKey></insert>的原理上入手,没找到。。。就自己思考了下,其实无外乎<insert>标签内执行新增前或后调用<selectKey>标签获取返回值,突然想明白了,既然如此,<insert>标签内里面写select 1当然是有问题的,<insert>标签里面应该写insert语句才不报错嘛,于是立马创建了一张表BASE_TPL,讲语句改为如下
<insert id="insert" parameterType="com.sl.pt.base.model.Customer" >
insert into dbo.BASE_TPL(ID,NAME) VALUES('1','1')
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
insert into dbo.BASE_CUSTOMER (...)
values(...) SELECT SCOPE_IDENTITY()
</selectKey>
</insert>
这样写了以后发现可以解决了,但是我想随着数据的增多,BASE_TPL表里面的数据也变多了,于是在触发器中写了个删除BASE_TPL的语句。
虽然问题是解决了,但是平白无故多出一张没用的表,还是感觉不太好,如果有更好的方法,希望各位提出来。