方法返回List集合
- 实际开发中在定义查询方法的时候,往往会将多个数据包装到一个List集合中返回,MyBatis也支持这样的操作.
- 示例:定义接口方法
public List<Employee> getAll(@Param("column") String column);
- 配置sql映射文件
- 此时resultType属性的类型,应该是Lis<Employee>集合中的类型,也就是Employee类型
<select id="getAll" resultType="mao.shu.vo.Employee">
SELECT *
FROM employee
ORDER BY ${column}
</select>
- 测试方法
@Test
public void getAll(){
EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
List<Employee> emp = employeeDAO.getAll("id");
for (Employee vo : emp){
System.out.println(vo);
}
}
返回Map
- 在实际开发中,有些时候会将查询方法返回一个Map集合,如果使用MyBatis实现,则MyBatis会自动对查询结果进行处理.
- 返回一条数据
- 如果查询结果返回的只是一条数据,那么返回的Map集合中,key=字段名称,value=字段对应的内容.
- 示例:查询以下的数据
- 定义接口的查询方法
public Map<String,Object> getEmp(Integer id);
- 定义sql映射文件
- sql语句的resultType 属性值为 Map类型的别名"map"
<select id="getEmp" resultType="map">
SELECT id, ename, age, job
FROM employee
WHERE id = #{id}
</select>
- 测试方法
@Test
public void getEmp(){
EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
Map<String,Object> emp = employeeDAO.getEmp(1);
System.out.println(emp);
}
- 返回多条数据
- 如果返回多条数据,则可以使用 @MapKey 注解来指定使用哪个属性用于做Map集合的key,MyBatis会自动将多条数据解析为实体类对象作为Map集合的value
- 示例:查询以下数据
- 定义接口方法
@MapKey("id")
public Map<Integer,Employee> getEmpsByName(String name);
- 定义sql映射文件
<select id="getEmpsByName" resultType="map">
SELECT id, ename, age, job
FROM employee
WHERE ename = #{ename}
</select>
- 测试方法
@Test
public void getEmpsByName(){
EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
Map<Integer,Employee> emp = employeeDAO.getEmpsByName("添加测试");
System.out.println(emp);
}
自定义结果映射规则
- 当有些特殊的字段于类属性名称相差太大时,可以通过使用<resultMap>标签自定义转换规则
- 示例:自定义数据表字段于类属性转换规则
- 使用<resultMap>标签设置转换规则
- 在<select>sql映射标签中使用 resultMap属性指定使用的转换规则
<?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="mao.shu.dao.EmployeeDAOPlus">
<!--配置数据表Employee表的字段与Employee类属性之间的转换规则
<id> 标签作为数据表的主键,为特殊字段,
column指定数据表对应的列名称
property指定java类对应的属性名称
<result>标签描述普通的字段与属性
-->
<resultMap id="employeeToEmp" type="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
</resultMap>
<!--sql语句中使用 resultMap 属性指定自定义的转换类型-->
<select id="getOneEmp" resultMap="employeeToEmp">
SELECT id, ename, age, job
FROM employee
WHERE id = #{id}
</select>
</mapper>
- 测试规则
public class EmployeeDAOPlusTest {
private SqlSession sqlSession;
@Before
public void before() throws IOException {
String resource = "config/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
this.sqlSession = sqlSessionFactory.openSession();
}
@After
public void after(){
this.sqlSession.commit();
this.sqlSession.close();
}
@Test
public void getOneEmp(){
EmployeeDAOPlus employeeDAOPlus = this.sqlSession.getMapper(EmployeeDAOPlus.class);
Employee employee = employeeDAOPlus.getOneEmp(1);
System.out.println(employee);
}
}
关联查询
- 数据库中有两张表,雇员表和部门表,这两张表为单向一对第一关系
- Employee(描述雇员)
- Department(描述部门)
- 每一个雇员表中包含一个dept_id字段,对应Department部门表的id
- Employee数据表的实体类
public class Employee {
private Integer id;
private String ename;
private Integer age;
private String job;
private Department department;
```
}
- Department数据表的实体类
public class Department {
private Integer deptID;
private String dname;
...
}
级联查询结果封装
- 定义接口查询方法
public Employee getEmpAndDepartment(Integer id);
- 定义sql映射文件
<resultMap id="empAndDept" type="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
<result column="deptno" property="department.deptID"/>
<result column="dname" property="department.dname"/>
</resultMap>
<select id="getEmpAndDepartment" resultMap="empAndDept">
SELECT e.id id, e.ename ename, e.age age, e.job job, e.dept_id deptid, d.dept_id deptno, d.dname dname
FROM employee e,
department d
WHERE e.dept_id = d.dept_id
AND e.id = #{id}
</select>
- 测试程序
@Test
public void getEmpAndDepartment(){
EmployeeDAOPlus employeeDAOPlus = this.sqlSession.getMapper(EmployeeDAOPlus.class);
Employee employee = employeeDAOPlus.getEmpAndDepartment(1);
System.out.println(employee);
}
- 测试结果
association定义关联对象封装规则
- 示例:修改sql映射文件
<resultMap id="empAndDept" type="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
<!--<result column="deptno" property="department.deptID"/>
<result column="dname" property="department.dname"/>-->
<!--使用association描述关联对象
javaType为对应的java类名称,该属性必须要指定
-->
<association property="department" javaType="mao.shu.vo.Department">
<id column="deptno" property="deptID"/>
<result column="dname" property="dname"/>
</association>
</resultMap>
<select id="getEmpAndDepartment" resultMap="empAndDept">
SELECT e.id id, e.ename ename, e.age age, e.job job, e.dept_id deptid, d.dept_id deptno, d.dname dname
FROM employee e,
department d
WHERE e.dept_id = d.dept_id
AND e.id = #{id}
</select>
- 测试代码
@Test
public void getEmpAndDepartment(){
EmployeeDAOPlus employeeDAOPlus = this.sqlSession.getMapper(EmployeeDAOPlus.class);
Employee employee = employeeDAOPlus.getEmpAndDepartment(1);
System.out.println(employee);
}
association分步查询
-
对于每一个数据表都有对应的一个操作接口,当要使用关联查询的时候,可以通过<association>调用其他sql语句来为关联对象赋值.
-
使用association指定关联属性使用的查询方法:
- property:指定关联属性名称
- select:指定调用的方法为关联属性赋值,select值为 方法sql映射文件的 namespace+对应<select>的id值
- column:将查询出的哪一列数据传递给调用的方法
-
例如:雇员类中拥有一个部门对象,当查询一个雇员信息的时候,希望将部门信息一起查出来,并赋值给部门对象.
- 部门接口操作方法
public Department getDeptById(Integer id);
- getDeptById()方法的sql映射文件
<mapper namespace="mao.shu.dao.DepartmentDAO">
<select id="getDeptById" resultType="mao.shu.vo.Department">
SELECT d.dept_id, dname
FROM department d
WHERE dept_id = #{id}
</select>
</mapper>
- 雇员操作接口方法
public Employee getEmpStep(Integer id);
- getEmpStep()方法的sql映射文件
<select id="getEmpStep" resultMap="getEmpStepResultMap">
SELECT id, ename, age, job, dept_id
FROM employee
WHERE id = #{id}
</select>
<resultMap id="getEmpStepResultMap" type="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
<!--使用association指定关联属性的封装规则
property:指定关联属性名称
select:指定调用的方法为关联属性赋值,select值为 方法sql映射文件的 namespace+对应<select>的id值
column:将查询出的哪一列数据传递给调用的方法
-->
<association
property="department"
select="mao.shu.dao.DepartmentDAO.getDeptById"
column="dept_id">
</association>
</resultMap>
- 测试方法
@Test
public void getEmpStep(){
EmployeeDAOPlus employeeDAOPlus = this.sqlSession.getMapper(EmployeeDAOPlus.class);
Employee employee = employeeDAOPlus.getEmpStep(1);
System.out.println(employee);
}
懒加载
- 当进行查询时,默认情况下会对关联属性一起进行查询,也就是说尽管没有用到关联属性,MyBatis也会默认去调用关联属性的方法,导致过多的方法问数据库,导致性能下降.
- 示例:开启懒加载
- 修改MyBatis配置文件
<settings>
<!--开启懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
定义关联集合封装规则
- 当实体类中拥有一个集合的关联属性,在编写映射sql语句时,可以使用"<collection> 标签来处理集合属性封装规则.
- 示例:一个部门中会包含多个雇员,在部门的实体类中,可以使用List集合来描述多个雇员信息
- 定义接口查询方法,根据一个部门id查询一个部门信息,并且将部门中所有的雇员信息一起查出来
public Department getDeptAndEmpsById(Integer id);
- 配置sql映射文件
<select id="getDeptAndEmpsById" resultMap="getDeptAndEmpsById_resultMap">
SELECT dept.dept_id deptId, dept.dname, emp.id empId, emp.ename, emp.age, emp.job, emp.dept_id
FROM department dept
LEFT JOIN employee emp
ON dept.dept_id = emp.dept_id
WHERE dept.dept_id = #{id}
</select>
<resultMap id="getDeptAndEmpsById_resultMap" type="mao.shu.vo.Department">
<id column="deptId" property="deptId"/>
<result column="dname" property="dname"/>
<!--使用collection定义集合的封装规则
property:描述实体类中集合的名字
ofType:描述集合中元素的类型
-->
<collection property="emps" ofType="mao.shu.vo.Employee">
<id column="empId" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
</collection>
</resultMap>
- 测试方法
@Test
public void getDeptAndEmpsById(){
DepartmentDAO departmentDAO = this.sqlSession.getMapper(DepartmentDAO.class);
Department department = departmentDAO.getDeptAndEmpsById(1);
System.out.println(department);
List<Employee> emps = department.getEmps();
for (Employee emp : emps){
System.out.println(emp);
}
}
collection分步查询$延迟加载
- 分布查询:
- 先查询出部门信息
- 在查询出部门中所有的员工信息
- 示例:在雇员接口上添加根据部门id查询所有的雇员的方法
public List<Employee> getEmpsByDeptId(Integer deptId);
- 在sql映射文件中添加对应的sql查询语句
<select id="getEmpsByDeptId" resultType="mao.shu.vo.Employee">
SELECT id,ename,age,job,dept_id
FROM employee
WHERE dept_id = #{id}
</select>
- 在部门接口中添加分布查询的方法
public Department getDeptAndEmpsByIdStep(Integer id);
- 修改部门的sql映射文件,调价对应的sql映射语句
<select id="getDeptAndEmpsByIdStep" resultMap="getDeptAndEmpsByIdStep_resultMap">
SELECT dept_id, dname
FROM department
WHERE dept_id = #{id}
</select>
<resultMap id="getDeptAndEmpsByIdStep_resultMap" type="mao.shu.vo.Department">
<id column="dept_id" property="deptID"/>
<result column="dname" property="dname"/>
<!--
分步查询:
使用select指定要使用的查询方法
column:指定要将那列数据传给调用的方法
-->
<collection property="emps" select="mao.shu.dao.EmployeeDAOPlus.getEmpsByDeptId" column="dept_id"/>
</resultMap>
- 测试方法
@Test
public void getDeptAndEmpsByIdStep(){
DepartmentDAO departmentDAO = this.sqlSession.getMapper(DepartmentDAO.class);
Department department = departmentDAO.getDeptAndEmpsByIdStep(1);
System.out.println(department);
List<Employee> emps = department.getEmps();
for (Employee emp : emps){
System.out.println(emp);
}
}
分布查询传递多列值
- 示例:传递多个列值
<resultMap id="getDeptAndEmpsByIdStep_resultMap" type="mao.shu.vo.Department">
<id column="dept_id" property="deptID"/>
<result column="dname" property="dname"/>
<!--
column:可以同时传递多个列值
格式为:{关键字=列名称,关键字=列名称...}
-->
<collection column="{id=dept_id,dn=dname}" property="emps" select="mao.shu.dao.EmployeeDAOPlus.getEmpsByDeptId" />
</resultMap>
- 接收列值
- 接收时使用"#{关键字}"即可接收对应的列值
<select id="getEmpsByDeptId" resultType="mao.shu.vo.Employee">
SELECT id,ename,age,job,dept_id
FROM employee
WHERE dept_id = #{id}
</select>
- 示例:覆盖全局设置的懒加载策略
- 在<association>标签或者<collection>标签中都可以定义对关联属性的懒加载策略
- 使用 fetchType 属性控制懒加载策略
- lazy:使用懒加载
- eager:不使用懒加载,对于关联属性立即加载
- 使用 fetchType 属性控制懒加载策略
<resultMap id="getDeptAndEmpsByIdStep_resultMap" type="mao.shu.vo.Department">
<id column="dept_id" property="deptID"/>
<result column="dname" property="dname"/>
<!--
分步查询:
使用select指定要使用的查询方法
column:指定要将那列数据传给调用的方法
-->
<!--
column:可以同时传递多个列值
格式为:{关键字=列名称,关键字=列名称...}
-->
<!--
在<association>标签或者<collection>标签中都可以定义对关联属性的懒加载策略
使用 fetchType 属性控制懒加载策略
lazy:使用懒加载
eager:不使用懒加载,对于关联属性立即加载
-->
<collection fetchType="eager" column="{id=dept_id,dn=dname}" property="emps" select="mao.shu.dao.EmployeeDAOPlus.getEmpsByDeptId" />
</resultMap>
discrimination鉴别器
-
MyBatis允许在为结果进行封装的时候,使用鉴别器来判断某列的值,根据不同的值来对结果进行不同的封装
-
示例:对查询雇员的结果中, 如果ename为"测试人员",则将job属性设置为ename字段值
<resultMap id="empAndDept" type="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="job" property="job"/>
<!--<result column="deptno" property="department.deptID"/>
<result column="dname" property="department.dname"/>-->
<!--使用association描述关联对象
javaType为对应的java类名称,该属性必须要指定
-->
<association property="department" javaType="mao.shu.vo.Department">
<id column="deptno" property="deptID"/>
<result column="dname" property="dname"/>
</association>
<!--辨别器
如果ename为"测试人员",则将job属性设置为ename字段值
javaType:字段对应的java类型
column:判断的数据列
<case value="测试数据">:判断对应的数据列值是否为value值
如果是,则以\<case\>标签中的封装规则为结果进行封装
-->
<discriminator javaType="string" column="ename">
<case value="测试数据" resultType="mao.shu.vo.Employee">
<id column="id" property="id"/>
<result column="ename" property="job"/>
</case>
</discriminator>
</resultMap>