0%

【Mybatis】映射文件

关于Mybatis映射文件的学习笔记

一、基本的增删改查

1.1 操作实例

  • (1)定义接口方法
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface EmployeeMapper {
//增
public abstract void addEmp(Employee emp);

//删
public abstract void deleteEmpById(Integer id);

//改
public abstract void updateEmp(Employee emp);

//查
public abstract Employee getEmpById(int id);
}
  • (2)在映射文件中,填写相应的sql语句
    • 增的标签为:<insert>
    • 删的标签为:<delete>
    • 改的标签为:<update>
    • 查的标签为:<select>
      • 查的标签要额外添加resultType来确定返回值的类型
    • 传入的参数是个对象时,仍然可以直接使用#{}来获取对象属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<mapper namespace="Dao.EmployeeMapper">
<insert id="addEmp" parameterType="Dao.Employee" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name, email, gender)
values(#{lastName}, #{email}, #{gender})
</insert>

<delete id="deleteEmpById">
delete from tbl_employee
where id=#{id}
</delete>

<update id="updateEmp">
update tbl_employee
set last_name=#{lastName}, email=#{email}, gender=#{gender}
where id=#{id}
</update>

<select id="getEmpById" resultType="Dao.Employee">
select id, last_name lastName, gender, email
from tbl_employee
where id = #{id}
</select>
</mapper>
  • (3)Java程序调用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Test
public void test1() throws IOException {
String resource = "./Dao/mybatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);

//不会自动提交数据
SqlSession ss = ssf.openSession();//参数true/false,true自动提交数据,flase需要手动提交数据,默认false

EmployeeMapper mapper = ss.getMapper(EmployeeMapper.class);

//增
Employee employee = new Employee(null, "yjj",'0', "yyy.com");
mapper.addEmp(employee);

//改
mapper.updateEmp(new Employee(3, "yjj", '0', "丫丁丁.com"));

//查
Employee emp = mapper.getEmpById(3);
System.out.println(emp);

//删
mapper.deleteEmpById(3);

//手动提交数据
ss.commit();
}

1.2 拓展:获取自增主键的值

  • 在插入时,mysql支持自增主键,自增主键的获取,mybatis底层是利用了JDBC的**statement.getGenreatedKeys()**的方法
  • 使用方法:
    • <insert>标签添加额外的属性
    • useGenerateKeys="true":开启获取自增主键
    • keyProperty="":将获取的信息封装到JavaBean的哪个属性
  • 举例
1
2
3
4
5
1、修改映射文件中的<insert>标签
<insert id="addEmp" parameterType="Dao.Employee" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name, email, gender)
values(#{lastName}, #{email}, #{gender})
</insert>
1
2
3
4
5
6
2、修改Java程序的增方法进行测试
Employee employee = new Employee(null, "yjj",'0', "yyy.com");
mapper.addEmp(employee);
//上面创建employee并没有对id进行赋值,但是employee.getId()返回的并不是null
//因为获取了自增值,并将该值封装到JavaBean的id属性中
System.out.println(employee.getId());

1.3 拓展:获取非自增主键的值

  • <insert>中添加字标签<selectKey>
    • keyProperty:sql查询出来的数据,封装给JavaBean的那个属性
    • order:执行顺序”BEFORE/AFTER”,在sql语句之前/之后执行
    • resultType:返回结果类型
  • 举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
以获取非自增主键的方式,来实现获取自增主键
<insert id="addEmp" parameterType="Dao.Employee">
insert into tbl_employee(last_name, email, gender)
values(#{lastName}, #{email}, #{gender})

<!--在插入后执行,获取最后一条数据的id,将此id封装到JavaBean中-->
<selectKey keyProperty="id" order="AFTER" resultType="Integer">
select id
from tbl_employee
order by id desc
limit 1;
</selectKey>
</insert>

二、参数传递

2.1 单个参数

  • 对于单个参数,Mybatis不会做任何处理,参数名字可以随便起
  • 使用方法:#{参数名}

2.2 多个参数

  • 对于过个参数。MyBatis对进行特殊处理,会将参数封装到为一个Map集合中
  • 若全局配置文件useActualParamName="true",则该Map存储格式为:
    • Key:param1, param2….paramN 或者 arg0, arg1 …. argN
    • value:按顺序传入的参数
  • 若全局配置文件useActualParamName="false",则该Map存储格式为:
    • Key:param1, param2….paramN 或者 0, 1 …. N
    • value:按顺序传入的参数
  • Mybaits版本不同,默认值可能发生变化
1
2
接口
public abstract Employee getEmpByIdAndLastName(Integer id, String lastName);
1
2
3
4
5
6
映射文件
<select id="getEmpByIdAndLastName" resultType="Dao.Employee">
select *
from tbl_employee
where id=#{arg0} and last_name=#{arg1}
</select>

2.3 命名参数

  • 由于参数一多,上面的方法就十分不便利,由此引出命名参数
  • 命名参数:【明确指定封装的参数时map的Key】
  • 在接口传参时使用格式:Xxxx(@Param(“id”)int id, @Param(“name”)String name)
  • Map存储格式为:
    • Key:使用@Param注解指定的值
    • value:按顺序传入的参数
1
2
接口方法
public abstract Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);
1
2
3
4
5
6
映射文件
<select id="getEmpByIdAndLastName" resultType="Dao.Employee">
select *
from tbl_employee
where id=#{id} and last_name=#{lastName}
</select>

2.4 Map集合

  • 为了方便,我们也可以传入map
  • 直接#{Key}:来取出对象的值
1
2
接口方法:
public abstract Employee getEmpByMap(Map<String, Object> map);
1
2
3
4
5
6
映射文件
<select id="getEmpByMap" resultType="Dao.Employee">
select *
from tbl_employee
where id=#{id} and last_name=#{lastName}
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方法测试
@Test
public void test2() throws IOException {
String resource = "./Dao/mybatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession ss = ssf.openSession();

EmployeeMapper mapper = ss.getMapper(EmployeeMapper.class);

//创建Map集合
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName", "C酱");

Employee emp = mapper.getEmpByMap(map);
System.out.println(emp);
}

2.5 集合/数组

  • Mybatis也会对集合、数组封装为Map
  • Collection:
    • Key:collection[0], collection[1] … collection[N]
    • 使用方法:#{Key}
  • List:
    • Key:list[0], list[1] … list[N]
    • 使用方法:#{Key}
  • Set:
    • Key:set[0], set[1] … set[N]
    • 使用方法:#{Key}
  • Array:
    • Key:array[0], array[1] … array[2]
    • 使用方法:#{Key}
  • 举例:以List集合为例
1
2
接口方法
public abstract Employee getEmpByList(List<Object> list);
1
2
3
4
5
6
映射文件
<select id="getEmpByList" resultType="Dao.Employee">
select *
from tbl_employee
where id=#{list[0]} and last_name=#{list[1]}
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方法测试
@Test
public void test3() throws IOException {
String resource = "./Dao/mybatisConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession ss = ssf.openSession();

EmployeeMapper mapper = ss.getMapper(EmployeeMapper.class);

//创建list集合
ArrayList<Object> list = new ArrayList<>();
list.add(2);
list.add("C酱");

Employee emp = mapper.getEmpByList(list);
System.out.println(emp);
}

2.6 补充

  • (1)若接口定义返回类型为:集合
    • 映射文件,resultType填写集合属性的类型
  • (2)若接口定义返回类型为:Map
    • 映射文件中resultTyoe填写map

2.7 获取参数

  • #{}:可以获取map中的值或pojo对象属性的值
  • ${}:可以获取map中的值或pojo对象属性的值
  • #{} 和 ${} 的区别
    • #{}是以预编译的形式,将参数设置到sql语句中;类似PreparedStatement
    • ${}取出的值直接拼装在sql中,进行sql拼接;类似Statement
  • 使用场景
    • 大多情况下,使用#{}
    • 在原生JDBC不支持占位符的地方(表名),可以用${}进行取值

2.8 参数处理

  • #{},在取出参数时,可以进行一些规则
  • 支持的属性:javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
    • javaType:在Java中的属性
    • jdbcType:在数据库中的属性
    • mode:存储过程(后面讲)
    • numericScale:保留几位小数
    • resultMap:规定封装的结果集
    • typeHandler:类型处理器
    • jdbcTypeName:和jdbcType一样
    • expression:表达式(未来支持的功能)
  • 上述属性中,就jdbcType可能需要进行设置
    • 在我们数据为null时,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错)
    • 解决方法:
      • 因为mybatis对所有的null都映射为jdbc OTHER
      • 解决方法(1):设置jdbcType=NULL
      • 解决方法(2):全局配置文件<setting name="jdbcTypeForNull" value="NULL">

三、resultMap的使用

3.1 基础介绍

  • resultMap和resultType类,来确定返回的类型,但resultMap可以进行自定义
  • resultMap和resultType不能同时使用
  • reslutMap
  • <resultMap id="" type=""></resultMap>
    • id:resultMap的唯一标识
    • type:自定义规则的JavaBean
  • 子标签<id>
  • <id>是用来定义主键
  • <id column="" property=""/>
    • column:数据库中列的字段名
    • property:JavaBean的属性名
    • 将查询对象的字段名,封装到对应JavaBean的属性
  • 子标签<result>

  • <result>用来定义非主键

  • <result column="" property=""/>

    • column:数据库中列的字段名
    • property:JavaBean的属性名
  • 其他不指定的类会自动封装;但是一旦使用resultMap的话,建议全部指定

  • 举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
映射文件
<resultMap id="MyEmp" type="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender" />
<result column="email" property="email" />
</resultMap>

<select id="getEmpById" resultMap="MyEmp">
select *
from tbl_employee
where id = #{id}
</select>

3.2 前期准备

  • (1)为Employee类添加多一个属性为Department
  • (2)创建相应的类(JavaBean),接口,映射文件,并将映射文件注册到全局配置文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

public class Department {
private Integer id;
private String departmentName;
private List<String> emps;

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
public List<String> getEmps() {
return emps;
}
public void setEmps(List<String> emps) {
this.emps = emps;
}

@Override
public String toString() {
return "Department{" +
"id=" + id +
", departmentName='" + departmentName + '\'' +
'}';
}
}
1
2
3
4
接口
public interface DepartmentMapper {
public Department getDeptById(Integer id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
映射文件
<?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">

<mapper namespace="Dao.DepartmentMapper">
<select id="getDeptById" resultType="Dao.Department">
select id, dept_name as departmentName
from tbl_department
where id = #{id}
</select>
<mapper>
  • (3)在数据库中创建相应的表部门表
1
2
3
4
5
创建部门表
create table tbl_department(
id int(11) primary key auto_increment,
dept_name varchar(255)
)
  • (4)在数据库为员工表添加额外字段,并和部门表进行联系
1
2
3
添加字段
alter table tbl_employee
add column dept_id int(11)
1
2
3
4
5
联系两表
alter table tbl_employee
add constraint fk_emp_dept
foreign key(dept_id)
references tbl_department(id)

3.3 联合查询

  • (1)级联属性封装结果集
  • 举例:在查询员工表时,根据员工表的部门号,把相应的部门查询出来
  • 在resultMap中的result,通过对象.属性进行对部门类赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
映射文件
<resultMap id="EmpDept" type="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender" />
<result column="email" property="email" />
<!--级联赋值方式-->
<result column="dept_id" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>

<select id="getEmpAndDept" resultMap="EmpDept">
select A.id, last_name, gender, email, B.id as dept_id, B.dept_name
from tbl_employee as A, tbl_department as B
where A.dept_id = B.id And A.id = #{id}
</select>
  • (2)利用association对象实现
  • 子标签<association property="" javaType=""></association>
    • <assovaition>:指定联合的JavaBean对象
    • property:指定哪个属性是联合对象
    • javaType:联合的类的全类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
映射文件
<resultMap id="EmpDept2" type="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender" />
<result column="email" property="email" />
<association property="dept" javaType="Dao.Department">
<id column="dept_id" property="id" />
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>

<select id="getEmpAndDept" resultMap="EmpDept2">
select A.id, last_name, gender, email, B.id as dept_id, B.dept_name
from tbl_employee as A, tbl_department as B
where A.dept_id = B.id And A.id = #{id}
</select>
  • (3)关联的对象时集合时
    • 关联的对象时多个值时使用
    • 举例:一个部门有多个员工,在查询一个部门时,把该部门的员工查询出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
映射文件
<!--利用Collection实现集合关联-->
<resultMap id="MyDept" type="Dao.Department">
<id column="dept_id" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--collection定义集合类型的属性的封装规则
ofType:集合里面数据的类型
-->
<collection property="emps" ofType="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</collection>
</resultMap>

3.4 分步查询

  • 利用assocation来实现
  • 分布查询的好处:以已有的方法,完成复杂的sql操作,不用额外写sql语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Employee映射文件
<resultMap id="EmpByStep" type="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName" />
<result column="gender" property="gender" />
<result column="email" property="email" />
<!--association:定义关联对象的封装规则
select:查询的方法。关联对象的属性通过哪个sql语句来获取
column:指定哪个值传递给sql语句来进行查询
-->
<association property="dept" select="Dao.DepartmentMapper.getDeptById" column="dept_id">
<!--不填写,默认封装-->
</association>
</resultMap>

<select id="getEmpByIdStep" resultMap="DSM" >
select *
from tbl_employee
where id = #{id}
</select>
1
2
3
4
5
6
Department映射文件
<select id="getDeptById" resultType="Dao.Department">
select id, dept_name as departmentName
from tbl_department
where id = #{id}
</select>
  • 拓展:分步查询传递多个数据
    • 可以将多个值封装到Map中进行传递
    • <association property="" select="" column="{key1=column1, key2=column2...}">
  • Collection实现分步查询
    • 关联的对象时多个值时使用
    • 举例:一个部门有多个员工,在查询一个部门时,把该部门的员工查询出来
1
2
3
4
5
6
7
8
9
10
映射文件
<!--Collection实现分步查询-->
<resultMap id="MyDeptStep" type="Dao.Department">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--collection分步查询使用方法和association类似-->
<collection property="emps" select="resultMap.EmployeeMapperPlus.getEmpByDid" column="id">
<!--默认封装-->
</collection>
</resultMap>

3.5 延迟加载

  • 我们每次查询Employee对象的时候,都将Department一起查询出来
  • 为了提升效率,希望部门信息在我们使用的时候再去查询,也就是延迟加载
  • 在分布查询的基础上,在全局配置文件加上两个配置即可实现延迟加载
    • <setting name="lazyLoadingEnable" value="true"/>:开启所有关联对象都会延迟加载
    • <setting name="aggressiveLazyLoading" value="false"/>:关闭所有对象全部加载
  • 可以不在全局配置文件中开启lazyLoadingEnable
    • association中添加fetchType="lazy/enger来开启延迟加载

3.6 鉴别器

  • mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
  • 举例:封装Employee
    • 如果是男生,把last_name这一列的值赋值给email
    • 如果查询的时女生,则把部门信息查询出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
映射文件
<resultMap id="DSM" type="Dao.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName" />
<result column="gender" property="gender" />
<result column="email" property="email" />
<!--鉴别器:
column:指定判断的列名
javaType:列值对应的java类型
-->
<discriminator javaType="String" column="gender">
<!-- 男生 -->
<case value="0" resultType="Dao.Employee">
<result column="last_name" property="email" />
</case>
<!--女生 -->
<case value="1" resultType="Dao.Employee">
<association property="dept" select="Dao.DepartmentMapper.getDeptById" column="dept_id">
<!--默认封装-->
</association>
</case>
</discriminator>
</resultMap>