MyBatis version
3.5.5
Database vendor and version
MS Sql Server 2008 R2,MS Sql Server 2019
com.microsoft.sqlserver:mssql-jdbc:8.4.0.jre8
com.microsoft.sqlserver:mssql-jdbc:7.4.1.jre8
Test case or example project
Table:
CREATE TABLE [dbo].[student](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
CONSTRAINT [PK_student] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
Entity:
public class Student implements Serializable {
private static final long serialVersionUID = -9113130818289196330L;
private Integer id;
private String name;
}
dao:
public interface StudentMapper {
int batchInsert(List list);
}
mapper:
insert into student (name) values
(#{item.name,jdbcType=VARCHAR})
test:
@Test
void batchInsert() {
Student student1=new Student();
student1.setName("Jack");
Student student2=new Student();
student2.setName("Tom");
Liststudents=new ArrayList<>();
students.add(student1);
students.add(student2);
studentMapper.batchInsert(students);
students.forEach(
e->{
log.info("{}",e.getId());
}
);
}
Expected result
10
9
Actual result
2020-08-03 22:42:35.691 INFO 1940 --- [ Test worker] c.e.testbatch.dao.StudentMapperTest : 10
2020-08-03 22:42:35.692 INFO 1940 --- [ Test worker] c.e.testbatch.dao.StudentMapperTest : null
在Mysql下,返回正常,但是在MS Sql Server里,只返回批量插入的最后一条的自增主键。在Mybatis上提交了issues,回复说:
This is a limitation from SQL Server's end and there's not much the driver can do. After speaking with the SQL Server team, changing the server response to return all generated keys instead of the last one (similar to OracleDB for example) would not be a trivial task, and the change likely won't be made as it will break a lot of existing design. However, we're keeping this issue open for future references.
后面也给出了解决方法,就是使用Sql Server 2005开始的特性:output
To add a bit more background to this problem - currently the driver attempts to get the generated keys by selecting SCOPE_IDENTITY() after the user's query. However, this only returns the last generated value, and if we want to return all generated values from a statement, we would have to use an OUTPUT clause.
The problem with this approach is that we would be modifying the user's statement to put an OUTPUT clause in the middle of the statement (as per OUTPUT syntax), and we don't want our driver to modify the user's query. Even if the driver were to parse the user's statement to put an OUTPUT clause in it, it would not be a trivial task to parse the user's query since it needs to find where in the middle of the statement the clause would need to be inserted. Also, since only one OUTPUT clause is allowed, if the user's statement already contains an OUTPUT clause, we wouldn't be able to put another OUTPUT clause in there.
Note that this is a lot easier on OracleDB's end due to syntactical differences, where their equivalent of OUTPUT clause (RETURNING ROW ID INTO) comes at the end of a statement. I hope this explains why for now this issue is not being solved by the team for the time being
照此解决方法为:
Dao:
List batchInsert(List list);
Mapper: