赞
踩
在setting元素中有两个可以配置的选项 autoMappingBehavior 和 mapUnderscoreToCamelCase,它们是控制自动映射和驼峰映射的开关。一般而言,自动映射会使用得多一点,因为可以通过SQL别名机制处理一些细节。
配置自动映射的autoMappingBehavior选项的取值范围是:
package com.learn.ssm.pojo;
private int id;
private String roleName;
private String note;
/* getter and setter */
}
只要SQL列名和属性名保持一致,就会形成自动映射,比如通过角色编码(id)获取Role,如下代码:
<select id="getRole" parameterType="int" resultType="role">
select id,role_name as roleName,note
from t_role
where id=#{id}
</select>
这样就可以直接映射上去,无需做另外的配置,减少了工作量。
如果系统都严格按照驼峰命名法(比如,数据库的字段为role_Name,则POJO属性名为roleName;又如数据库字段为user_name,则POJO属性名为userName),那么只需要在配置把mapUnderscoreToCamelCase设置为true就行了。
在Mybatis中允许map接口通过键值对传递多个参数,把接口方法定义为:
public List<Role> findRolesByMap(Map<String, Object> parameterMap);
此时,传递给映射器的是一个map对象,使用它在sql中设置对应的参数,如下代码:
<select id="findRoleByMap" parameterType="map" resultType="role">
select id, role_name as roleName, note from t_role
where role_name like concat('%', #{roleName}, '%'})
and note like concat('%', #{note}, '%')
</select>
注意,roleName和note是map的键,传递参数应该像是如下这样:
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("roleName",1);
map.put("note",1);
List<Role> roleList = roleMapper.findRolesByMap(map);
不推荐使用map传递参数,原因有二:
MyBatis为开发者提供了一个注解Param,可以通过它去定义映射器的参数名称,使用它可以得到更好的可读性,将接口方法定义为:
public List<Role> findRolesByAnnotation(@Param("roleName") String roleName, @Param("note") String note);
<select id="findRolesByAnnotation" resultType="role">
select id, role_name as roleName, note
from t_role
where roleName like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
注意,此时不需要给出parameterType属性,让Mybatis自动探索就可以了。
通过改写使得可读性大大提高了,但是有一个麻烦:SQL很复杂,拥有大于十个参数,那么接口方法的参数个数也就多了,使用起来就很不容易,不过Mybatis还提供了JavaBean传递形式。
定义一个参数POJO —— RoleParams,如下:
package com.learn.ssm.pojo.params; public class RoleParams { private String roleName; private String note; public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
此时接口方法定义为:
public List<Role> findRolesByBean(RoleParams roleParams);
<select id="findRolesByBean" parameterType="com.learn.ssm.pojo.params.RoleParams"
resultType="role">
select id, role_name as roleName, note
from t_role
where roleName like concat('%',#{roleName},'%')
and note like concat('%',#{note},'%')
</select>
如果查询一个角色,可以通过角色名称和备注进行查询,与此同时还要支持分页,而分页的POJO如代码:
package com.learn.ssm.pojo.params; public class PageParams { private int start; private int end; public int getStart() { return start; } public void setStart(int start) { this.start = start; } public int getEnd() { return end; } public void setEnd(int end) { this.end = end; } }
这个时候的接口设计如下:
public List<Role> findByMix(@Param("params") RoleParams roleParams, @Param("pags") PageParams pageParams);
<select id="findByMix" resultType="role">
select id, role_name as roleName, note
from t_role
where role_name like concat('%', #{params.roleName}, '%')
and note like concat('%', #{params.note},'%')
limit #(page.start), #{page.limit}
</select>
这样就能使用混合参数了
<?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.learn.ssm.mapper.UserMapper"> <resultMap id="userMapper" type="user"> <result property="id" column="id"/> <result property="userName" column="user_name"/> <result property="password" column="password"/> <result property="sex" column="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/> <result property="mobile" column="mobile"/> <result property="tel" column="tel"/> <result property="email" column="email"/> <result property="note" column="note"/> </resultMap> <select id="getUser" parameterType="long" resultMap="userMapper"> select id,user_name,password,sex,mobile,tel,email,note from t_user where id=#{id} </select> </mapper>
Mybatis 不仅支持分页,它还内置了一个专门处理分页的类:RowBounds,RowBounds源码如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.ibatis.session; public class RowBounds { public static final int NO_ROW_OFFSET = 0; public static final int NO_ROW_LIMIT = 2147483647; public static final RowBounds DEFAULT = new RowBounds(); private int offset; private int limit; public RowBounds() { this.offset = 0; this.limit = 2147483647; } public RowBounds(int offset, int limit) { this.offset = offset; this.limit = limit; } public int getOffset() { return this.offset; } public int getLimit() { return this.limit; } }
offset是偏移量,即从第几行开始记录,limit是限制条数,默认为0到java最大整数,使用非常简单,只要给接口增加一个RowBounds参数:
public List<Role> findByRowBounds(@Param("roleName") String roleName, @Param("note") String note, RowBounds rowBounds);
<select id="findByRowBounds" resultType="role">
select id, role_name as roleName, note
from t_role
where role_name like concat('%', #{params.roleName}, '%')
and note like concat('%', #{params.note},'%')
</select>
注意,此时代码并没有任何关于分页的信息,但是Mybatis会自动识别它,使用如下代码进行测试:
package com.learn.ssm.chapter5; import com.learn.ssm.mapper.RoleMapper; import com.learn.ssm.pojo.Role; import com.learn.ssm.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.SqlSession; import java.util.List; public class Main { public static void main(String[] args){ String resource = "com/learn/ssm/chapter5/resource/mybatis-config.xml"; SqlSession sqlSession = null; try{ sqlSession = SqlSessionFactoryUtil.openSqlSession(resource); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); RowBounds rowBounds = new RowBounds(0,20); List<Role> roleList = roleMapper.findByRowBounds("role_name","note",rowBounds); System.out.println(roleList.size()); }catch (Exception e){ e.printStackTrace(); }finally { if(sqlSession != null){ sqlSession.close(); } } } }
<insert id="insertRole" parameterType="role">
insert into t_role(role_name,note)
values (#{roleName},#{note})
</insert>
跟select元素大同小异
有时候增加新用户的时候,首先会插入用户表的记录,然后插入用户和角色关系表,插入用户时如果没有办法拿到用户的主键,那么就没有办法插入用户和角色关系表了,因此在这个时候要拿到对应的主键。
JDBC中的Statement对象在只想插入的SQL后,可以通过getGeneratedKeys方法获得数据库生成的主键,这样便能达到获取主键的功能。在insert语句中有一个开关属性,userGenerateKeys,用来控制是否打开这个功能,它的默认值为false,当打开这个开关,还要配置其属性KeyProperty或者KeyColumn,告诉系统把生成的主键放入哪个属性,如果存在多个主键,则要用(。)分隔开
<insert id="insertRole" parameterType="role"
useGeneratedKeys="true" keyProperty="id">
insert into t_role(role_name,note)
values (#{roleName},#{note})
</insert>
useGeneratedKeys代表采用了JDBC的Statement对象的getGeneraterdKeys方法返回主键,而KeyProperty则代表用哪个POJO的属性,这里是id,说明它会用数据库生成的主键去赋值给这个POJO,测试主键回填:
有时候主键可能依赖于某些规则,比如取消角色表(t_role)的id递增规则,而将其修改:
<insert id="insertRole" parameterType="role">
<selectKey keyProperty="id" resultType="long" order="BEFORE">
select if(max(id) = null, 1, max((id) + 3) from t_role)
</selectKey>
insert into t_role(id,role_name,note)
values (#{id},#{roleName},#{note})
</insert>
执行完返回一个整数,代表影响了数据库的行数,先来看看更新和删除角色表:
<delete id="deleteRole" parameterType="int">
delete
from t_role
where id=#{id}
</delete>
<update id="updateRole" parameterType="role">
update t_role
set role_name=#{roleName},note=#{note}
where id=#{id}
</update>
sql 元素的作用在于可以定义一条sql的一部分,方便后面的sql引用,如下所示:
<sql id="roleCols">
id, role_name as roleName, note
</sql>
<select id="getRole" parameterType="int" resultType="role">
<!--select id,role_name as roleName,note -->
select <include refid="roleCols"/>
from t_role
where id=#{id}
</select>
还支持变量传递:
<sql id="roleCols">
${alias}.id, ${alias}.role_name,${alias}.note
</sql>
<select id="getRole" parameterType="long" resultMap="roleMap"
select
<include refid="roleCold">
<property name="alias" value="r"/>
</include>
from t_role r where id = #{id}
</select>
resultMap的作用是定义映射规则,级联的更新,定制类型转化器等。resultMap定义的主要是一个结果集的映射关系,也就是SQL到JavaBean的映射关系定义,它也支持级联等特性。只是Mybatis现有的版本只支持resultMap查询,不支持更新或者保存。
resultMap 元素的自元素,如代码:
<resultMap id="" type="">
<constructor>
<idArg/>
<arg/>
</constructor>
<id/>
<result/>
<association/>
<collection/>
<discriminator javaType="">
<case value=""></case>
</discriminator>
</resultMap>
其中constructor元素用来配置构造方法,一个POJO可能不存在没有参数的构造方法,可以使用constructor进行配置。假设角色类RoleBean不存在没有参数的构造方法,它的构造方法声明为public RoleBean(Integer id, String roleName),那么需要配置结果集,如代码:
<resultMap .....>
<constructor>
<idArg column="id" javaType="int"/>
<arg column="role_name" javaType="string"/>
</constructor>
...
</resultMap>
id 元素表示哪个列是主键,允许多个主键,多个主键则称为联合主键,result是配置POJO到SQL列名的映射关系。
一般而言,任何select语句都可以使用map存储,如下代码:
<select id="findColorByNote" parameterType="string" resultType="map">
select id,color,note from t_color
where note like concat('%','#{note}','%')
</select>
使用map原则上是可以匹配所有结果集的,但是使用map接口就代表可读性下降,更多情况下会使用POJO方式。
使用map方式就意味着可读性的丢失,POJO是最常用的方式。一方面可以使用自动映射,正如使用resultType属性一样,但是有时候需要更为复杂的映射或者级联,这个时候还可以使用select语句的resultMap属性配置映射集合,只是使用前要配置类似的resultMap,如代码:
<resultMap id="roleResultMap" type="com.learn.ssm.pojo.Role">
<id property="id" column="id"/>
<result property="roleName" column="role_name"/>
<result property="note" column="note"/>
</resultMap>
resultMap元素的属性id代表这个resultMap的标识,type代表要映射的pojo。
配置了resultMap之后就不允许在配置resultType
++HealthForm.java++
package com.learn.ssm.pojo; public abstract class HealthForm { private Long id; private Long empId; private String heart; private String liver; private String lung; private String kidney; private String note; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getEmpId() { return empId; } public void setEmpId(Long empId) { this.empId = empId; } public String getHeart() { return heart; } public void setHeart(String heart) { this.heart = heart; } public String getLiver() { return liver; } public void setLiver(String liver) { this.liver = liver; } public String getLung() { return lung; } public void setLung(String lung) { this.lung = lung; } public String getKidney() { return kidney; } public void setKidney(String kidney) { this.kidney = kidney; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
++FemaleHealthForm.java++
package com.learn.ssm.pojo;
public class FemaleHealthForm extends HealthForm {
private String uterus;
public String getUterus() {
return uterus;
}
public void setUterus(String uterus) {
this.uterus = uterus;
}
}
++MaleHealthForm.java++
package com.learn.ssm.pojo;
public class MaleHealthForm {
private String prostate;
public String getProstate() {
return prostate;
}
public void setProstate(String prostate) {
this.prostate = prostate;
}
}
员工表,工牌表和任务表的POJO,以员工表作为核心:
++WorkCard.java++
package com.learn.ssm.pojo.params; public class WordCard { private Long id; private Long empId; private String realName; private String department; private String mobile; private String position; private String note; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getEmpId() { return empId; } public void setEmpId(Long empId) { this.empId = empId; } public String getRealName() { return realName; } public void setRealName(String realName) { this.realName = realName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
++Task.java++
package com.learn.ssm.pojo; public class Task { private Long id; private String title; private String context; private String note; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContext() { return context; } public void setContext(String context) { this.context = context; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
++EmployeeTask.java++
package com.learn.ssm.pojo; public class EmployeeTask { private Long id; private Long empId; private Task task = null; private String taskName; private String note; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getEmpId() { return empId; } public void setEmpId(Long empId) { this.empId = empId; } public Task getTask() { return task; } public void setTask(Task task) { this.task = task; } public String getTaskName() { return taskName; } public void setTaskName(String taskName) { this.taskName = taskName; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } }
++Employee.java++
package com.learn.ssm.pojo; import com.learn.ssm.chapter4.Enum.SexEnum; import java.util.Date; import java.util.List; public class Employee { private Long id; private String realName; private SexEnum sex = null; private Date birthday; private String mobile; private String email; private String position; private String note; private WordCard wordCard; private List<EmployeeTask> employeeTaskList = null; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getRealName() { return realName; } public void setRealName(String realName) { this.realName = realName; } public SexEnum getSex() { return sex; } public void setSex(SexEnum sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPosition() { return position; } public void setPosition(String position) { this.position = position; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } public WordCard getWordCard() { return wordCard; } public void setWordCard(WordCard wordCard) { this.wordCard = wordCard; } public List<EmployeeTask> getEmployeeTaskList() { return employeeTaskList; } public void setEmployeeTaskList(List<EmployeeTask> employeeTaskList) { this.employeeTaskList = employeeTaskList; } }
++FemaleEmployee.java++
package com.learn.ssm.pojo;
public class FemaleEmployee extends Employee {
private FemaleHealthForm femaleHealthForm = null;
}
++MaleEmployee.java++
package com.learn.ssm.pojo;
public class MaleEmployee extends Employee {
private MaleHealthForm maleHealthForm = null;
}
配置映射文件是级联的核心内容,Mapper不给出。
++TaskMapper.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.learn.ssm.mapper.TaskMapper">
<select id="getTask" parameterType="long" resultMap="com.learn.ssm.pojo.Task">
select id, title, context, note from t_task
where id = #{id}
</select>
</mapper>
++WordCardMapper.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.learn.ssm.mapper.WordCardMapper">
<select id="getWorkCardByEmpId" parameterType="long" resultType="com.learn.ssm.pojo.WordCard">
select id,emp_id as empId,real_name as realName, department, mobile, position
note
from t_work_card
where emp_id = #{empId}
</select>
</mapper>
z这样完成了两张映射文件。雇员任务表通过任务编号(task_id) 和任务表示关联,这是一个一对一的级联关系,使用association元素,雇员任务表一对一级联如下:
<?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.learn.ssm.mapper.EmployeeTaskMapper"> <resultMap type="com.learn.ssm.pojo.EmployeeTask" id="EmployeeTaskMap"> <id column="id" property="id"/> <result column="emp_id" property="empId"/> <result column="task_name" property="taskName"/> <result column="note" property="note"/> <association property="task" column="task_id" select="com.learn.ssm.mapper.TaskMapper.getTask"/> </resultMap> <select id="getEmployeeTaskByEmpId" resultMap="EmployeeTaskMap"> select id, emp_id, task_name, task_id, note from t_employee_task where emp_id = #{empID} </select> </mapper>
association代表着一对一级联的开始,property属性代表映射带POJO属性上。
体检表能拆分为男性和女性雇员,所以就有两个简单的映射器:
<?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.learn.ssm.mapper.MaleHealthFormMapper">
<select id="getMaleHealthForm" parameterType="long" resultType="com.learn.ssm.pojo.MaleHealthForm">
select id, heart, liver, spleen, lung, kidney, prostate, note
from t_male_health_form
where emp_id = #{id}
</select>
</mapper>
<?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.learn.ssm.mapper.FemaleHealthFormMapper">
<select id="getFemaleHealthForm" parameterType="long" resultType="com.learn.ssm.pojo.FemaleHealthForm">
select id, heart, liver, spleen, lung, kidney, uterus, note
from t_female_health_form
where emp_id = #{id}
</select>
</mapper>
下面代码创建雇员的映射关系,如代码:
<?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.learn.ssm.mapper.EmployeeMapper"> <resultMap id="employee" type="com.learn.ssm.pojo.Employee"> <id column="id" property="id"/> <result column="real_name" property="realName"/> <result column="sex" property="sex" typeHandler="com.learn.ssm.handler.SexEnumTypeHandler"/> <result column="birthday" property="birthday"/> <result column="mobile" property="mobile"/> <result column="email" property="email"/> <result column="position" property="position"/> <result column="note" property="note"/> <association property="wordCard" column="id" select="com.learn.ssm.mapper.WordCardMapper.getWordCardByEmpId"/> <collection property="employeeTaskList" column="id" select="com.learn.ssm.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId"/> <discriminator javaType="long" column="sex"> <case value="1" resultMap="maleHealthFormMapper"/> <case value="0" resultMap="femaleHealthFormMapper"/> </discriminator> </resultMap> <resultMap id="femaleHealthFormMapper" type="com.learn.ssm.pojo.FemaleEmployee" extends="employee"> <association property="femaleHealthForm" column="id" select="com.learn.ssm.mapper.FemaleHealthFormMapper.getFemaleHealthForm"/> </resultMap> <resultMap id="maleHealthFormMapper" type="com.learn.ssm.pojo.MaleHealthForm" extends="employee"> <association property="maleHealthForm" column="id" select="com.learn.ssm.mapper.MaleHealthFormMapper.getMaleHealthForm"/> </resultMap> <select id="getEmployee" parameterType="long" resultMap="employee"> select id, real_name as realName, sex, birthday, mobile, email,position, note from t_employee where id=#{id} </select> </mapper>
package com.learn.ssm.chapter5; import com.learn.ssm.mapper.EmployeeMapper; import com.learn.ssm.pojo.Employee; import com.learn.ssm.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; public class Main { public static void main(String[] args){ String resource = "com/learn/ssm/chapter5/resource/mybatis-config.xml"; SqlSession sqlSession = null; try{ sqlSession = SqlSessionFactoryUtil.openSqlSession(resource); sqlSession = SqlSessionFactoryUtil.openSqlSession(resource); EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class); Employee employee = employeeMapper.getEmployee(1L); // MaleHealthFormMapper mapper = sqlSession.getMapper(MaleHealthFormMapper.class); // mapper.getMaleHealthForm(1L); sqlSession.commit(); System.out.println(); }catch (Exception e){ e.printStackTrace(); }finally { if(sqlSession != null){ sqlSession.close(); } } } }
上面的级联已经成功,但是会引发性能问题:比如如果只想看到员工信息和员工任务信息,那么体验表和工牌的信息就是多余的,会让数据库多执行几条毫无疑义的SQL。
假设有N个关联关系完成了级联,那么只要再加入一个关联关系,就变成了N+1个级联,所有的级联SQL都会被执行,这样会造成浪费。Mybatis提供了延迟加载的技术
MyBatis的settings的配置有两个配置级联:
配置项 | 作用 | 配置选项说明 | 默认值 |
---|---|---|---|
lazyLoadingEnabled | 延迟架子啊的全局开关。当开启时,所有关联对象都会延迟加载,在特定的关联关系中,可通过设置fetchType来覆盖该项的开关状态 | true、false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会是带有延迟加载属性的对象完整加载;反之,则每种属性按需加载 | true、false | 版本3.4.1之前为true,之后为false |
调整配置文件mybatis-config.xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
运行代码,会发现先打印:
DEBUG 2019-05-21 20:20:10,507 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, real_name as realName, sex, birthday, mobile, email,position, note from t_employee where id=?
DEBUG 2019-05-21 20:20:10,598 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2019-05-21 20:20:10,683 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====> Preparing: select id, heart, liver, spleen, lung, kidney, prostate, note from t_male_health_form where emp_id = ?
DEBUG 2019-05-21 20:20:10,684 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====> Parameters: 1(Long)
DEBUG 2019-05-21 20:20:10,685 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==== Total: 1
DEBUG 2019-05-21 20:20:10,687 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
然后再打印:
DEBUG 2019-05-21 20:20:10,687 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, emp_id, task_name, task_id, note from t_employee_task where emp_id = ?
DEBUG 2019-05-21 20:20:10,687 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2019-05-21 20:20:10,688 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
DEBUG 2019-05-21 20:20:10,688 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id,emp_id as empId,real_name as realName, department, mobile, position note from t_work_card where emp_id = ?
DEBUG 2019-05-21 20:20:10,689 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2019-05-21 20:20:10,689 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
从而得知,agressiveLazyLoading配置是一个层级开关,当设置为true,就开启了层级开关的加载,下面修改为false之后,打印日志:
DEBUG 2019-05-21 20:22:03,113 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:22:03,116 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, real_name as realName, sex, birthday, mobile, email,position, note from t_employee where id=?
DEBUG 2019-05-21 20:22:03,170 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2019-05-21 20:22:03,255 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
可以看出,只有雇员记录被查询出来。
选项lazeLoadingEnabled决定是否开启延迟加载,而选项aggressiveLazeLoading则控制是否采用层级加载,但是他们全都是全局配置,并不能解决需要,但是可以使用fetchType进行处理,fetchType出现在级联元素(association,collection,discriminator没有这个属性可配置),存在两个值:
<collection property="employeeTaskList" column="id" select="com.learn.ssm.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId"
fetchType="eager"/>
进行打印,得到日志:
UG 2019-05-21 20:29:01,861 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, real_name as realName, sex, birthday, mobile, email,position, note from t_employee where id=?
DEBUG 2019-05-21 20:29:01,910 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2019-05-21 20:29:01,985 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====> Preparing: select id, emp_id, task_name, task_id, note from t_employee_task where emp_id = ?
DEBUG 2019-05-21 20:29:01,985 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ====> Parameters: 1(Integer)
DEBUG 2019-05-21 20:29:01,986 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==== Total: 0
DEBUG 2019-05-21 20:29:01,986 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
两个一对多,形成多对多。
一级缓存是在SqlSession上的缓存,二级缓存实在SqlSessionFactory上的缓存。默认情况下,也就是没有任何配置的情况下,Mybatis会开启一级缓存,也就是对于SqlSession层面的缓存,这个缓存不需要POJO对象可序列化
++***测试一级缓存:***++
package com.learn.ssm.chapter5; import com.learn.ssm.mapper.RoleMapper; import com.learn.ssm.pojo.Role; import com.learn.ssm.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; public class Main { public static void main(String[] args){ String resource = "com/learn/ssm/chapter5/resource/mybatis-config.xml"; Logger logger = Logger.getLogger(Main.class); SqlSession sqlSession = null; try{ sqlSession = SqlSessionFactoryUtil.openSqlSession(resource); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Role role = roleMapper.getRole(1); logger.info("再获取一次POJO....."); Role role2 = roleMapper.getRole(1); }catch (Exception e){ logger.info(e.getMessage(), e); e.printStackTrace(); }finally { if(sqlSession != null){ sqlSession.close(); } } } }
DEBUG 2019-05-21 20:36:47,785 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1935972447.
DEBUG 2019-05-21 20:36:47,786 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:36:47,791 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id,role_name as roleName,note from t_role where id=?
DEBUG 2019-05-21 20:36:47,830 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2019-05-21 20:36:47,879 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
INFO 2019-05-21 20:36:47,879 com.learn.ssm.chapter5.Main: 再获取一次POJO.....
DEBUG 2019-05-21 20:36:47,879 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:36:47,891 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:36:47,891 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 1935972447 to pool.
从日志上可以看出,虽然对同一对象进行了两次获取,但是实际上只有一条sql被执行,原因是代码使用了同一个SqlSession对象获取数据,当一个SqlSession第一个通过Sql和参数获取对象后,它会将其缓存起来,如果下次的Sql和参数都没有发生变化,并且缓存没有超市或者声明需要刷新时,那么他就会从缓存中获取数据.
package com.learn.ssm.chapter5; import com.learn.ssm.mapper.RoleMapper; import com.learn.ssm.pojo.Role; import com.learn.ssm.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; public class Main { public static void main(String[] args){ String resource = "com/learn/ssm/chapter5/resource/mybatis-config.xml"; Logger logger = Logger.getLogger(Main.class); SqlSession sqlSession = null; SqlSession sqlSession1 = null; try{ sqlSession = SqlSessionFactoryUtil.openSqlSession(resource); sqlSession1 = SqlSessionFactoryUtil.openSqlSession(resource); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Role role = roleMapper.getRole(1); sqlSession.commit(); logger.info("不同对象再获取一次POJO....."); RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class); roleMapper1.getRole(1); sqlSession1.commit(); }catch (Exception e){ logger.info(e.getMessage(), e); e.printStackTrace(); }finally { if(sqlSession != null){ sqlSession.close(); } } } }
DEBUG 2019-05-21 20:42:07,318 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:42:07,320 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id,role_name as roleName,note from t_role where id=?
DEBUG 2019-05-21 20:42:07,409 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2019-05-21 20:42:07,453 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
INFO 2019-05-21 20:42:07,454 com.learn.ssm.chapter5.Main: 不同对象再获取一次POJO.....
DEBUG 2019-05-21 20:42:07,454 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2019-05-21 20:42:07,461 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1210898719.
DEBUG 2019-05-21 20:42:07,462 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@482cd91f]
DEBUG 2019-05-21 20:42:07,462 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id,role_name as roleName,note from t_role where id=?
DEBUG 2019-05-21 20:42:07,464 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2019-05-21 20:42:07,465 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
DEBUG 2019-05-21 20:42:07,466 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:42:07,466 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7364985f]
DEBUG 2019-05-21 20:42:07,466 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 1935972447 to pool.
可以看出,Sql被执行的两次,这说明了一级缓存实在SqlSession层面上的,对于不同的SqlSession对象是不能共享的,开启二级缓存,只需要在映射文件(RoleMapper.xml)上加入代码
<cache/>
这个时候Mybatis会序列化和反序列化对应的POJO,也就要求POJO是一个可序列化对象,那么就需要实现java.io.Serializable接口。
配置好,运行,得到日志:
DEBUG 2019-05-21 20:45:23,898 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d49af10]
DEBUG 2019-05-21 20:45:23,902 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id,role_name as roleName,note from t_role where id=?
DEBUG 2019-05-21 20:45:23,972 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Integer)
DEBUG 2019-05-21 20:45:24,019 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 0
INFO 2019-05-21 20:45:24,033 com.learn.ssm.chapter5.Main: 不同对象再获取一次POJO.....
DEBUG 2019-05-21 20:45:24,038 org.apache.ibatis.cache.decorators.LoggingCache: Cache Hit Ratio [com.learn.ssm.mapper.RoleMapper]: 0.5
DEBUG 2019-05-21 20:45:24,038 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d49af10]
DEBUG 2019-05-21 20:45:24,039 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d49af10]
DEBUG 2019-05-21 20:45
属性 | 说明 | 取值 | 备注 |
---|---|---|---|
blocking | 是否使用阻塞性缓存,在读/写时他会加入JNI的锁进行操作 | true|false,默认值为false | 可保证读/写安全性,但加锁后性能不佳 |
readOnly | 缓存内容是否只读 | true|false,默认值为false | 如果为只读,则不会因为多个线程读/写造成不一致 |
eviction | 缓存策略,分为: LRU最近最少使用的:移除最长时间不被使用的对象 FIFO先进先出:按对象进入缓存的顺序来移除它们 SOFT软引用:移除基于垃圾回收器状态和软引用规则的对象 WEAK弱引用:更积极移除基于垃圾收集器状态和弱引用规则的对象 | 默认值是LRU | |
flushInterval | 以毫秒为单位,如果1分钟刷新一次,则配置60000,默认为null,没有刷新时间,时候执行update,delete,insert才进行刷新 | ||
type | 自定义缓存类,要求实现org.apache.ibatis.cache.Cache | ||
size | 缓存对象个数 | 正整数,默认1024 |
对于其他语句,也可以这样:
<select ... flushCache="false" userCache="true"/>
这是RoleMapper.xml的缓存配置,如果其他映射器也需要使用同样的配置,可以这样:
<cache-ref namespace=".....RoleMapper.xml"/>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。