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();
    }

}

 

posted @ 2022-08-03 13:53  酒剑仙*  阅读(55)  评论(0)    收藏  举报