MyBatis--代理模式实现数据库增删改查

2022-05-15 09:06:58 浏览数 (1)

上篇MyBatis--初入MyBatis中,对MyBatis操作数据库进行了简单的使用,利用xml映射文件告诉MyBatis返回类型以及sql语句,最后通过SqlSession,执行映射文件中id对应的方法,但这只是单纯的映射了sql和实体类,对于Dao层,我们还是需要定义接口和实现类去调用SqlSession方法和返回

对此,MyBatis还支持动态代理,也就是说只需要在DAO层写一个接口,MyBatis在运行时会自动的帮助我们生成代理类,省去了我们反复写实现类的操作

一、使用代理模式
1. 新建Mapper接口

包结构:

DeptMapper

DeptMapper 接口:

代码语言:javascript复制
public interface DeptMapper {
    List<Dept> findAll();
}
3. 在resources目录下新建xml映射文件

内容为:

代码语言:javascript复制
<?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">
<!--对应Java中Mapper的全类名-->
<mapper namespace="com.aruba.mapper.DeptMapper">
    <!--public List<Dept> findAll(){    }-->
    <select id="findAll" resultType="dept">-- resultType对应实体类
        select * from dept
    </select>
</mapper>

注意:namespace需要和DeptMapper接口的全类名一致

3. Mybatis配置文件中只需要加入包扫描
4.测试运行

通过sqlSession的getMapper方法,传入DeptMapper接口的类对象获取DeptMapper的接口代理对象

代码语言:javascript复制
public class Test1 {
    private SqlSession sqlSession;

    @Before
    public void init() throws IOException {
        SqlSessionFactoryBuilder sb = new SqlSessionFactoryBuilder();
        // 将配置文件作为参数传入
        sqlSession = sb.build(Resources.getResourceAsStream("sqlMapConfig.xml")).openSession();
    }


    @Test
    public void test1() {
        // 直接获取DeptMapper接口代理对象
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        List<Dept> result = mapper.findAll();
        result.forEach(System.out::println);
    }

    @After
    public void release() {
        if (sqlSession != null)
            sqlSession.close();
    }
}

结果:

二、参数传递

接下来为SQL语句中,需要使用参数的几种方式

先按照员工表emp,准备员工实体类:

代码语言:javascript复制
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
    /**
     * 员工编号
     */
    private Integer empno;
    /**
     * 姓名
     */
    private String ename;
    /**
     * 职位
     */
    private String job;
    /**
     * 上级
     */
    private Integer mgr;
    /**
     * 入职日期
     */
    private Date hiredate;
    /**
     * 薪水
     */
    private Double sal;
    /**
     * 奖金
     */
    private Double comm;
    /**
     * 部门编号
     */
    private Integer deptno;
}
1. 单个基本数据类型

实现根据员工编号,获取员工信息

创建EmpMapper接口:

代码语言:javascript复制
public interface EmpMapper {
    Emp findEmp(int empno);
}

定义映射文件:

代码语言:javascript复制
<?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">
<!--对应Java中Mapper的全类名-->
<mapper namespace="com.aruba.mapper.EmpMapper">
    <!--Emp findEmp(int empno);-->
    <!--parameterType可以省略-->
    <select id="findEmp" resultType="emp" parameterType="int">
        select * from emp where empno = #{empno}
    </select>
</mapper>

SQL语句中使用${ },或者#{ }来传递参数,$使用的是Statement,#使用的是PreparedStatement

测试方法:

代码语言:javascript复制
//    单个基本数据类型
    @Test
    public void test2() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp result = mapper.findEmp(7698);
        System.out.println(result);
    }
2. 多个基本数据类型

根据部门编号和薪资下限去查询员工信息

2.1 方法参数方式

接口中定义方法:

代码语言:javascript复制
public interface EmpMapper {
    ...

    /**
     * 根据部门编号和薪资下限去查询员工信息
     */
    List<Emp> findEmpByDeptnoAndSal(@Param("deptno") int deptno, @Param("sal") double sal);
}

可以在参数上添加@Param注释指定映射文件中对应要使用的参数名

映射文件中添加标签:

