查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,如果还能重新取出来才有用,多数应用也都是查询比修改要频繁。
<select > </select>标签属性参考表:
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。 |
useCache | 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
fetchSize | 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。 |
databaseId | 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。 |
一、select返回值
1、返回list
mapper接口:
public abstract List<Employee> getEmpsByLastNameLike(String lastName);
mapper文件:
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!--resultType:如果返回的是一个集合,要写集合中元素的类型 -->
<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
2、返回map
(1)返回单条记录的map: Map<String, Object>:key为列名称,value为对应的值
mapper接口:
public Map<String, Object> getEmpByIdReturnMap(Integer id);
mapper文件:resultType="map",resultType不再指定为元素类型,而是hashmap类型。
<!--public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>
查询结果:
{manager_id=100, department_id=50, job_id=ST_MAN, employee_id=120, last_name=Weiss, phone_number=650.123.1234, hire_date=2004-07-18, salary=8000.00, first_name=Matthew, email=MWEISS}
(2)多条记录封装一个map:Map<Integer,Employee>:key为其中单条记录的主键,value是记录封装后的javaBean
mapper接口:@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("lastName")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
mapper文件:
<!-- public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.starfall.mybaits.entity.Employee">
SELECT
employee_id AS employeeId,
first_name AS firstName,
last_name AS lastName,
email AS email,
phone_number AS phoneNumber,
hire_date AS hireDate,
job_id AS jobId,
salary AS salary,
manager_id AS managerId,
department_id AS departmentId
FROM
employee
WHERE
last_name like #{lastName}
</select>
查询结果:
{Ernst=Employee [employeeId=104, firstName=Bruce, lastName=Ernst, email=BERNST, phoneNumber=590.423.4568, hireDate=Mon May 21 00:00:00 CST 2007, jobId=IT_PROG, salary=6000.00, managerId=103, departmentId=60], Everett=Employee [employeeId=193, firstName=Britney, lastName=Everett, email=BEVERETT, phoneNumber=650.501.2876, hireDate=Thu Mar 03 00:00:00 CST 2005, jobId=SH_CLERK, salary=3900.00, managerId=123, departmentId=50], Errazuriz=Employee [employeeId=147, firstName=Alberto, lastName=Errazuriz, email=AERRAZUR, phoneNumber=011.44.1344.429278, hireDate=Thu Mar 10 00:00:00 CST 2005, jobId=SA_MAN, salary=12000.00, managerId=100, departmentId=80]}
二、resultMap
1、高级结果映射
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
<!-- 超复杂的 Result Map -->
<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>
(1)ResultMap标签
属性 | 描述 |
---|---|
id | 当前命名空间中的一个唯一标识,用于标识一个result map. |
type | 类的完全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格). |
autoMapping | 如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset。 |
(2)id 标签:<id property="id" column="post_id"/>
id 表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样可以提高整体的性能,尤其是缓存和嵌套结果映射(也就是联合映射)的时候。
result标签:<result property="subject" column="post_subject"/>
id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。比如,你可以这样映射一些简单的东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。 |
column | 数据库中的列名,或者是列的别名。一般情况下,这和 传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType | 一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证期望的行为。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。 |
typeHandler | 我们在前面讨论过的默认类型处理器。使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器实现类的完全限定名,或者是类型别名。 |
2、association
(1)一对一关联查询
查询Employee的同时查询员工对应的部门:Employee===Department
对应的javaBean:
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
private Department dept;
//省略getter和setter方法。。。。
}
SQL:public Employee getEmpAndDept(Integer id);
<!--
使用association定义关联的单个对象的封装规则;
-->
<resultMap type="com.atguigu.mybatis.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.atguigu.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- 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>
association标签的属性说明:
属性 | 描述 |
---|---|
column | 来自数据库的列名,或重命名的列标签。这和通常传递给 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引起 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
select | 另外一个映射语句的 ID,可以加载这个属性映射需要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句作为参数。表格后面 有一个详细的示例。 select 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引起 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
fetchType | 可选的。有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled。 |
(2)association分步查询
步骤: 1、先按照员工id查询员工信息。
2、根据查询员工信息中的d_id值去部门表查出部门信息。
3、部门设置到员工中。
需要额外的一条SQL:根据部门id查部门表的信息
<!--public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
SQL:改写sql映射的resultMap:public Employee getEmpAndDept(Integer id);
<resultMap type="com.atguigu.mybatis.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:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
</select>
说明:column的值为resultMap中的一个result的column,而不是select对应方法的参数名称。建议column和select方法参数同名。
测试:
注意:在执行一种方法时候,例如:public List<Employee> getEmpAndDepByLastName(String lastName);
此时,会查处多条员工和部门的集合信息,有两个查询,一个查询员工,一个查询部门信息,则会出现“N+1 查询问题”。
N+1 查询问题可以是这样引起的:查询了一次员工列表,遍历N个员工的时候,进行N此查询部门的信息。
例如以下示例:
解决办法:对于这种情况,不建议使用延迟加载查询,应该使用join语句一次性的查询员工和部门的信息:
SELECT
emp.*, dep.department_name
FROM
employee emp
LEFT JOIN department dep
ON emp.department_id = dep.department_id
WHERE
emp.employee_id = 102
3、collection
(1)一对多关联查询
查询Department的同时查询该部门对应的员工:Department ==List<Employee>
对应javaBean:
public class Department {
private Integer id;
private String departmentName;
private List<Employee> emps;
//省略getter和setter方法
}
SQL:public Department getDeptByIdPlus(Integer id);
<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--
collection定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
-->
<collection property="emps" ofType="com.atguigu.mybatis.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>
<!-- 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标签属性说明:
ofType:集合里面元素的类型
javaType:集合的类型
(2)collection分步查询
需要额外的一条SQL:根据部门id查询员工的信息
<select id="getEmpByDepId" resultType="com.starfall.mybaits.entity.Employee">
select * from employee where department_id=#{departmentId}
</select>
SQL:改写sql映射的resultMap:public Department getDepAndEmpById(Integer departmentId);
<resultMap type="com.starfall.mybaits.entity.Department" id="depAndEmp">
<id column="department_id" jdbcType="INTEGER" property="departmentId" />
<result column="department_name" jdbcType="VARCHAR" property="departmentName" />
<result column="manager_id" jdbcType="INTEGER" property="managerId" />
<result column="location_id" jdbcType="INTEGER" property="locationId" />
<collection
property="emps"
ofType="com.starfall.mybaits.entity.Employee"
select="com.starfall.mybaits.dao.EmployeeMapper.getEmpByDepId"
column="department_id"></collection>
</resultMap>
<!-- public Department getDepAndEmpById(Integer departmentId); -->
<select id="getDepAndEmpById" resultMap="depAndEmp">
select * from department where department_id = #{departmentId,jdbcType=INTEGER}
</select>
测试:
4、延迟加载
延迟加载前提:需要使用association和collection的分步查询。
(1)全局配置:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认为false.
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
(2)局部配置:
association和collection的局部配置在association和collection的标签配置属性:fetchType,有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled。
5、扩展:多列的值传递过去
分步查询中,association和collection标签中的column:指定将哪一列的值传给select方法
多列的值:将多列的值封装map传递,示例:column="{key1=column1,key2=column2}"
6、鉴别器discriminator
mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
示例:如果查出的是女生:就把部门信息查询出来,否则不查询;如果是男生,把last_name这一列的值赋值给email;
<resultMap type="com.atguigu.mybatis.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:指定封装的结果类型;不能缺少。/resultMap-->
<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
<case value="1" resultType="com.atguigu.mybatis.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>