MyBatis代理模式下的参数传递问题
下面继续使用Mapper代理方式完成更多更复杂的数据库操作,涉及多个参数传递、模糊查询,自增主键回填等内容。
一、多参数传递
Mapper代理方式下参数传递的类型有:
- 单个基本数据类型
- 多个基本数据类型
- 单个引用数据类型
- map集合数据类型
- 多个引用数据类型
1.1.准备接口文件
下面还是以Emp表为例,编写接口如下:
public interface EmpMapper { /*查询所有员工接口 * 返回所有员工封装在一起的Emp List集合*/ List<Emp> findAllEmp(); /** * 根据员工编号查询员工信息 * @param empno 员工编号 * @return */ //@Param的作用就是给参数命名,后面在xml映射文件中以@Param后面写的参数名为主 Emp findEmpByEmpno(@Param("empNo") int empno); /** * 根据部分编号和薪水查询员工信息:基本数据类型作为参数 * @param deptno 编号 * @param sal 薪水 * @return */ List<Emp> findEmpByDeptNoAndSal1(@Param("deptNo") int deptno, @Param("sal") double sal); /** * 根据部分编号和薪水查询员工信息:单个引用数据类型作为参数查询 * @param emp 员工对象 * @return */ List<Emp> findEmpByDeptNoAndSal2(Emp emp); /** * 根据部分编号和薪水查询员工信息:多个引用数据类型作为参数查询 * @param emp1 对象1 * @param emp2 对象2 * @return */ List<Emp> findEmpByDeptNoAndSal3(@Param("emp1") Emp emp1, @Param("emp2") Emp emp2); /** * 参数以Map的形式传递 * @param map map集合 * @return */ List<Emp> findEmpByDeptNoAndSal4(Map<String,Object> map); }
1.2.准备映射文件
在mapper包下创建EmpMapper.xml文件,编写对应的映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus.mapper.EmpMapper"> <!-- 1 接口的名字和Mapper映射为文件名字必须保持一致(不包含拓展名) 2 Mapper映射文件的namespace必须是接口的全路径名 3 sql语句的id必须是对应方法的名 4 EmpMapper映射文件应该和接口编译之后放在同一个目录下 List<Emp> findAllEmp(); --> <select id="findAllEmp" resultType="com.augus.pojo.Emp"> select * from emp </select> <!--根据编号查询员工信息--> <select id="findEmpByEmpno" resultType="com.augus.pojo.Emp"> select * from emp where empno = #{empNo} </select> <!--根据部分编号和薪水查询员工信息:基本数据类型作为参数--> <select id="findEmpByDeptNoAndSal1" resultType="com.augus.pojo.Emp"> select * from emp where deptno = #{deptNo} and sal = #{sal} </select> <!--根据部分编号和薪水查询员工信息:单个引用数据类型作为参数查询 单个应用类型作为参数,#{}中写属性的名字--> <select id="findEmpByDeptNoAndSal2" resultType="com.augus.pojo.Emp"> select * from emp where deptno = #{deptno} and sal = #{sal} </select> <!--根据部分编号和薪水查询员工信息:多个引用数据类型作为参数查询 单个应用类型作为参数,#{}中写属性的名字 如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名--> <select id="findEmpByDeptNoAndSal3" resultType="com.augus.pojo.Emp"> select * from emp where deptno = #{emp1.deptno} and sal = #{emp2.sal} </select> <!--参数以Map的形式传递--> <select id="findEmpByDeptNoAndSal4" resultType="com.augus.pojo.Emp"> select * from emp where deptno = #{deptno} and sal = #{sal} </select> </mapper>
1.3.测试文件
创建测试类Test2,代码如下:
public class Test02 { private SqlSession sqlSession; @BeforeEach public void setUp(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } //查询所有员工 @Test public void testFindAllDept(){ /*获取字节码文件*/ EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); //执行SQL List<Emp> allEmp = mapper.findAllEmp(); for (Emp emp : allEmp) { System.out.println(emp); } } @Test public void testFindEmp(){ EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); // 1单个基本数据类型作为方法参数 Emp empByEmpno = mapper.findEmpByEmpno(7900); System.out.println(empByEmpno); //2.多个基本数据类型作为方法参数 List<Emp> empByDeptNoAndSal1 = mapper.findEmpByDeptNoAndSal1(30, 1250.00); for (Emp emp : empByDeptNoAndSal1) { System.out.println(emp); } //3.单个引用类型作为方法参数 Emp emp1 = new Emp(); emp1.setDeptno(30);//设置部门编号 emp1.setSal(1250.00);//设置薪水 List<Emp> empByDeptNoAndSal2 = mapper.findEmpByDeptNoAndSal2(emp1); for (Emp emp : empByDeptNoAndSal2) { System.out.println(emp); } //4.多个引用类型作为方法参数 Emp emp2 = new Emp(); emp2.setDeptno(20); Emp emp3 = new Emp(); emp3.setSal(1100.00); List<Emp> empByDeptNoAndSal3 = mapper.findEmpByDeptNoAndSal3(emp2, emp3); for (Emp emp : empByDeptNoAndSal3) { System.out.println(emp); } //5.参数以map的形式传递 HashMap<String, Object> map = new HashMap<>(); map.put("deptno",30); map.put("sal", 1250.00); List<Emp> empByDeptNoAndSal4 = mapper.findEmpByDeptNoAndSal4(map); for (Emp emp : empByDeptNoAndSal4) { System.out.println(emp); } } @AfterEach public void tearDown(){ //关闭 sqlSession.close(); } }
二、模糊查询
在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。参考:https://www.w3school.com.cn/html/html_entities.asp
2.1.接口准备
还是在之前的EmpMapper上添加如下
package com.augus01.mapper;
import com.augus01.pojo.Emp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface EmpMapper {
//根据员工姓名模糊查询
List<Emp> findEmpByName(String name);
}
2.2.准备映射文件
在映射文件中添加对应sql信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespace需要指定接口文件的路径
-->
<mapper namespace="com.augus01.mapper.EmpMapper">
<!--
List<Emp> findEmpByName(String name);
-->
<select id="findEmpByName" resultType="emp" parameterType="string">
select * from emp where ename like concat('%',#{name},'%')
</select>
</mapper>
2.3.测试文件
public class Test02 { private SqlSession sqlSession; @BeforeEach public void setUp(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } @Test public void testFindEmpByName(){ EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); //执行 List<Emp> emps = mapper.findEmpByName("a"); for (Emp emp : emps) { System.out.println(emp); } } @AfterEach public void tearDown(){ //关闭 sqlSession.close(); } }
三、自增主键回填
MySQL支持主键自增。有时候完成添加后需要立刻获取刚刚自增的主键,由下一个操作来使用。比如结算购物车后,订单的主键确定后,需要作为后续订单明细项的外键存在。如何拿到主键呢,MyBatis提供了支持,可以非常简单的获取。
3.1.方式1
useGeneratedKeys:表示要使用自增的主键
keyProperty:表示把自增的主键赋给JavaBean的哪个成员变量。
以添加Dept对象为例,添加前Dept对象的deptno是空的,添加完毕后可以通过getDeptno() 获取自增的主键。
3.2.方式2:
order:取值AFTER|BEFORE,表示在新增之后|之前执行<selectKey>中的SQL命令
keyProperty:执行select @@identity后结果填充到哪个属性中
resultType:结果类型。
3.3.案例演示
3.3.1.接口准备 DeptMapper
在mapper下的DeptMapper添加两个新增部门信息的接口如下:
public interface DeptMapper { //查询所有的部门信息,返回的是多条数据,所以返回成一个dept的list集合 List<Dept> findAllDept(); /** * 新增部分信息 */ int addDept1(Dept dept); int addDept2(Dept dept); }
3.3.2.准备映射文件 DeptMapper.xml
在下面编写主键回显的映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace这里需要设置和接口类的路径 可以理解成要实现的SQL定义在那个接口文件中 --> <mapper namespace="com.augus.mapper.DeptMapper"> <!--需要注意的是每一个配置文件只是mapper中的内容有区别--> <!-- 要实现查询出来所有的部门信息的接口 List<Dept> findAllDept(); select标签是查询,id属性的值必须和DeptMapper中定义的方法名 resultType="com.augus01.pojo.Dept" 返回值类型这里指定是那个这pojo包下的Dept类 --> <select id="findAllDept" resultType="dept"> select * from dept </select> <!--新增部门信息-测试主键回显 useGeneratedKeys="true" 返回数据库帮我们生成的主键 keyProperty="deptno" 生成的主键值用dept对象那个属性存储 --> <insert id="addDept1" parameterType="dept" useGeneratedKeys="true" keyProperty="deptno"> insert into dept values (null ,#{dname},#{loc}) </insert> <!--新增部门信息-测试主键回显--> <insert id="addDept2" parameterType="emp"> <selectKey order="AFTER" keyProperty="deptno" resultType="int"> select @@identity </selectKey> insert into dept values(null,#{dname},#{loc}) </insert> </mapper>
3.3.3.测试文件
分别两个两种方式的主键自增回显
public class Test01 { private static SqlSession sqlSession; @BeforeEach public void setUp(){ //创建一个对象:参照了XML 文档或上面讨论过的更特定的 mybatis-config.xml 文件的 Reader 实例 SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); InputStream resourceAsStream; try { //Resources.getResourceAsStream对于简单的只读二进制或文本数据,加载为 Stream。 resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); /* SqlSessionFactory SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择这些方法时你需要考虑以下几点: 事务处理:需要在 session 使用事务或者使用自动提交功能(auto-commit)吗?(通常意味着很多数据库和/或 JDBC 驱动没有事务) 连接:需要依赖 MyBatis 获得来自数据源的配置吗?还是使用自己提供的配置? 执行语句:需要 MyBatis 复用预处理语句和/或批量更新语句(包括插入和删除)吗? */ SqlSessionFactory build = factoryBuilder.build(resourceAsStream); /* 默认的 openSession()方法没有参数,它会创建有如下特性的 SqlSession: 会开启一个事务(也就是不自动提交)。 将从由当前环境配置的 DataSource 实例中获取 Connection 对象。 事务隔离级别将会使用驱动或数据源的默认设置。 预处理语句不会被复用,也不会批量处理更新。 */ sqlSession = build.openSession(); } catch (IOException ioException) { ioException.printStackTrace(); } } @Test public void testGetAllDept(){ // 这里写需要执行的SQL,写的时候DeptMapper.xml中select标签id属性的值 List<Object> findAllDept = sqlSession.selectList("findAllDept"); for (Object o : findAllDept) { System.out.println(o); } } /** * 主键自增回填1 */ @Test public void testAddDept1(){ DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); //创建dept对象 Dept dept = new Dept(null, "运维部", "广州"); int i = mapper.addDept1(dept); //提交事务 sqlSession.commit(); System.out.println("自增后获取的主键为:"+dept.getDeptno()); System.out.println(i); } /** * 主键自增回填2 */ @Test public void testAddDept2(){ DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); //创建dept对象 Dept dept = new Dept(null, "测试部", "广州"); int i = mapper.addDept2(dept); //提交事务 sqlSession.commit(); System.out.println("自增后获取的主键为:"+dept.getDeptno()); System.out.println(i); } @Test public void tearDown(){ sqlSession.close(); } }
四、实现DML操作
4.1.准备EmpMapper接口
在EmpMapper中添加增、删、该对应的接口
public interface EmpMapper { /** * 新增员工信息 * @param emp * @return */ int addEmp(Emp emp); /** * 根据员工编号修改员工姓名的方法 * @param empno 员工编号 * @param ename 员工姓名 * @return */ int updateEmpNameByEmpNo(@Param("empno") int empno,@Param("ename") String ename); /** * 根据编号删除员工信息 * @param empno 根据编号删除 * @return */ int deleteEmpByEmpNo(@Param("empno") int empno); }
4.2.准备EmpMapper.xml映射文件
添加对应的映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus.mapper.EmpMapper"> <!--新增员工信息 主键哪里把null写成default也可以--> <insert id="addEmp" parameterType="emp" useGeneratedKeys="true" keyProperty="empno"> insert into emp values (null ,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno}) </insert> <!-- 根据员工编号修改员工姓名的方法--> <update id="updateEmpNameByEmpNo"> update emp set ename = #{ename} where empno = #{empno} </update> <!--根据编号删除员工信息--> <delete id="deleteEmpByEmpNo" parameterType="int"> delete from emp where empno = #{empno} </delete> </mapper>
4.3.测试
编写测试文件,测试新增、修改和删除操作
public class Test02 { private SqlSession sqlSession; @BeforeEach public void setUp(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } /** * 测试新增员工信息 */ @Test public void testAddEmp(){ //新增员工信息 EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); //构建emp对象 Emp emp = new Emp(null, "tom", "sfsdfd", 7782, new Date(), 2000.0, 300.0, 10); //执行 int i = mapper.addEmp(emp); //提交事务 sqlSession.commit(); System.out.println("新增后主键为:"+emp.getEmpno()); System.out.println(i); } /** * 测试修改员工信息 */ @Test public void testUpdateEmpNameByEmpNo(){ //新增员工信息 EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); //执行 int i = mapper.updateEmpNameByEmpNo(7876,"jsww"); //提交事务 sqlSession.commit(); System.out.println(i); } /** * 测试删除员工信息 */ @Test public void testDeleteEmpByEmpNo(){ //新增员工信息 EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); //执行 int i = mapper.deleteEmpByEmpNo(7903); //提交事务 sqlSession.commit(); System.out.println(i); } @AfterEach public void tearDown(){ //关闭 sqlSession.close(); } }

浙公网安备 33010602011771号