五、MyBatis获取参数值的两种方式以及传参情况

本文详细介绍了MyBatis框架中参数传递的多种方式,包括使用${}

MyBatis获取参数值的两种方式:${}和#{}

(1) ${}的本质就是字符串拼接,#{}的本质就是占位符赋值。
(2) ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号。

<select id="selectPersonByName" resultType="Person">
        select * from t_person where name = '${name}'
</select>

(3) #{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。

    <select id="selectPersonByName" resultType="Person">
        <!--select * from t_person where name = '${name}'-->
        select * from t_person where name = #{name}
    </select>

传参情况

演示环境

       以下演示均已#{}方式获取参数。
数据库表
在这里插入图片描述
pojo
在这里插入图片描述
映射接口
在这里插入图片描述

1.单个字面量类型的参数

       以查询为例,通过name查出一条数据。
接口方法

public interface PersonMapper {
    //按name查询
    Person selectPersonByName(String name);
}

映射文件

<select id="selectPersonByName" resultType="Person">
        select * from t_person where name = #{name}
</select>

测试方法

		PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        mapper.selectPersonByName("zhangsan");

说明
       当参数是一个的时候#{}中可以填写任意值,比如下方的写法也是可以获取的,但最好写的见名知意。

<select id="selectPersonByName" resultType="Person">
        select * from t_person where name = #{aaa}
</select>

2.多个字面量类型的参数

       以按name和age两个参数进行查询,当然多个参数也是类似的写法,只是我演示所用的表只有两个数据项。
接口方法

public interface PersonMapper {
    //按name和age进行查询
    Person selectPersonByNameAge(String name,Integer age);
}

映射文件
(1)方式一

<select id="selectPersonByNameAge" resultType="Person">
        select * from t_person where name = #{arg0} and age=#{arg1}
</select>

(2)方式二

<select id="selectPersonByNameAge" resultType="Person">
        select * from t_person where name = #{param1} and age=#{param2}
</select>

(3)方式三

<select id="selectPersonByNameAge" resultType="Person">
        select * from t_person where name = #{arg0} and age=#{param2}
</select>

测试方法

		PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        mapper.selectPersonByNameAge("zhangsan",20);

说明
       若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1…为键,以参数为值;以param1,param2…为键,以参数为值;
       因此只需要通过#{}访问map集合的键就可以获取相对应的值。
       注意${}需要手动加单引号,param是从1开始的。

3.map集合类型的参数

       这次以插入一条数据为例,插入name=“李四” age=23。
接口方法

public interface PersonMapper {
    //Map
    int insertPersonMap(Map<String,Object> map);
}

