返回List
编写接口方法
public List<Employee> getEmpsByLastNameLike(String lastName);
编写sql语句
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!-- resultType, 如果要返回的是一个集合,要写集合中的元素类型-->
<select id="getEmpsByLastNameLike" resultType="com.shen.mybaties.bean.Employee" >
select * from tbl_employee where last_name like #{lastName}
</select>
注意:虽然是List集合,但是resultType
还是要写集合元素的类型,MyBatis会自动帮我们把结果集封装成指定元素的集合类型。
返回Map
单条记录的Map
编写接口方法
//返回一个记录的map,key就是列明,值就是对应的值
public Map<String,Object> getEmpByIdReturnMap(Integer id);
编写sql语句
<!-- public Map<String,Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id = #{id}
</select>
注意:这里的resultType="map"
,MyBatis给Map起了别名就是map
,返回的记录就会以Map的形式返回。
多条记录的Map
编写接口方法
//多条记录封装一个map,Map<Integer(指定类型),Employee>:键是记录的某个属性,值是记录封装后的JavaBean
//告诉MyBatis封装这个map的时候,使用哪个属性作为map的key
@MapKey("lastName")
public Map<String,Employee> getEmpsByLastNameReturnMap(String lastName);
注意:这里的Map,Key是指定的bean的某属性值,Value是记录封装的bean对象。
指定Key需要使用@MapKey
这个注解,它的value是bean对象的属性名称。
编写sql语句
<!-- public Map<Integer,Employee> getEmpsByLastNameReturnMap(String lastName); -->
<select id="getEmpsByLastNameReturnMap" resultType="com.shen.mybaties.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
这里的resultType
依旧是Map中Value元素的类型。
自定义映射规则(<resultMap>)
常规写法
编写接口方法
public Employee getEmpById(Integer id);
编写映射规则
<!-- 自定义某个javaBean的封装规则
type:自定义规则的Java类型
id:唯一id方便引用
-->
<resultMap type="com.shen.mybaties.bean.Employee" id="MyEmp">
<!-- 指定主键列的封装规则
id定义主键会底层有优化
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则 -->
<result column="Last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装,推荐:只要写resultMap,就要把全部的映射规则都写上-->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
这个是重点,即编写我们自己定制的映射规则。
(0)属性id:唯一id,用于引用,下面sql语句的resultMap
用这个id进行引用。
(1)子标签<id>
:定义主键的映射规则,相比<result>
,会有底层的优化。
(2)子标签<result>
:定义普通列的映射规则
(3)(1)和(2)的子标签<column>
:结果集的列名,和下面一一对应
(4)(1)和(2)的子标签<property>
:javaBean的属性名,和上面一一对应
注意:若没有指定,则自动进行封装(找对应列名封装)。
编写sql语句
<!-- resultMap:自定义结果集映射规则 ,resultMap和resultType只能存在一个 -->
<!-- public Employee getEmpById(Integer id); -->
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id = #{id}
</select>
联合查询
增加bean
public class Department {
private Integer id;
private String departmentName;
public Department() {
super();
}
public Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
}
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;
}
@Override
public String toString() {
return "Department [id=" + id + ", departmentName=" + departmentName + "]";
}
}
增加成员变量。
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
private Department dept;
每个员工都有一个部门信息。
利用级联属性
编写接口方法
public Employee getEmpAndDept(Integer id);
编写映射规则
<!--
联合查询:级联属性封装结果集
-->
<resultMap type="com.shen.mybaties.bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
这是重点:其中的did和dept_name都是部门信息,后面的property使用级联属性封装结果集
,使用dept.id和dept.departmentName
。
编写sql语句
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp">
select e.id id,e.last_name last_name, e.gender gender, e.d_id d_id,
d.id did, d.dept_name dept_name
from tbl_employee e, tbl_dept d
where e.d_id=d.id and e.id=#{id}
</select>
这里的resultMap
引用的就是刚才的id,这里select出来的结果集列就用到了自定义的映射规则。
利用association标签
自定义的映射规则
<!-- 使用association定义关联的单个对象的封装规则 -->
<resultMap type="com.shen.mybaties.bean.Employee" id="MyDifEmp2">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<!-- association可以指定联合的javaBean对象
property="dept" 指定哪个属性是联合的对象
javaType: 指定这个属性对象的类型 (不能省略)
-->
<association property="dept" javaType="com.shen.mybaties.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
这里使用association标签进行联合对象的属性映射。
分步查询
利用association标签
编写自定义的映射规则
<!-- 使用association进行分步查询:
1.按照员工ID查询id信息
2.根据查询员工信息中的d_id值去部门表查出部门信息
3.部门设置到员工信息中
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的类型
-->
<resultMap type="com.shen.mybaties.bean.Employee" id="MyEmpByStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
-->
<association property="dept"
select="com.shen.mybaties.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>
重点
:<association>
中需要配置几个属性。
(1)select:表明当前属性是调用select指定的方法查出的结果,这里需要用到其他的select方法,我们需要编写对应的Mapper接口和Mapper XML文件。
(2)column:指定将哪一列的值传递给这个select方法(猜测只能传一个)。
编写Mapper接口和Mapper XML
Department属性的Mapper接口:
public interface DepartmentMapper {
public Department getDeptById(Integer id);
}
DepartmentMapper接口对应的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">
<mapper namespace="com.shen.mybaties.dao.DepartmentMapper">
<!-- public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.shen.mybaties.bean.Department">
select id,dept_name departmentName from tbl_dept where id = #{id};
</select>
</mapper>
可以看出:这个查询方法需要一个参数id,而在<assiciation>
中的column
中指定一个参数传进来,这个参数是上层结果集的某列。
EmployeeMapper的XML文件:
<!-- public Employee getEmpByIdStep(Integer id); -->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id = #{id}
</select>
测试
这里看到:
(1)先进行员工信息的查询
(2)利用员工信息的某列,根据这列去查询部门信息
(3)再将部门信息整合到员工信息中。
延迟加载
在使用分布查询的基础上,我们可以简单的通过两个设置,来达到延迟加载的效果(懒加载)。
全局配置文件中加入如下两个设置:
<settings>
<!-- 其他配置 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
这样就可以达到延迟加载的效果,即用到才去查询。
注意点:<association>
只能是单个的联合对象,而不能去加载一个对象的集合。
集合类型的属性封装
场景:查询部门的时候将部门对应的所有员工信息也查询出来。
public class Department {
private Integer id;
private String departmentName;
private List<Employee> emps;
}
collection嵌套结果集的方式
编写映射规则
<!--
collection嵌套结果集的方式,定义关联的集合类型的属性封装规则
-->
<resultMap type="com.shen.mybaties.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--
collection定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
-->
<collection property="emps" ofType="com.shen.mybaties.bean.Employee">
<!-- 定义这个集合的封装规则 -->
<id column="eid" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
重点
:由于Department的emps属性为一个集合,所以封装的时候,要使用<collection>
标签来封装。
其中两个属性:
(1)property:表明是上层对象的哪一个属性名。
(2)ofType:表明集合内部的元素类型。(不要想当然,不是JavaType
)
sql语句
<!-- public Department getDeptByIdPlus(Integer id); -->
<select id="getDeptByIdPlus" resultMap="MyDept">
SELECT d.id did,d.dept_name dept_name,
e.id eid,e.last_name last_name,
e.email email,e.gender gender
FROM tbl_dept d
LEFT JOIN tbl_employee e
ON d.id=e.d_id
WHERE d.id = #{id}
</select>
这里我们使用连接查询(效果一样)。
collection分步查询的方式
编写映射规则以及sql语句
<resultMap type="com.shen.mybaties.bean.Department" id="MyDeptStep">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps"
select="com.shen.mybaties.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="id"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name from tbl_dept where id = #{id}
</select>
就像单元素的分步查询一样,但这里使用的是collection
而不是association
,它内部也有一个select
属性和column
属性,同样select负责第二步的查询语句,column负责传入select需要的参数。
编写第二步查询的Mapper接口方法和Mapper XML
public List<Employee> getEmpsByDeptId(Integer deptId);
<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
<select id="getEmpsByDeptId" resultType="com.shen.mybaties.bean.Employee">
select * from tbl_employee where d_id=#{deptId}
</select>
上面两个是为了部门查询时,第二步查询员工信息的查询语句。
分步查询的补充
传递多值
不管是单元素(association
)还是集合类型(collection
),进行后面步骤查询的时候,都需要一个select属性
,这里面规定一个外部的查询引用(namespace+select id)
,通常他都需要参数才能进行查询(#{XX}),同时有一个column属性
,是为了传入select需要的参数,column是上层查询的结果集的列名
。
解决办法:将多列的值封装map传递 ,column="{key1=column1,key2=column2}"
局部延迟加载配置
<association>
和<collection>
都有一个属性叫fetchType
,默认为lazy。
虽然在全局配置中,我们开启了延迟加载,但是可以在局部进行配置,仅对当前的封装生效。
fetchType有lazy
和eager
两个值,字面意思。
discriminator鉴别器
mybits可以使用discriminator判断某列的值,然后根据某列的值改变封装行为。
编写带有鉴别器的映射规则
规则:
如果查出的是女生,就把部门信息查询出来,否则不查询。
如果是男生,把last_name这一列的值赋值给email。
<!-- 鉴别器:
mybits可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装Employee:
如果查出的是女生,就把部门信息查询出来,否则不查询
如果是男生,把last_name这一列的值赋值给email
-->
<resultMap type="com.shen.mybaties.bean.Employee" id="MyEmpDis">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
column:指定判定的列名
javaType:列值对应的java类型
-->
<discriminator javaType="string" column="gender">
<!-- 女生 resultType:指定封装的结果类型;不能缺少resultType或resultMap-->
<case value="0" resultType="com.shen.mybaties.bean.Employee">
<association property="dept"
select="com.shen.mybaties.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!-- 男生 -->
<case value="1" resultType="com.shen.mybaties.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="last_name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>