大纲 SQL 映射文件 select 标签 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-7
。
属性说明 select
标签用于定义查询操作,拥有以下常用属性:
id
:唯一标识符,用来引用这条语句,需要与接口的方法名一致。parameterType
:参数类型,可以不传递,MyBatis 会根据 TypeHandler
自动推断参数类型。resultType
:返回值类型,可以是类型别名或者全限定类名。如果返回结果是集合,则其值是集合中元素的类型。resultType
不能和 resultMap
同时使用。提示
select
标签还拥有很多其他属性,详细说明请点击这里 查看。
使用案例 返回 List 类型 1 2 3 4 5 public interface EmployeeMapper { public List<Employee> getAllEmp () ; }
1 2 3 <select id ="getAllEmp" resultType ="com.clay.mybatis.bean.Employee" > select id, last_name as lastName, gender, email from t_employee </select >
1 查询结果:[1_Tom_1_tom@gmail.com, 2_Jim_1_jim@gmail.com]
返回 Map 类型(第一种场景) 1 2 3 4 5 6 public interface EmployeeMapper { public Map<String, Object> getEmpById (Long id) ; }
1 2 3 4 5 <select id ="getEmpById" parameterType ="Long" resultType ="Map" > select id, last_name as lastName, gender, email from t_employee where id = #{id} </select >
1 查询结果:{lastName=Tom, gender=1, id=1, email=tom@gmail.com}
返回 Map 类型(第二种场景) 1 2 3 4 5 6 7 8 9 10 11 12 public interface EmployeeMapper { @MapKey("id") public Map<String, Employee> getEmpByLastName (String lastName) ; }
1 2 3 4 5 <select id ="getEmpByLastName" parameterType ="String" resultType ="Map" > select id, last_name as lastName, gender, email from t_employee where last_name like #{lastName} </select >
1 查询结果:{1={lastName=Tom, gender=1, id=1, email=tom@gmail.com}, 2={lastName=Jim, gender=1, id=2, email=jim@gmail.com}}
结果映射 全局 setting 设置 配置说明 – autoMappingBehavior
可以开启自动映射的功能,默认值是 PARTIAL
,唯一的要求是字段名和 JavaBean 属性名需要一致。 – 如果将 autoMappingBehavior
设置为 null
,则 MyBatis 会取消自动映射。 – 另外还可以使用数据库字段命名规范,即 JavaBean 的属性名符合驼峰命名法,如 last_name
映射为 lastName
,简单设置 mapUnderscoreToCamelCase = true
就可以开启自动驼峰命名规则映射功能。
使用案例 1 2 3 4 5 6 <configuration > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings > </configuration >
resultMap 的使用 高级结果映射 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 <select id ="selectBlogDetails" resultMap ="detailedBlogResultMap" > select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select >
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 <resultMap id ="detailedBlogResultMap" type ="Blog" > <constructor > <idArg column ="blog_id" javaType ="int" /> </constructor > <result property ="title" column ="blog_title" /> <association property ="author" javaType ="Author" > <id property ="id" column ="author_id" /> <result property ="username" column ="author_username" /> <result property ="password" column ="author_password" /> <result property ="email" column ="author_email" /> <result property ="bio" column ="author_bio" /> <result property ="favouriteSection" column ="author_favourite_section" /> </association > <collection property ="posts" ofType ="Post" > <id property ="id" column ="post_id" /> <result property ="subject" column ="post_subject" /> <association property ="author" javaType ="Author" /> <collection property ="comments" ofType ="Comment" > <id property ="id" column ="comment_id" /> </collection > <collection property ="tags" ofType ="Tag" > <id property ="id" column ="tag_id" /> </collection > <discriminator javaType ="int" column ="draft" > <case value ="1" resultType ="DraftPost" /> </discriminator > </collection > </resultMap >
属性说明 通过自定义的 resultMap
,可以实现高级结果映射功能。resultMap
标签的属性列表如下:
属性 描述 id 当前命名空间中的一个唯一标识,用于标识一个结果映射。 type 类的完全限定名,或者一个类型别名。 autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior
,默认值是 未设置(unset)
。
resultMap
标签的子标签说明如下:
constructor
- 类在实例化时,用来注入结果到构造方法中 – idArg
- ID 参数;标记结果作为 ID 可以帮助提高整体效能 – arg
- 注入到构造方法的一个普通结果id
– 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能result
– 注入到字段或 JavaBean 属性的普通结果association
- 一个复杂类型的关联,许多结果将包装成这种类型嵌套结果映射 – 关联可以是 resultMap
元素,或是对其它结果映射的引用 collection
- 一个复杂类型的集合嵌套结果映射 – 集合可以是 resultMap
元素,或是对其它结果映射的引用 discriminator
- 使用结果值来决定使用哪个 resultMap
case
- 基于某些值的结果映射嵌套结果映射 – case
也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射 resultMap
标签的 id
和 result
子标签都可以将一个列的值映射到一个简单数据类型(String、int、double、Date 等)的属性或字段。这两者之间的唯一区别是,id
子标签对应的属性会被标记为对象的标识符,在比较对象实例时使用。这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是级联映射)的时候。这两个子标签都有一些属性:
属性 描述 property
映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 column
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName)
方法的参数一样。 javaType
一个 Java 类的全限定名,或一个类型别名。如果你映射到一个 JavaBean,那么 MyBatis 通常可以自动推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType
来保证行为与期望的相一致。 jdbcType
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的” 支持的 JDBC 类型”。只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 typeHandler
使用这个属性,你可以覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
MyBatis 通过内置的 jdbcType
枚举类型支持以下的 JDBC 类型:
自动映射 在简单的场景下,MyBatis 可以自动映射查询结果,但如果遇到复杂的场景,则需要构建一个结果映射,而且这两种策略支持混合使用。当自动映射查询结果时,MyBatis 会获取结果中返回的列名,并在 Java 类中查找相同名字的属性(忽略大小写)。这意味着如果发现了 ID
列和 id
属性,MyBatis 会将列 ID
的值赋给 id
属性。通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔,而 Java 属性一般遵循驼峰命名法约定;为了在这两种命名方式之间启用自动映射,需要在全局的配置文件中将 mapUnderscoreToCamelCase
设置为 true。MyBatis 甚至在提供了结果映射后,自动映射也能工作。在这种情况下,对于每一个结果映射,在 ResultSet
出现的列,如果没有设置手动映射,将被自动映射;在自动映射处理完毕后,再处理手动映射。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-8
。
自动映射等级 MyBatis 提供了三种自动映射等级:
FULL
- 自动映射所有属性NONE
- 禁用自动映射,仅对手动映射的属性进行映射PARTIAL
- 对除在内部定义了嵌套结果映射(也就是关联的属性)以外的属性进行映射默认的自动映射等级是 PARTIAL
,这是有原因的。当对级联查询的结果使用 FULL
时,级联查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。无论设置的自动映射等级是哪种,MyBatis 都支持在结果映射上设置 autoMapping
属性来为指定的结果映射设置启用或禁用自动映射,如下所示:
1 2 3 <resultMap id ="userResultMap" type ="User" autoMapping ="false" > <result property ="password" column ="hashed_password" /> </resultMap >
使用案例之一 1 2 3 <resultMap id ="userResultMap" type ="User" > <result property ="password" column ="hashed_password" /> </resultMap >
1 2 3 4 5 6 7 8 <select id ="selectUsers" resultMap ="userResultMap" > select user_id as "id", user_name as "userName", hashed_password from some_table where id = #{id} </select >
在上面的例子中,id
和 userName
列将被自动映射,hashed_password
列将根据配置进行映射。
使用案例之二 1 2 3 4 5 6 public interface EmployeeMapper { public Employee getById (Long id) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> </resultMap > <select id ="getById" resultMap ="employeeResultMap" > select id, last_name, gender, email from t_employee where id = #{id} </select > </mapper >
级联映射 本节所有的案例代码,都基于以下的表结构和 JavaBean 类编写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 CREATE TABLE `t_department` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `name` varchar (255 ) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; CREATE TABLE `t_employee` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `last_name` varchar (255 ) DEFAULT NULL , `gender` char (1 ) DEFAULT NULL , `email` varchar (255 ) DEFAULT NULL , `dept_id` int (11 ) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8;
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Department { private Long id; private String name; private List<Employee> employees; ...... @Override public String toString () { return "Department [id=" + id + ", name=" + name + ", employees=" + employees + "]" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Employee { private Long id; private String lastName; private String gender; private String email; private Department department; ...... @Override public String toString () { return "Employee [id=" + id + ", lastName=" + lastName + ", gender=" + gender + ", email=" + email + ", department=" + department + "]" ; } }
方式一(级联属性封装) 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-9
。
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpAndDept (Long id) ; }
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 ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <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 ="department.id" /> <result column ="dept_name" property ="department.name" /> </resultMap > <select id ="getEmpAndDept" resultMap ="employeeResultMap" > select emp.id, emp.last_name, emp.gender, emp.email, dept.id as dept_id, dept.name as dept_name from t_employee emp left join t_department dept on emp.dept_id = dept.id where emp.id = #{id} </select > </mapper >
提示
上述 resultMap
的写法,当调用 getEmpAndDept()
方法时,可以实现级联属性的封装(即级联映射 / 级联查询)。
1 打印 getEmpAndDept() 方法的调用结果:Employee [id=6, lastName=Tom, gender=1, email=tom@gmail.com, department=Department [id=1, name=开发部门]]
方式二(嵌套结果映射) 级联映射除了上面的使用方式之外,还可以使用 resultMap
的子标签 association
实现嵌套结果映射。嵌套结果映射的定义,是使用嵌套的结果映射来处理连接结果的重复子集。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-10
。
属性 描述 resultMap
结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。它可以作为使用额外 select
语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet
。这样的 ResultSet
有部分数据是重复的。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许 “串联” 结果映射,以便解决嵌套结果集的问题。 columnPrefix
当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet
中产生重复的列名。指定 columnPrefix
列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 notNullColumn
默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。你可以在这个属性上指定非空的列来改变默认行为,指定后 Mybatis 将只在这些列中任意一列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值是 未设置(unset)
。 autoMapping
如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior
。特别注意,本属性对外部的结果映射无效,所以不能搭配 select
或 resultMap
元素使用。默认值是 未设置(unset)
。
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpAndDept (Long id) ; }
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 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> <association property ="department" javaType ="com.clay.mybatis.bean.Department" > <id column ="dept_id" property ="id" /> <result column ="dept_name" property ="name" /> </association > </resultMap > <select id ="getEmpAndDept" resultMap ="employeeResultMap" > select emp.id, emp.last_name, emp.gender, emp.email, dept.id as dept_id, dept.name as dept_name from t_employee emp left join t_department dept on emp.dept_id = dept.id where emp.id = #{id} </select > </mapper >
若希望 Department 的结果映射可以被重用,那么还可以使用以下的写法:
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 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> <association property ="department" javaType ="com.clay.mybatis.bean.Department" resultMap ="departmentResultMap" /> </resultMap > <resultMap type ="com.clay.mybatis.bean.Department" id ="departmentResultMap" > <id column ="dept_id" property ="id" /> <result column ="dept_name" property ="name" /> </resultMap > <select id ="getEmpAndDept" resultMap ="employeeResultMap" > select emp.id, emp.last_name, emp.gender, emp.email, dept.id as dept_id, dept.name as dept_name from t_employee emp left join t_department dept on emp.dept_id = dept.id where emp.id = #{id} </select > </mapper >
1 打印 getEmpAndDept() 方法的调用结果:Employee [id=8, lastName=Tom, gender=1, email=tom@gmail.com, department=Department [id=1, name=开发部门]]
方式三(嵌套 Select 查询) 级联映射除了上面的使用方式之外,还可以使用 resultMap
的子标签 association
实现嵌套 Select 查询(也叫分段查询)。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-11
。
属性 描述 property
映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 javaType
一个 Java 类的全限定名,或一个类型别名。如果你映射到一个 JavaBean,那么 MyBatis 通常可以自动推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType
来保证行为与期望的相一致。 jdbcType
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的” 支持的 JDBC 类型”。只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 typeHandler
使用这个属性,你可以覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。 column
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName)
方法的参数一样。特别注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 select
查询语句的列名。这会使得 prop1
和 prop2
作为参数对象,被设置为对应嵌套 select
语句的参数。 select
用于加载复杂类型属性的映射语句的 ID,它会从 column
属性指定的列中检索数据,作为参数传递给目标 select
语句。特别注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 select
查询语句的列名。这会使得 prop1
和 prop2
作为参数对象,被设置为对应嵌套 select
语句的参数。 fetchType
可选的,有效值为 lazy
和 eager
。指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled
属性的值。
1 2 3 4 5 public interface DepartmentMapper { public Department getById (Long id) ; }
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpAndDept (Long id) ; }
1 2 3 4 5 6 7 8 9 <mapper namespace ="com.clay.mybatis.dao.DepartmentMapper" > <select id ="getById" parameterType ="Long" resultType ="com.clay.mybatis.bean.Department" > select id, name from t_department where id = #{id} </select > </mapper >
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 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> <association property ="department" javaType ="com.clay.mybatis.bean.Department" select ="com.clay.mybatis.dao.DepartmentMapper.getById" column ="dept_id" > </association > </resultMap > <select id ="getEmpAndDept" resultMap ="employeeResultMap" > select id, last_name, gender, email, dept_id from t_employee where id = #{id} </select > </mapper >
1 打印 getEmpAndDept() 方法的调用结果:Employee [id=16, lastName=Tom, gender=1, email=tom@gmail.com, department=Department [id=1, name=开发部门]]
方式四(集合的嵌套结果映射) 级联映射除了上面的使用方式之外,还可以使用 resultMap
的子标签 association
实现集合的嵌套结果映射(适用于集合类型)。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-12
。
1 2 3 4 5 public interface DepartmentMapper { public Department getDeptById (Long id) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <mapper namespace ="com.clay.mybatis.dao.DepartmentMapper" > <resultMap id ="deptResultMap" type ="com.clay.mybatis.bean.Department" > <id column ="dept_id" property ="id" /> <result column ="dept_name" property ="name" /> <collection property ="employees" ofType ="com.clay.mybatis.bean.Employee" > <id column ="emp_id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> </collection > </resultMap > <select id ="getDeptById" parameterType ="Long" resultMap ="deptResultMap" > select dept.id as dept_id, dept.name as dept_name, emp.id as emp_id, emp.last_name, emp.gender, emp.email from t_department dept left join t_employee emp on dept.id = emp.dept_id where dept.id = #{id} </select > </mapper >
1 打印 getDeptById() 方法的调用结果:Department [id=1, name=开发部门, employees=[Employee [id=1, lastName=Jim, gender=1, email=jim@gmail.com, department=null], Employee [id=2, lastName=Peter, gender=1, email=peter@gmail.com, department=null]]]
方式五(集合的嵌套 Select 查询) 级联映射除了上面的使用方式之外,还可以使用 resultMap
的子标签 association
实现集合的嵌套 Select 查询(也叫集合的分段查询,适用于集合类型)。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-13
。
1 2 3 4 5 public interface DepartmentMapper { public Department getDeptById (Long id) ; }
1 2 3 4 5 public interface EmployeeMapper { public List<Employee> getByDept (Long deptId) ; }
1 2 3 4 5 6 7 8 9 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <select id ="getByDept" resultType ="com.clay.mybatis.bean.Employee" > select id, last_name, gender, email, dept_id from t_employee where dept_id = #{deptId} </select > </mapper >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <mapper namespace ="com.clay.mybatis.dao.DepartmentMapper" > <resultMap id ="deptResultMap" type ="com.clay.mybatis.bean.Department" > <id column ="id" property ="id" /> <result column ="name" property ="name" /> <collection property ="employees" ofType ="com.clay.mybatis.bean.Employee" select ="com.clay.mybatis.dao.EmployeeMapper.getByDept" column ="id" /> </resultMap > <select id ="getDeptById" parameterType ="Long" resultMap ="deptResultMap" > select id, name from t_department where id = #{id} </select > </mapper >
1 打印 getDeptById() 方法的调用结果:Department [id=1, name=开发部门, employees=[Employee [id=1, lastName=null, gender=1, email=jim@gmail.com, department=null], Employee [id=2, lastName=null, gender=1, email=peter@gmail.com, department=null]]]
嵌套 Select 查询 & 延迟加载 嵌套 Select 查询这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳,这个问题被称为 N+1 查询问题
。概括地讲,N+1
查询问题是这样子的:
执行了一个单独的 SQL 语句来获取结果的一个列表(就是 +1
)。 对列表返回的每条记录,执行一个 select
查询语句来为每条记录加载详细信息(就是 N
)。 这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果,则可以全局配置 MyBatis 启用延迟加载。当启用延迟加载后,只有级联属性被使用到的时候,MyBatis 才会发出查询 SQL,以此提高级联加载的性能。
1 2 3 4 5 6 <settings > <setting name ="lazyLoadingEnabled" value ="true" /> <setting name ="aggressiveLazyLoading" value ="false" /> </settings >
lazyLoadingEnabled
:延迟加载,默认值为 false
。aggressiveLazyLoading
:控制具有延迟加载特性的对象的属性的加载情况,true
表示如果对具有延迟加载特性的对象的任意调用会导致这个对象的完整加载,false
表示每种属性按照需要加载。在 MyBatis 3.4.1(包含)
版本之前,默认值为 true
,之后默认值为 false
。值得一提的是,若全局配置不适合特定的需求,MyBatis 还为此提供了局部延时加载功能。在 collection
或 association
标签上加入属性值 fetchType
就可以了,其取值分别是 eager
和 lazy
。
discriminator 鉴别器的使用 有时候,一个数据库查询可能希望根据不同的条件返回多个不同的结果集(但总体上还是有一定的联系的)。鉴别器(discriminator
)标签就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解,它很像 Java 语言中的 switch
语句。一个鉴别器的定义需要指定 column
和 javaType
属性。column
指定了 MyBatis 查询被比较值的地方,而 javaType
用来确保使用正确的相等测试(虽然很多情况下字符串的相等测试都可以工作)。完整的使用说明可查看 官方文档 ,本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-14
。
1 2 3 4 5 public interface DepartmentMapper { public Department getById (Long id) ; }
1 2 3 4 5 public interface EmployeeMapper { public Employee getEmpAndDept (Long id) ; }
1 2 3 4 5 6 7 8 9 <mapper namespace ="com.clay.mybatis.dao.DepartmentMapper" > <select id ="getById" parameterType ="Long" resultType ="com.clay.mybatis.bean.Department" > select id, name from t_department where id = #{id} </select > </mapper >
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 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <resultMap type ="com.clay.mybatis.bean.Employee" id ="employeeResultMap" > <id column ="id" property ="id" /> <result column ="last_name" property ="lastName" /> <result column ="gender" property ="gender" /> <result column ="email" property ="email" /> <discriminator javaType ="String" column ="gender" > <case value ="1" resultType ="com.clay.mybatis.bean.Employee" > <association property ="department" javaType ="com.clay.mybatis.bean.Department" select ="com.clay.mybatis.dao.DepartmentMapper.getById" column ="dept_id" > </association > </case > </discriminator > </resultMap > <select id ="getEmpAndDept" resultMap ="employeeResultMap" > select id, last_name, gender, email, dept_id from t_employee where id = #{id} </select > </mapper >
上述的 discriminator
标签作用是,当 Employee(员工)
的 gender(性别)
属性为 1
时,才级联查询 Department(部门)
的信息,否则不级联查询。