映射文件

    <insert id="insertPersonMap">
        insert into t_person values(#{key1},#{key2})
    </insert>

测试方法

		PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
		HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("key1","李四");
        map.put("key2",23);
        mapper.insertPersonMap(map);

说明
       若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过和#{}访问map集合的就可以获取相对应的值,注意${}需要手动加单引号

4.实体类类型的参数

       以插入一个Person对象为例,new Person(“wangwu”,66)
接口方法

public interface PersonMapper {
    //传参对象
    int insertPerson(Person person);

}

映射文件

 <insert id="insertPerson">
        insert into t_person values(#{name},#{age})
</insert>

测试方法

		PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        mapper.insertPerson(new Person("wangwu",66));

说明
       若mapper接口中的方法参数为实体类对象时,此时可以使用#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号。

5.使用@Param标识参数

删除年龄为66岁的。
接口方法

public interface PersonMapper {
    //@param
    int deletePersonByAge(@Param("Abb") Integer age);

}

映射文件
(1)方式一

    <delete id="deletePersonByAge">
    <!--最好见名知意,这里为了演示-->
        delete from t_person where age=#{Abb}
    </delete>

(2)方式二

    <delete id="deletePersonByAge">
        delete from t_person where age=#{param1}
    </delete>

测试方法

		PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
        mapper.deletePersonByAge(66);

说明

       可以通过@Param注解标识mapper接口中的方法参数。
(1)此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;
(2)以param1,param2…为键,以参数为值;只需要通过和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。

<think> 我们正在讨论MyBatis中两个SQL之间传参的方法。用户之前的问题是关于在MyBatis中调用Oracle函数时,由于函数内使用了WITH临时表且依赖主SQL的字段值,导致抽出函数后无法访问字段的问题。现在用户的新问题是:在MyBatis中,是否有一种方法可以在两个SQL语句之间传递参数? 根据引用[1][2][3][4]的内容,我们可以总结MyBatis参数传递的几种方式,但并没有直接提到两个独立SQL语句之间传递参数。在MyBatis中,每个SQL语句都是独立的,它们之间的参数传递通常需要通过应用程序代码(如Java代码)来中转,或者利用数据库的临时表/会话变量等特性。不过,我们可以结合MyBatis的特性,给出一些可能的解决方案。 思路: 1. **应用程序中转**:在应用程序中执行第一个SQL,获取结果,然后将结果作为参数传递给第二个SQL。这是最常见的方式。 2. **使用数据库特性**:利用数据库的临时表或会话变量来存储第一个SQL的结果,然后在第二个SQL中访问这些临时表或变量。但这种方式依赖于数据库,且需要注意会话的生命周期。 3. **使用MyBatis的嵌套查询**:在同一个Mapper方法中,通过嵌套查询(例如在结果映射中使用<collection>或<association>)可以实现多个SQL的关联,但这通常用于主从表关联,而且嵌套查询中的参数可以来自主查询的结果。 4. **使用存储过程**:将两个SQL封装在一个存储过程中,在存储过程中实现参数传递。 我们具体分析: ### 1. 应用程序中转 这是最直接的方式。在Service层中,先执行第一个查询,获取结果,再将结果作为参数传递给第二个查询。 示例代码: ```java // Service层 public void executeTwoQueries() { // 执行第一个SQL List<ResultType1> result1 = mapper1.selectFirst(); // 将result1中的某个值作为参数传递给第二个SQL if (result1 != null && !result1.isEmpty()) { ResultType1 firstResult = result1.get(0); List<ResultType2> result2 = mapper2.selectSecond(firstResult.getSomeField()); // ... 后续处理 } } ``` ### 2. 使用数据库的临时表或会话变量 以Oracle为例,可以使用会话级临时表或SESSION变量来存储中间数据。 步骤: a) 在第一个SQL中,将结果插入到临时表或设置会话变量。 b) 在第二个SQL中,从临时表或会话变量中读取数据。 注意:这种方式要求两个SQL在同一个数据库会话(同一个事务)中执行。 示例(使用Oracle会话级临时表): - 创建临时表: ```sql CREATE GLOBAL TEMPORARY TABLE temp_table (id NUMBER) ON COMMIT PRESERVE ROWS; ``` - 第一个SQL插入数据: ```xml <insert id="insertTemp"> INSERT INTO temp_table (id) VALUES (#{value}) </insert> ``` - 第二个SQL查询数据: ```xml <select id="selectFromTemp" resultType="int"> SELECT id FROM temp_table </select> ``` 在同一个事务中,先执行insertTemp,再执行selectFromTemp。 使用会话变量(Oracle): - 第一个SQL设置变量: ```sql BEGIN :my_var := #{value}; END; ``` 注意:在MyBatis中执行PL/SQL块需要使用`<select>`标签,并设置`statementType="CALLABLE"`。 - 第二个SQL使用变量: ```sql SELECT * FROM some_table WHERE column = :my_var ``` 但是,这种方式MyBatis中较为复杂,且不同数据库支持不同,不推荐。 ### 3. 嵌套查询(关联查询) 如果两个查询有逻辑上的关联,比如一对多关系,可以使用MyBatis的嵌套查询。 例如,第一个查询获取用户列表,第二个查询获取每个用户的订单。在结果映射中,我们可以使用`<collection>`来嵌套第二个查询。 但是,这种方式不能用于任意的两个SQL,而且嵌套查询可能会引发N+1问题。 ### 4. 使用存储过程 将两个SQL放在一个存储过程中,在存储过程中实现参数传递。然后在MyBatis中调用这个存储过程。 示例: - 创建存储过程(Oracle): ```sql CREATE OR REPLACE PROCEDURE my_proc (param1 IN VARCHAR2, result_cursor OUT SYS_REFCURSOR) AS v_temp NUMBER; BEGIN -- 第一个SQL SELECT column INTO v_temp FROM table1 WHERE ...; -- 第二个SQL使用第一个SQL的结果 OPEN result_cursor FOR SELECT * FROM table2 WHERE column = v_temp; END; ``` - MyBatis调用存储过程: ```xml <select id="callProcedure" statementType="CALLABLE"> {call my_proc(#{param1,mode=IN}, #{result,jdbcType=CURSOR,mode=OUT,resultMap=resultMapName})} </select> ``` 总结: - 如果两个SQL是连续的,并且都在同一个事务中,推荐使用应用程序中转的方式,简单可控。 - 如果两个SQL有很强的业务关联(如主从关系),可以考虑嵌套查询。 - 如果两个SQL需要在一个数据库事务中共享中间结果,且不想通过应用程序中转,可以考虑临时表(注意临时表的作用域和生命周期)或存储过程。 根据引用[3]和[4],我们还可以注意MyBatis参数传递的规范: - 使用`@Param`注解明确参数名(引用[3])。 - 避免使用`${}`进行字符串拼接(引用[2]),以防止SQL注入。 - 在动态SQL中,确保参数类型正确,以避免解析错误(引用[4])。 因此,针对用户的问题,最通用的方法是应用程序中转。如果用户希望完全在数据库层面实现,那么临时表或存储过程是可行的,但需要考虑数据库的依赖性。 相关问题: 1. MyBatis中如何调用存储过程? 2. 如何在Oracle中创建和使用临时表? 3. MyBatis如何实现嵌套查询(关联查询)? 4. 在同一个事务中执行多个Mapper方法,如何保证事务一致性? </think> ### MyBatis中实现两个SQL语句间传参的解决方案 在MyBatis中,两个SQL语句之间**无法直接传递参数**,因为每个SQL语句都是独立的执行单元。但可通过以下四种方法实现间接参数传递,结合您的Oracle函数场景,方案3和4最值得关注: --- #### 解法1:应用层中转传参(推荐) **核心逻辑**:在Java业务层执行第一个SQL→获取结果→作为参数传入第二个SQL ```java // Service层实现参数中转 public void executeChain() { // 1. 执行第一个SQL获取结果 Map<String, Object> result1 = sqlSession.selectOne("firstQuery", params); Object paramValue = result1.get("TEMP_COLUMN"); // 2. 将结果作为参数传递给第二个SQL Map<String, Object> result2 = sqlSession.selectOne("secondQuery", Map.of("funcParam", paramValue)); } ``` ```xml <!-- 第二个SQL调用Oracle函数 --> <select id="secondQuery" resultType="map"> SELECT your_function(#{funcParam}) AS result FROM dual </select> ``` **优势**: - 完全避免数据库作用域问题 - 符合MyBatis标准传参模式[^2] - 调试简单,参数可见性强[^4] --- #### 解法2:嵌套子查询(需数据库支持) **核心逻辑**:将第一个查询作为子查询嵌入第二个SQL ```sql SELECT your_function(sub.col_val) FROM ( SELECT temp_column AS col_val -- 第一个查询 FROM main_table WHERE id = #{id} ) sub ``` **限制**: - Oracle函数的WITH临时表仍无法访问外层字段 - 仅适用于简单单值传递 - 可能引发性能问题(嵌套层级过多) --- #### 解法3:全局临时表(GTT)共享 **核心逻辑**:用Oracle全局临时表存储中间结果 ```sql <!-- 第一个SQL创建临时表 --> <update id="createTemp"> CREATE GLOBAL TEMPORARY TABLE my_temp_table ( key_col VARCHAR2(50), value_col NUMBER ) ON COMMIT PRESERVE ROWS </update> ``` ```sql <!-- 第二个SQL读取临时表 --> <select id="useTemp" resultType="map"> SELECT your_function(value_col) AS result FROM my_temp_table WHERE key_col = 'SPECIFIC_KEY' </select> ``` **操作步骤**: 1. 同一事务中先执行`createTemp`和插入数据 2. 再执行函数调用SQL 3. 事务提交后自动清空临时表 **适用场景**: - 复杂中间结果传递 - 多个SQL共享数据[^1] --- #### 解法4:存储过程封装 **核心逻辑**:用Oracle存储过程封装多个SQL逻辑 ```sql CREATE OR REPLACE PROCEDURE execute_chain ( p_param IN VARCHAR2, p_result OUT SYS_REFCURSOR ) AS v_temp_val NUMBER; BEGIN -- 第一个SQL SELECT temp_column INTO v_temp_val FROM main_table WHERE id = p_param; -- 第二个SQL调用函数 OPEN p_result FOR SELECT your_function(v_temp_val) AS result FROM dual; END; ``` ```xml <!-- MyBatis调用存储过程 --> <select id="callProc" statementType="CALLABLE"> {call execute_chain( #{param,mode=IN,jdbcType=VARCHAR}, #{result,mode=OUT,jdbcType=CURSOR,resultMap=funcResult} )} </select> ``` **优势**: - 彻底解决作用域隔离问题 - 函数可访问过程内所有变量 - 减少网络往返次数[^3] --- ### 方案选型建议 | 方法 | 适用场景 | Oracle函数支持 | 复杂度 | |---------------|---------------------------------|----------------|--------| | 应用层中转 | 简单参数传递,逻辑清晰 | ★★★★☆ | 低 | | 全局临时表(GTT) | 大量中间数据共享,多SQL协作 | ★★★★☆ (需DML权限) | 中 | | 存储过程 | 复杂业务逻辑,函数依赖主SQL字段 | ★★★★★ | 高 | | 嵌套子查询 | 简单值传递,无跨会话需求 | ★★☆☆☆ | 低 | > **特别推荐**:针对您的Oracle函数依赖主SQL字段的场景,**存储过程封装(方案4)** 是最彻底的解决方案,它允许: > 1. 函数直接访问过程内定义的变量 > 2. 多个临时表共享同一作用域 > 3. 避免WITH临时表的作用域隔离问题 --- ### 典型错误规避 1. **避免`${}`拼接传参** ```xml <!-- 危险!SQL注入风险 --> SELECT * FROM table WHERE col = ${rawValue} <!-- 正确!使用#预处理 --> SELECT * FROM table WHERE col = #{safeValue} ``` [^2] 2. **参数类型强制声明** 当出现参数类型解析错误时(常见于动态SQL): ```xml <if test="param != null and param gt 0"> <!-- 可能误判类型 --> AND col = #{param, jdbcType=NUMERIC} <!-- 显式声明类型 --> </if> ``` [^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

齊 天 大 聖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值