【mybatis】mybatis调用sqlserver存储过程和表值函数

本文详细解析了存储过程和表值函数的区别,重点强调了两者在MyBatis调用方式上的不同。存储过程以pr开头,通常用于执行复杂的数据库操作并返回单一结果;而表值函数以fn开头,主要用来返回表结构的数据集。文章还提供了具体的XML配置示例,展示了如何在MyBatis中正确调用这两种数据库对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

存储过程和表值函数的区别

最简单的区别是存储过程是pr开头的,而存储过程是fn开头的,表值函数返回的是一个表结构的结果。如果mybatis用调用存储过程的方式去调用表值函数,就会抛出以下的异常:

exception:不能调用'xxxxx',因为'xxxx'是表值函数对象。

调用存储过程

如果要调用一个叫做“pr_test_a”的存储过程,需要传入参数并且有返回值。如下所示,resultMap中存的是返回值的字段名和类型,在select中调用存储过程,传入对应的参数

<resultMap type="java.util.HashMap" id="resultMap">
	<result column="id" property="id" javaType="java.lang.Integer" jdbcType="INTEGER"/>
	<result column="code" property="code" javaType="java.lang.String" jdbcType="VARCHAR"/>
	<result column="name" property="name" javaType="java.lang.String" jdbcType="VARCHAR"/>
	<result column="modified_time" property="modifiedTime" javaType="java.sql.Timestamp" jdbcType="TIMESTAMP"/>
</resultMap>