代码语言:javascript复制
<mapper namespace="com.aruba.mapper.EmpMapper">
...

    <select id="findEmpByDeptnoAndSal" resultType="emp">
        select * from emp where deptno = #{deptno} and sal &gt; #{sal}
    </select>
</mapper>

注意:大于号和小于号,以及大于等于、小于等于,由于小于号在xml中有特殊含义,需要转义,推荐使用 > < ≥ ≤来表示

测试方式:

代码语言:javascript复制
//    方法参数方式
    @Test
    public void test3() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> result = mapper.findEmpByDeptnoAndSal(20,1000.0);
        result.forEach(System.out::println);
    }
2.2 Map集合方式

接口中定义方法:

代码语言:javascript复制
public interface EmpMapper {
    ...

    /**
     * 根据部门编号和薪资下限去查询员工信息
     */
    List<Emp> findEmpByDeptnoAndSal(Map<String,Object> params);
}

映射文件中不需要修改

测试方法:

代码语言:javascript复制
//    Map集合方式
    @Test
    public void test4() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Map<String,Object> params = new HashMap<>();
        params.put("deptno",20);
        params.put("sal",1000.0);
        List<Emp> result = mapper.findEmpByDeptnoAndSal(params);
        result.forEach(System.out::println);
    }
2.3 实体类方式

MyBatis还支持直接传入实体类,根据实体类中有值的属性名和属性值传参

定义接口:

代码语言:javascript复制
public interface EmpMapper {
    ...

    /**
     * 根据部门编号和薪资下限去查询员工信息
     */
    List<Emp> findEmpByDeptnoAndSal(Emp emp);
}

测试方法:

代码语言:javascript复制
    //    实体类方式
    @Test
    public void test5() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = new Emp();
        emp.setDeptno(20);
        emp.setSal(1000.0);
        List<Emp> result = mapper.findEmpByDeptnoAndSal(emp);
        result.forEach(System.out::println);
    }
3. 映射文件中接收参数的方式

上面我们已经在映射文件中使用了#{参数名}、${参数名}的方式来接收Java层传递的参数,

除了使用参数名外,还有两种表示方式

实现根据一个员工的部门,和另一个员工的薪资查询员工集合

首先定义接口:

代码语言:javascript复制
public interface EmpMapper {
    ...

    /**
     * 根据一个员工的部门,和另一个员工的薪资查询员工集合
     */
    List<Emp> findEmpByEmp(Emp emp1,Emp emp2);
}

映射文件:

  • arg方式:索引从0开始
代码语言:javascript复制
    <select id="findEmpByEmp" resultType="emp">
        select * from emp where deptno = #{arg0.deptno} and sal &gt; #{arg1.sal}
    </select>
  • param方式:索引从1开始
代码语言:javascript复制
    <select id="findEmpByEmp" resultType="emp">
        select * from emp where deptno = #{param1.deptno} and sal &gt; #{param2.sal}
    </select>

测试方法:

代码语言:javascript复制
    //    映射文件arg和param方式接收参数
    @Test
    public void test6() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = new Emp();
        emp.setDeptno(20);
        Emp emp2 = new Emp();
        emp2.setSal(1000.0);
        List<Emp> result = mapper.findEmpByEmp(emp,emp2);
        result.forEach(System.out::println);
    }
三、返回参数

查询操作时,除了返回实体类和列表外,MyBatis还能直接返回Map集合,需要指定查询结果中的一个字段作为Map的key

定义接口方法:

代码语言:javascript复制
public interface EmpMapper {
...

    /**
     * 根据部门编号和薪资下限去查询员工信息
     */
    @MapKey("empno")
    Map<Integer, Emp> findEmpMapByDeptnoAndSal(@Param("deptno") int deptno, @Param("sal") double sal);
}

使用MapKey指定Map的key

映射文件中新增查询:

代码语言:javascript复制
    <select id="findEmpMapByDeptnoAndSal" resultType="emp">
        select * from emp where deptno = #{deptno} and sal &gt; #{sal}
    </select>

测试方法:

代码语言:javascript复制
    //    查询返回Map集合
    @Test
    public void test7() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Map<Integer, Emp> result = mapper.findEmpMapByDeptnoAndSal(20, 2000);
        result.forEach((s, emp) -> {
            System.out.println("empno:"   s   "  emp:"   emp);
        });
    }
