上篇MyBatis--初入MyBatis中,对MyBatis操作数据库进行了简单的使用,利用xml映射文件告诉MyBatis返回类型以及sql语句,最后通过SqlSession,执行映射文件中id对应的方法,但这只是单纯的映射了sql和实体类,对于Dao层,我们还是需要定义接口和实现类去调用SqlSession方法和返回
对此,MyBatis还支持动态代理,也就是说只需要在DAO层写一个接口,MyBatis在运行时会自动的帮助我们生成代理类,省去了我们反复写实现类的操作
一、使用代理模式
1. 新建Mapper接口
包结构:
![](https://i-blog.csdnimg.cn/blog_migrate/e5c1ede5212ea33c06d73ab79338a130.png)
DeptMapper 接口:
public interface DeptMapper {
List<Dept> findAll();
}
3. 在resources目录下新建xml映射文件
![](https://i-blog.csdnimg.cn/blog_migrate/5a076e180186fc48de07fbbae05c69a9.png)
内容为:
<?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配置文件中只需要加入包扫描
![](https://i-blog.csdnimg.cn/blog_migrate/2e5d6dc33240f10e67019104006b2789.png)
4.测试运行
通过sqlSession的getMapper方法,传入DeptMapper接口的类对象获取DeptMapper的接口代理对象
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();
}
}
结果:
![](https://i-blog.csdnimg.cn/blog_migrate/a22a68f09975bbd88bd9d4adddd0d19f.png)
二、参数传递
接下来为SQL语句中,需要使用参数的几种方式
先按照员工表emp,准备员工实体类:
@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接口:
public interface EmpMapper {
Emp findEmp(int empno);
}
定义映射文件:
<?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
测试方法:
// 单个基本数据类型
@Test
public void test2() {
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp result = mapper.findEmp(7698);
System.out.println(result);
}
2. 多个基本数据类型
根据部门编号和薪资下限去查询员工信息
2.1 方法参数方式
接口中定义方法:
public interface EmpMapper {
...
/**
* 根据部门编号和薪资下限去查询员工信息
*/
List<Emp> findEmpByDeptnoAndSal(@Param("deptno") int deptno, @Param("sal") double sal);
}
可以在参数上添加@Param注释指定映射文件中对应要使用的参数名
映射文件中添加标签:
<mapper namespace="com.aruba.mapper.EmpMapper">
...
<select id="findEmpByDeptnoAndSal" resultType="emp">
select * from emp where deptno = #{deptno} and sal > #{sal}
</select>
</mapper>
注意:大于号和小于号,以及大于等于、小于等于,由于小于号在xml中有特殊含义,需要转义,推荐使用 > < ≥ ≤来表示
测试方式:
// 方法参数方式
@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集合方式
接口中定义方法:
public interface EmpMapper {
...
/**
* 根据部门编号和薪资下限去查询员工信息
*/
List<Emp> findEmpByDeptnoAndSal(Map<String,Object> params);
}
映射文件中不需要修改
测试方法:
// 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还支持直接传入实体类,根据实体类中有值的属性名和属性值传参
定义接口:
public interface EmpMapper {
...
/**
* 根据部门编号和薪资下限去查询员工信息
*/
List<Emp> findEmpByDeptnoAndSal(Emp emp);
}
测试方法:
// 实体类方式
@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层传递的参数,
除了使用参数名外,还有两种表示方式
实现根据一个员工的部门,和另一个员工的薪资查询员工集合
首先定义接口:
public interface EmpMapper {
...
/**
* 根据一个员工的部门,和另一个员工的薪资查询员工集合
*/
List<Emp> findEmpByEmp(Emp emp1,Emp emp2);
}
映射文件:
- arg方式:索引从0开始
<select id="findEmpByEmp" resultType="emp">
select * from emp where deptno = #{arg0.deptno} and sal > #{arg1.sal}
</select>
- param方式:索引从1开始
<select id="findEmpByEmp" resultType="emp">
select * from emp where deptno = #{param1.deptno} and sal > #{param2.sal}
</select>
测试方法:
// 映射文件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
定义接口方法:
public interface EmpMapper {
...
/**
* 根据部门编号和薪资下限去查询员工信息
*/
@MapKey("empno")
Map<Integer, Emp> findEmpMapByDeptnoAndSal(@Param("deptno") int deptno, @Param("sal") double sal);
}
使用MapKey指定Map的key
映射文件中新增查询:
<select id="findEmpMapByDeptnoAndSal" resultType="emp">
select * from emp where deptno = #{deptno} and sal > #{sal}
</select>
测试方法:
// 查询返回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函数拼接
实现根据员工姓名模糊查询员工信息
定义接口方法:
public interface EmpMapper {
...
/**
* 根据员工姓名模糊查询员工信息
* @param ename
* @return
*/
List<Emp> findEmpLikeEname(String ename);
}
映射文件添加查询:
<select id="findEmpLikeEname" resultType="emp">
select * from emp where ename like concat('%',#{ename},'%')
</select>
测试方法:
// 模糊查询
@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
实现新增一个员工
定义接口方法:
/**
* 新增一个员工
* @param emp
* @return
*/
int addEmp(Emp emp);
映射文件新增插入:
<insert id="addEmp">
insert into emp values(DEFAULT,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno});
</insert>
测试方法:
// 新增员工
@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标签上指定:
<insert id="addEmp" useGeneratedKeys="true" keyProperty="empno">
insert into emp values(DEFAULT,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno});
</insert>
- 使用selectKey标签:
<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>
测试方法:
// 新增员工
@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
实现根据员工编号更新员工姓名
定义接口方法:
/**
* 根据员工编号更新员工姓名
* @param emp
* @return
*/
int updateEnameByEmpno(Emp emp);
映射文件新增更新:
<update id="updateEnameByEmpno">
update emp set ename= #{ename} where empno = #{empno}
</update>
测试方法:
// 更新员工
@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
实现根据员工编号删除员工
定义接口方法:
/**
* 根据员工编号删除员工
* @param empno
* @return
*/
int deleteEmpByEmpno(int empno);
映射文件中新增删除:
<delete id="deleteEmpByEmpno">
delete from emp where empno = #{empno}
</delete>
测试方法:
// 删除员工
@Test
public void test11() {
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
int rows = mapper.deleteEmpByEmpno(7938);
System.out.println(rows);
// 提交事务
sqlSession.commit();
}