<select id="selectA" parameterType="java.util.HashMap" statementType="CALLABLE" resultMap="resultMap">
	{
    	call pr_test_a(
    	#{id,mode=IN,jdbcType=INTEGER},
    	#{card_no,mode=IN,jdbcType=VARCHAR}
	}
</select>

调用表值函数

就当做一张表来查询即可。如调用一个表值函数fn_test_b,返回结果到resultMap中。

<resultMap type="java.util.HashMap" id="resultMap">
    <result column="fullid" property="fullid" javaType="java.lang.String" jdbcType="VARCHAR"/>
    <result column="seq" property="seq" javaType="java.lang.String" jdbcType="VARCHAR"/>
    <result column="material_id" property="materialId" javaType="java.lang.Integer" jdbcType="INTEGER"/>
</resultMap>

<select id="fn_bom_decomposition" parameterType="java.util.HashMap" resultMap="resultMap">
    select * from fn_test_b(
        #{material_id,mode=IN,jdbcType=INTEGER},
        #{quantity,mode=IN,jdbcType=NUMERIC},
        #{start_time,mode=IN,jdbcType=TIMESTAMP}
    )
</select>
<think>我们正在使用MyBatis进行批量插入操作,并希望从SQL Server数据库中获取所有插入记录的主键值。在MyBatis中,我们通常使用`useGeneratedKeys``keyProperty`来获取自增主键。但在批量插入时,情况会复杂一些,因为需要为多个记录获取主键。根据引用[3]的内容,SQLServer的JDBC驱动在执行批量操作时,如果要获取自动生成的主键,通常需要在INSERT语句中使用OUTPUT子句。但是,MyBatis默认生成的批量插入语句并不包含OUTPUT子句,而且直接修改SQL语句来添加OUTPUT子句可能会很复杂,并且如果用户语句中已经包含OUTPUT子句,则无法再添加。然而,我们可以考虑以下方法:1.使用MyBatis的`@Options`注解或`<insert>`标签的`useGeneratedKeys``keyProperty`属性,并配合`@InsertProvider`或`<insert>`标签编写包含OUTPUT子句的SQL语句。2.使用SQLServer的SCOPE_IDENTITY()函数只能获取最后插入的标识值,不适用于批量插入。因此,我们需要使用OUTPUT子句来返回插入的多个主键。3.在MyBatis中,我们可以使用`<foreach>`标签来构建批量插入语句,并在其中使用OUTPUT子句。但是,需要注意的是,在SQL Server中,使用OUTPUT子句返回结果集时,不能直接将结果集映射到keyProperty。因此,我们需要调整Mapper接口XML配置。具体步骤:步骤1:在Mapper接口中定义插入方法,返回值为插入的主键列表(或影响的行数,但我们需要主键)。步骤2:在XML映射文件中编写INSERT语句,使用OUTPUT子句返回插入的主键值。步骤3:由于MyBatis不支持直接将OUTPUT返回的结果集赋值给keyProperty,我们可以将结果集作为查询结果返回,然后在Mapper接口中接收。示例:假设我们有一个表`t_user`,主键为`id`(自增),其他字段为`name`。Mapper接口: ```javapublicinterface UserMapper {@Insert({"INSERT INTOt_user(name)","OUTPUT INSERTED.id","VALUES(#{name})"})@Options(useGeneratedKeys =true,keyProperty= "id")IntegerinsertUser(User user);//批量插入@InsertProvider(type =UserSqlProvider.class, method= "insertUsers")@Options(useGeneratedKeys =true,keyProperty= "ids")voidinsertUsers(@Param("list") List<User>users);} ```但是,上述单个插入可以工作,批量插入则不行。因为MyBatis默认的批量插入foreach并不支持OUTPUT子句返回多个主键并赋值给每个对象的属性。另一种做法是使用单独的SQL语句,在批量插入中返回所有生成的主键,然后手动设置到对象中。但是,这需要我们在一个事务中执行,并且需要确保顺序。我们可以这样写批量插入:```xml<insert id="insertUsers"useGeneratedKeys="true"keyProperty="id">INSERTINTO t_user (name)OUTPUTINSERTED.idVALUES<foreachcollection="list"item="item"separator=",">(#{item.name})</foreach></insert> ```然而,在SQL Server中,上述语句在批量插入时使用OUTPUT子句并配合VALUES列表是允许的,它会返回所有插入的id。但是,MyBatis需要能够将这些id设置到每个对象的keyProperty(即id)属性中。但是,MyBatis在处理批量插入的keyProperty时,要求数据库驱动支持返回多个自增主键。SQLServer的JDBC驱动支持通过OUTPUT子句返回多个主键,而MyBatis从3.3.0版本开始支持批量插入返回自增主键(需要数据库驱动支持)。因此,我们可以尝试以下配置:在MyBatis的XML映射文件中:```xml<insert id="insertUsers"useGeneratedKeys="true"keyProperty="id">INSERT INTOt_user(name)VALUES<foreach collection="list" item="item" separator=",">(#{item.name})</foreach> </insert>```注意:这里并没有显式使用OUTPUT子句,因为MyBatis会自动处理?实际上,对于SQLServerMyBatis会使用JDBC的getGeneratedKeys方法来获取主键,而JDBC驱动在内部可能会使用OUTPUT子句或SCOPE_IDENTITY(),但批量时通常使用OUTPUT。但是,根据引用[3]中的讨论,SQLServer的JDBC驱动在执行批量插入时,如果要获取生成的主键,需要在连接字符串中设置`useBulkCopyForBatchInsert=false`(默认是false),因为批量复制操作(BulkCopy)不支持返回生成的主键。因此,我们需要确保连接字符串中没有设置`useBulkCopyForBatchInsert=true`。同时,我们还需要注意,使用`<foreach>`拼接的批量插入在数据量很大时可能会超出SQL语句的长度限制,此时可以考虑分批次插入。另外,如果使用MyBatis执行批量插入,还可以使用ExecutorType.BATCH来执行批量操作,但这种方式下,获取主键可能会遇到问题,因为JDBC的批处理操作在返回主键方面支持有限。所以,总结如下:方案1:使用foreach拼接单个INSERT语句(适用于数据量不是特别大的情况)```xml<insertid="insertUsers" useGeneratedKeys="true" keyProperty="id">INSERTINTO t_user (name)VALUES<foreach collection="list" item="item" separator=",">(#{item.name})</foreach> </insert>```方案2:使用ExecutorType.BATCH,并配合在INSERT语句中使用OUTPUT子句返回主键,然后在Mapper方法中返回主键列表,并手动设置到对象中(较为复杂)。由于方案1简单且常用,我们优先尝试方案1。但是,请注意:SQL Server对单个INSERT语句插入多行记录时,使用VALUES列表是有限制的(最多1000行)。如果超过,需要分批次插入。另外,确保SQLServer的JDBC驱动版本支持此功能(较新的驱动都支持)。如果使用方案1,在插入后,传入的List<User>中的每个User对象的id属性会被自动设置为对应的主键值。示例代码:UserMapper.xml: ```xml<mappernamespace="com.example.mapper.UserMapper"><insertid="insertUsers" useGeneratedKeys="true" keyProperty="id">INSERTINTO t_user (name)VALUES<foreachcollection="list"item="user" separator=",">(#{user.name})</foreach></insert> </mapper>```在Java中调用:```javaList<User>users =new ArrayList<>(); //添加多个User对象userMapper.insertUsers(users);//此时,每个User对象的id已经被设置```如果遇到问题,可能需要检查:1.数据库连接字符串中是否设置了`useBulkCopyForBatchInsert=true`,如果设置为true,则不会返回主键,需要改为false(或删除,因为默认是false)。2. SQLServer版本是否支持(SQLServer2008及以上支持一次插入多行)。3.主键必须是自增列。如果数据量很大,可以分批次插入,例如每次插入1000条。因此,对于用户的需求,推荐使用foreach拼接的方式,并设置useGeneratedKeyskeyProperty,同时确保JDBC驱动能够返回生成的主键(SQLServer JDBC驱动默认支持通过OUTPUT子句返回,但需要避免使用批量复制模式)。引用说明:引用[3]提到,直接修改用户语句添加OUTPUT子句是困难的,但MyBatis通过JDBC驱动的getGeneratedKeys方法内部处理了这个问题,因此我们不需要显式添加OUTPUT子句(除非特殊需求)。在MyBatis中,我们只需要配置useGeneratedKeys即可。但是,根据SQLServer JDBC驱动的文档,当使用批量插入(多个VALUES)时,驱动会自动使用OUTPUT子句来返回生成的主键。因此,我们按照方案1即可。注意:在MyBatis官方文档中,对于批量插入获取主键,有类似的示例:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#insert_update_and_delete文档中说明:“对于支持自动生成记录主键的数据库(如 MySQLSQL Server),设置useGeneratedKeys=”true”,然后把keyProperty设置为目标属性就 OK了。”因此,我们可以放心使用。但是,如果使用ExecutorType.BATCH(即SqlSession的批处理模式),则不能使用foreach拼接,而需要循环执行单个插入,此时也可以获取主键,但性能可能不如方案1(因为需要多次执行SQL)。所以,在数据量允许的情况下,方案1是首选。如果数据量非常大,可能需要考虑使用SQLServer的Bulk Copy,但BulkCopy不返回主键,所以需要其他方式生成主键(例如使用UUID,或者提前从序列获取一批主键值)。综上所述,我们给出如下解决方案:1.使用MyBatis的foreach标签拼接批量插入语句。2.在insert标签中设置useGeneratedKeys="true"keyProperty="id"(假设对象的主键属性为id)。3.确保SQLServer JDBC驱动连接字符串中没有设置useBulkCopyForBatchInsert=true(因为这会禁用返回主键的功能)。4.如果数据量超过1000条,分批次插入(每批1000条)。代码示例:分批次插入的Java代码: ```javapublic voidbatchInsert(List<User> users) {intbatchSize=1000;for (int i=0;i <users.size(); i+= batchSize){List<User> batchList= users.subList(i,Math.min(i +batchSize, users.size()));userMapper.insertUsers(batchList);} }```这样,每个批次插入1000条,并且每个User对象的主键id会被正确设置。因此,回答用户问题:在使用MyBatis进行批量插入操作时,可以通过上述方案获取所有插入记录的主键值。</think>在使用 MyBatis 进行批量插入操作时,从 SQL Server 获取所有插入记录的主键值,可通过以下方案实现: ### 解决方案(基于 SQL Server 特性) #### 核心方法:使用 `OUTPUT` 子句 ```xml <insert id="batchInsert" parameterType="java.util.List"> INSERT INTO target_table (column1, column2) OUTPUT INSERTED.id <!-- 返回插入的主键值 --> VALUES <foreach collection="list" item="item" separator=","> (#{item.value1}, #{item.value2}) </foreach> </insert> ``` #### 关键步骤: 1. **Mapper 接口定义**: ```java List<Long> batchInsert(List<Entity> list); // 返回主键列表 ``` 2. **SQL 优化**: - 使用 `OUTPUT INSERTED.id` 子句直接返回主键 - 通过 `<foreach>` 拼接批量 VALUES 语句 - 单次插入量建议 ≤ 1000 条(避免超长 SQL) 3. **JDBC 连接配置**: ```properties jdbc.url=jdbc:sqlserver://...;useBulkCopyForBatchInsert=false ``` *必须关闭批量复制模式(否则无法返回主键)[^3]* #### 执行效果: ```java List<Long> ids = mapper.batchInsert(entityList); // ids 包含所有新生成的主键值 ``` ### 备选方案(分批次插入) ```java // 手动分批次提交(每批1000条) try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { for (int i = 0; i < list.size(); i++) { mapper.insertSingle(list.get(i)); if (i % 1000 == 0 || i == list.size() - 1) { session.commit(); // 触发主键获取 } } } ``` *需在单条插入语句中配置 `useGeneratedKeys="true"`* ### 注意事项: 1. **主键要求**: - 仅适用于 `IDENTITY` 自增主键或 `OUTPUT` 支持的列 - 非自增主键需提前生成主键值 2. **性能平衡**: - 大批量数据(>10万条)建议用 SQL Server 原生 `BULK INSERT` - 中等批量(1k~10万)用 `OUTPUT` 方案最优 3. **事务控制**: - 确保操作在事务内执行 - 批量失败时需回滚 > **SQL Server 限制**:`OUTPUT` 子句在包含 `TRIGGER` 的表上可能返回逻辑删除的伪记录,需测试验证[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值