四、模糊查询

映射文件中使用模糊查询并不能直接用%%的方式,而是要用concat函数拼接

实现根据员工姓名模糊查询员工信息

定义接口方法:

代码语言:javascript复制
public interface EmpMapper {
...

    /**
     * 根据员工姓名模糊查询员工信息
     * @param ename
     * @return
     */
    List<Emp> findEmpLikeEname(String ename);
}

映射文件添加查询:

代码语言:javascript复制
    <select id="findEmpLikeEname" resultType="emp">
        select * from emp where ename like concat('%',#{ename},'%')
    </select>

测试方法:

代码语言:javascript复制
    //    模糊查询
    @Test
    public void test8() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> result = mapper.findEmpLikeEname("a");
        result.forEach(System.out::println);
    }
五、实现DML操作

MyBatis自带事务管理,DML操作都必须调用SqlSession的commit方法,才能提交事务

1. insert

实现新增一个员工

定义接口方法:

代码语言:javascript复制
    /**
     * 新增一个员工
     * @param emp
     * @return
     */
    int addEmp(Emp emp);

映射文件新增插入:

代码语言:javascript复制
    <insert id="addEmp">
        insert into emp values(DEFAULT,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno});
    </insert>

测试方法:

代码语言:javascript复制
    //    新增员工
    @Test
    public void test9() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp newEmp = new Emp(null,"小明","SALESMAN",7698,new Date(),2000.0,10.0,30);
        int rows = mapper.addEmp(newEmp);
        System.out.println(rows);

        // 提交事务
        sqlSession.commit();
    }
2. 自增主键回填

MyBatis支持自增主键回填,如果需要获取插入后的员工编号,需要在映射文件中配置,有两种方式:

  • 在insert标签上指定:
代码语言:javascript复制
    <insert id="addEmp" useGeneratedKeys="true" keyProperty="empno">
        insert into emp values(DEFAULT,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno});
    </insert>
  • 使用selectKey标签:
代码语言:javascript复制
    <insert id="addEmp2">
        <selectKey order="AFTER" keyProperty="empno"  resultType="int">
            select @@identity
        </selectKey>
        insert into emp values(DEFAULT,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno});
    </insert>

测试方法:

代码语言:javascript复制
    //    新增员工
    @Test
    public void test9() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp newEmp = new Emp(null,"小明","SALESMAN",7698,new Date(),2000.0,10.0,30);
        int rows = mapper.addEmp(newEmp);
        System.out.println(rows);
        // 打印返回的empno
        System.out.println(newEmp.getEmpno());

        // 提交事务
        sqlSession.commit();
    }
3. update

实现根据员工编号更新员工姓名

定义接口方法:

代码语言:javascript复制
    /**
     * 根据员工编号更新员工姓名
     * @param emp
     * @return
     */
    int updateEnameByEmpno(Emp emp);

映射文件新增更新:

代码语言:javascript复制
    <update id="updateEnameByEmpno">
        update emp set ename= #{ename} where empno = #{empno}
    </update>

测试方法:

代码语言:javascript复制
    //    更新员工
    @Test
    public void test10() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = new Emp();
        emp.setEmpno(7938);
        emp.setEname("小黑");
        int rows = mapper.updateEnameByEmpno(emp);
        System.out.println(rows);

        // 提交事务
        sqlSession.commit();
    }
4. delete

实现根据员工编号删除员工

定义接口方法:

代码语言:javascript复制
    /**
     * 根据员工编号删除员工
     * @param empno
     * @return
     */
    int deleteEmpByEmpno(int empno);

映射文件中新增删除:

代码语言:javascript复制
    <delete id="deleteEmpByEmpno">
        delete from emp where empno = #{empno}
    </delete>

测试方法:

代码语言:javascript复制
    //    删除员工
    @Test
    public void test11() {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        int rows = mapper.deleteEmpByEmpno(7938);
        System.out.println(rows);

        // 提交事务
        sqlSession.commit();
    }

MyBatis的代理模式开发就到此结束了

项目地址

https://gitee.com/aruba/mybatis_study.git

0 人点赞