Mybatis笔记
Mybatis框架
第一章 初识Mybatis
1.1 框架概述
- 生活中“框架”
- 买房子【毛坯房】
- 手抓饼【半成品】
- 程序中框架【代码半成品】
- Mybatis:持久化层框架【dao层书写JDBC代码(BaseDao&JDBCUtils)】
- SpringMVC:控制层【表述层、表示层】
- Spring:全能【大管家】
1.2 Mybatis简介
- MyBatis是Apache的一个开源项目iBatis【Mybatis前身IBatis】
- Mybatis是一个半自动持久层ORM框架
- ORM:Object Relational Mapping【对象关系映射】
- 将Java中对象与数据库中表建立映射关系,优势:操作Java中对象,就可以影响表中数据。
- Mybatis与JDBC对比
- JDBC中的SQL与Java代码相耦合
- Mybatis将SQL与Java代码解耦
- Mybatis与Hibernate对比
- Mybatis是一个半自动化ORM持久化层框架【需要手写SQL】
- Hibernate是一个全自动化ORM持久化层框架【无需手写SQL】
- Java POJO(Plain Old Java Objects,普通老式 Java 对象)
- JavaWeb:JavaBean
- SSM:POJO
1.3 Mybatis官方网址
第二章 Mybatis框架搭建
2.1 搭建框架步骤
- 导入jar包
- 编写配置文件
- 使用框架核心类库
2.2 搭建Mybatis框架步骤
-
准备
- 建库建表
- 准备maven工程
-
搭建环境
-
导入jar包
<!--导入MyBatis的jar包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
编写核心配置文件
-
名称:mybatis-config.xml
-
位置:src/main/resources
-
示例代码
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 设置数据库连接环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db220212"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 加载映射文件路径--> <mappers> <mapper resource="mapper/EmployeeMapper.xml"/> </mappers> </configuration>
-
-
编写映射文件及相关接口【Dao->Mapper】
-
名称:接口名一致【XXXMapper.xml】
-
位置:src/main/resouces/mapper/
-
作用:为接口映射SQL语句,有以下三点注意事项
- 映射文件名称与接口名称一致
- 映射文件namespace与接口全类名一致
- 映射文件SQL的ID与接口中方法名一致
-
示例代码
<?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.atguigu.mapper.EmployeMapper"> <!-- 定义查询语句SQL 预期希望查询结果集,返回类型: --> <select id="selectEmpById" resultType="com.atguigu.pojo.Employee"> SELECT id, last_name lastName, email, salary FROM tbl_employee WHERE id = #{id} </select> </mapper>
-
-
测试【SqlSession】
- 先newSqlSessionFacotry工厂对象
- 再通过SqlSessionFactory获取SqlSession对象
- 通过SqlSession对象获取EmployeeMapper代理对象【实现类】
- 测试
-
2.3 搭建Mybatis框架环境常见问题
-
不能找到核心配置文件错误:java.io.IOException: Could not find resource mybati-config.xml
- 解决方案
- 检查文件名称及位置
- 使用maven的clean及compile重新编译
- 解决方案
-
不能找到映射文件错误:Caused by: java.io.IOException: Could not find resource mapper/EmployeeMaper.xml
- 解决方案:检查核心配置文件【mybatis-config.xml中mapper是否正确】
-
绑定异常,statement not found:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.atguigu.mapper.EmployeMapper.selectEmpById
- 解决方案:检查【三个一致】问题
第三章 Log4j日志框架
-
导入log4j的jar包
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
编写配置文件
-
名称:log4j.xml
-
位置:src/main/resources
-
示例代码
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <param name="Encoding" value="UTF-8" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="info" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>
-
第四章 Mybatis核心配置文件详解
4.1 概述
- MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息 ,配置文档的顶层结构如下:
4.2 核心配置文件详解
-
根标签:configuration,所有子标签均需要书写在根标签内部
-
子标签
-
properties属性标签
-
语法:
<properties resource="db.properties"></properties>
-
作用:定义property属性,也可以引入外部Properties属性文件。
-
示例代码
<properties resource="db.properties"></properties> <!-- 设置数据库连接环境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${db.driverClassName}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </dataSource> </environment> </environments>
-
-
settings子标签
- 作用:这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
- 属性
- mapUnderscoreToCamelCase:开启驼峰命名自动映射
- false:关闭【默认值】
- true:开启
- 注意:只能将a_col与aCol自动映射,不能将a_col与aCo自动映射
- mapUnderscoreToCamelCase:开启驼峰命名自动映射
-
typeAliases子标签
-
作用:类型别名可为 Java 类型设置一个缩写名字。【为POJO定义别名】
-
语法
<typeAliases> <!-- 为指定类设置别名--> <!-- <typeAlias type="com.atguigu.pojo.Employee" alias="employee"></typeAlias>--> <!-- 为当前包下所有类,设置别名【类名首字母小写作为别名】--> <package name="com.atguigu.pojo"/> </typeAliases>
-
Mybatis自定义别名
别名 类型 _int int _double double int或integer Integer list List map Map
-
-
environments子标签:环境配置
-
作用:设置多个数据库连接环境
-
语法
<!-- 设置数据库连接环境--> <environments default="development2"> <environment id="development"> <!-- 设置事务管理器--> <transactionManager type="JDBC"/> <!-- 设置连接池--> <dataSource type="POOLED"> <property name="driver" value="${db.driverClassName}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </dataSource> </environment> </environments>
-
-
mappers子标签:映射器
-
作用:设置映射文件路径
-
语法
<!-- 加载映射文件路径--> <mappers> <mapper resource="mapper/EmployeeMapper.xml"/> <!-- 老版本使用【要求:配置文件与接口必须在同一个包下】--> <!-- <package name="com.atguigu.mapper"/>--> </mappers>
-
-
-
总结:核心配置文件中子标签,是有顺序要求的,顺序参考官网文档即可!
第五章 Mybatis映射文件详解
5.1 映射文件概述
- MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。
- 如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。
- MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
5.2 映射文件标签详解
-
根标签mapper标签
-
语法
<mapper namespace="com.atguigu.mapper.EmployeMapper">
-
mapper中的namespace要求与接口的全类名一致
-
-
子标签
映射文件共有9个子标签,需要掌握如下8个子标签
- insert标签:定义添加SQL语句
- delete标签:定义删除SQL语句
- 删除共分为两种
- 物理删除:真实删除数据库中数据
- 逻辑删除:不删除数据库中数据【不显示】
- 删除共分为两种
- update标签:定义修改SQL语句
- select标签:定义查询SQL语句
- sql标签:定义SQL片段
- resultMap标签:自定义映射,resultType解决不了的问题交给resultMap
- cache标签:定义当前命名空间【名称空间】的缓存配置
- cache-ref标签:定义当前命名空间【名称空间】的缓存因为
5.3 子标签中常用属性
- resultType:设置期望结果集返回类型【类型全类名或别名】
- 注意
- 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。
- resultType 和 resultMap 之间只能同时使用一个。
- 注意
day03
第六章 获取主键自增及数据库受影响行数
6.1 获取主键自增数据【一般指的是id】
-
useGeneratedKeys:true
-
开启主键生成策略
-
keyProperty:设置生成数据存储对象属性
<insert id="insertEmp" keyProperty="id" useGeneratedKeys="true"> INSERT INTO tbl_employee(last_name,email,salary) VALUES(#{lastName},#{email},#{salary}) </insert>
6.2 获取数据库受影响行数
-
将接口返回值设置int或boolean即可
-
示例代码
/** * 添加员工信息 */ public boolean insertEmp(Employee employee); /** * 通过id修改员工其他信息 */ public int updateEmp(Employee employee);
第七章 Mybatis中参数传递问题【重点】
7.1 单个普通参数
-
Mybatis中传递单个参数时,是可以任意入参的
- 任意入参,指的是任意参数名及参数类型
-
示例代码
/** * 通过员工Id获取员工信息 */ public Employee selectEmpById(int empId);
<select id="selectEmpById" resultType="employee"> SELECT id, last_name, email, salary FROM tbl_employee WHERE id = #{empId} </select>
7.2 多个普通参数
-
Mybatis中传递多个普通参数时,不能任意入参,如任意使用会报如下错误
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'empId' not found. Available parameters are [arg1, arg0, param1, param2]
-
Mybatis中传递多个普通参数时,底层会将参数封装为一个Map,Map的key是【param1、param2、...】和
【arg0、arg1、...】
-
示例代码
/** * 通过员工id和姓名获取员工信息 * @param empId * @param lastName */ public Employee selectEmpByIdAndLastName(int empId,String lastName);
<select id="selectEmpByIdAndLastName" resultType="employee"> SELECT id, last_name, email, salary FROM tbl_employee WHERE id = #{arg0} AND last_name = #{arg1} </select>
7.3 POJO参数
-
Mybatis中POJO入参时,使用POJO中属性直接入参即可
-
示例代码
/** * 通过员工id和姓名获取员工信息 * @param employee */ public Employee selectEmpByOpr(Employee employee);
<select id="selectEmpByOpr" resultType="employee"> SELECT id, last_name, email, salary FROM tbl_employee WHERE id = #{id} AND last_name = #{lastName} </select>
7.4 命名参数
-
语法:@Param
-
Mybatis中使用@Param注解为参数命名,底层将参数封装为Map,key是命的名【命名参数支持param1,param2,...】
-
示例代码
/** * 通过员工id和姓名获取员工信息 */ public Employee selectEmpByNamedParam(@Param(value = "empId") int empId, @Param("lastName") String lastName);
<!-- 测试命名参数--> <select id="selectEmpByNamedParam" resultType="employee"> SELECT id, last_name, email, salary FROM tbl_employee WHERE id = #{empId} AND last_name = #{lastName} </select>
-
源码解析
public static class ParamMap<V> extends HashMap<String, V> {}
7.5 Map参数
-
Mybatis中可以支持Map入参,map的key就是参数key
-
示例代码
/** * 通过员工id和姓名获取员工信息 * @param map * @return */ public List<Employee> selectEmpByMap(Map<String,Object> map);
<select id="selectEmpByMap" resultType="employee"> SELECT id, last_name, email, salary FROM tbl_employee WHERE id = #{empId} AND last_name = #{lName} </select>
Map<String,Object> map = new HashMap<>(); map.put("empId",1); map.put("lName","cuicui"); List<Employee> employees = mapper.selectEmpByMap(map); for (Employee employee : employees) { System.out.println("employee = " + employee); }
7.6 List|Array|Collection等
- Mybatis支持List、Array、Collection等入参,key分别是【list,array,collection(list)】
第八章 #与$区别【面试题】
8.1 回顾JDBC底层对象
- DriverManager
- Connection
- Statement:执行SQL语句,入参使用SQL拼接【字符串拼接】方式
- PreparedStatement:执行SQL语句,入参使用预编译SQL方式【占位符】
- ResultSet
8.2 #{}与${}区别
-
{}:底层执行SQL语句,使用PreparedStatement,预编译SQL,防止SQL注入安全隐患,相对比较安全
- ${}:底层执行SQL语句,使用Statement,字符串拼接SQL,未防止SQL注入安全隐患,相对不安全
8.3 #与$使用场景
-
单表查询语句:select col,col2,... from table1 where col=? and col2=? group by ?,order by? limit ?,?
-
【#{}】使用场景:SQL语句中占位符(?)位置均可以使用#{}
-
【\({}】使用场景:#解决不了的问题时,使用\)
-
如:查询表中数据【动态化表名】
-
示例代码
/** * 动态查询表名 * @return */ public List<Employee> selectEmpByDynamicTable(@Param("tblName") String tblName);
<select id="selectEmpByDynamicTable" resultType="employee"> SELECT id,last_name,email,salary FROM ${tblName} </select>
-
第九章 Mybatis中返回结果四种情况
9.1 查询单行数据返回单个对象
/**
* 查询单行数据返回单个对象
*/
public Employee selectEmpById(int empId);
<select id="selectEmpById" resultType="employee">
select
id,
last_name,
email,
salary
from
tbl_employee
where
id = #{empId}
</select>
9.2 查询多行数据返回对象集合
/**
* 查询所有员工信息【查询多行数据返回对象集合】
*/
public List<Employee> selectAllEmps();
<select id="selectAllEmps" resultType="employee">
select
id,
last_name,
email,
salary
from
tbl_employee
</select>
9.3 查询单行数据返回Map
-
Mybatis底层返回结果:字段名作为Map的key,查询结果作为Map的value
-
示例代码
/** * 通过id获取员工信息【查询单行数据返回Map】 */ public Map<String,Object> selectEmpRetMap(int empId);
<select id="selectEmpRetMap" resultType="map"> select id, last_name, email, salary from tbl_employee where id = #{empId} </select>
9.4 查询多行数据返回Map
-
注意:Map<Integer,Employee>
- key:empId
- value:employee
- 必须使用@MapKey指定Map的key是Employee中的哪个属性
-
示例代码
/** * 查询所有员工信息【查询多行数据返回Map】 * map * key:empId * value:employee */ // public List<Map<String,Object>> selectAllEmpsReturnMap(); @MapKey("id") public Map<Integer,Employee> selectAllEmpsReturnMap();
<select id="selectAllEmpsReturnMap" resultType="map"> select id, last_name, email, salary from tbl_employee </select>
第十章 自动映射及自定义映射【重点】
10.1 自动映射与自定义映射
-
自动映射【resultType】
-
概述:指的是可以自动将类中的属性与表中字段进行关联映射
-
自动映射无法解决问题
-
多表连接查询时,需要返回多张表的结果集时,此时resultType解决不了
-
单表查询时,不支持驼峰式命名映射【不为字段定义别名】,此时resultType解决不了
-
-
-
自定义映射【resultMap】
- 概述:程序员自己定义字段与属性映射关系,称之为自定义映射【自动映射解决不了问题,交给自定义映射解决】
-
注意:自动映射与自定义映射只能同时使用一个
10.2 Mybatis中自定义映射【级联映射】
/**
* 通过员工Id获取员工信息及员工所属部门信息
*/
public Employee selectEmpAndDeptByEmpId(int empId);
<!-- 自定义映射-->
<resultMap id="empAndDeptResultMap" type="employee">
<!--id标签: 定义表中的【主键】字段与类中属性关联关系-->
<id property="id" column="id"></id>
<!--result标签: 定义表中的【非主键】字段与类中属性关联关系-->
<result property="lastName" column="last_name"></result>
<result property="email" column="email"></result>
<result property="salary" column="salary"></result>
<result property="dept.deptId" column="dept_id"></result>
<result property="dept.deptName" column="dept_name"></result>
</resultMap>
<select id="selectEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
SELECT
e.`id`,
e.`last_name`,
e.`email`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
e.`id` = #{empId}
</select>
10.3 Mybatis中自定义映射【association映射(一对一或多对一)】
/**
* 通过员工Id获取员工信息及员工所属部门信息【association映射】
*/
public Employee selectEmpAndDeptByEmpIdass(int empId);
<!-- association自定义映射-->
<resultMap id="empAndDeptResultMapAssociation" type="employee">
<id property="id" column="id"></id>
<result property="lastName" column="last_name"></result>
<result property="email" column="email"></result>
<result property="salary" column="salary"></result>
<!--association自定义映射-->
<association property="dept"
javaType="com.atguigu.pojo.Dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<select id="selectEmpAndDeptByEmpIdass" resultMap="empAndDeptResultMapAssociation">
SELECT
e.`id`,
e.`last_name`,
e.`email`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
e.`id` = #{empId}
</select>
- association自定义映射【分步查询】
- association自定义映射【延迟查询】
10.4 Mybatis中自定义映射【collection映射(一对多)】
/**
* 通过部门id获取部门信息,及部门所属员工信息
*/
public Dept selectDeptAndEmpByDeptId(int deptId);
<resultMap id="deptAndEmpResultMap" type="dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
<!-- 定义一对多关联映射-->
<collection property="empList"
ofType="com.atguigu.pojo.Employee">
<id property="id" column="id"></id>
<result property="lastName" column="last_name"></result>
<result property="email" column="email"></result>
<result property="salary" column="salary"></result>
</collection>
</resultMap>
<select id="selectDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
SELECT
e.`id`,
e.`last_name`,
e.`email`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
d.dept_id = #{deptId}
</select>
10.5 分步查询
-
为什么分步查询
- 提高程序性能
-
association分步查询
/** * association分步查询 * 通过员工Id获取员工信息及员工所属部门信息【多表连接查询】 1. 通过员工Id获取员工信息【dept_id】【单表】 2. 通过部门Id获取部门信息【单表】 */ public Employee selectEmpAndDeptByEmpIdAssociationStep(int empId);
<!-- association分步查询--> <resultMap id="empAndDeptRmAssociationStep" type="employee"> <id property="id" column="id"></id> <result property="lastName" column="last_name"></result> <result property="email" column="email"></result> <result property="salary" column="salary"></result> <!-- Association分步查询--> <association property="dept" select="com.atguigu.mapper.DeptMapper.selectDeptByDeptId" column="dept_id"> </association> </resultMap> <select id="selectEmpAndDeptByEmpIdAssociationStep" resultMap="empAndDeptRmAssociationStep"> select id, last_name, email, salary, dept_id from tbl_employee where id = #{empId} </select>
/** * 通过部门Id获取部门信息【单表】 */ public Dept selectDeptByDeptId(int deptId);
<select id="selectDeptByDeptId" resultType="dept"> select dept_id, dept_name from tbl_dept where dept_id=#{deptId} </select>
-
collection分步查询
/** * 通过部门id获取部门信息,及部门所属员工信息【分步查询】 1. 通过部门id获取部门信息【单表】 2. 通过部门id获取员工信息【单表】 */ public Dept selectDeptAndEmpByDeptIdStep(int deptId);
<!-- 定义collection分步查询--> <resultMap id="deptAndEmpRmStep" type="dept"> <id property="deptId" column="dept_id"></id> <result property="deptName" column="dept_name"></result> <collection property="empList" select="com.atguigu.mapper.EmployeeMapper.selectEmpByDeptId" column="dept_id" fetchType="lazy"> </collection> </resultMap> <select id="selectDeptAndEmpByDeptIdStep" resultMap="deptAndEmpRmStep"> select dept_id, dept_name from tbl_dept where dept_id=#{deptId} </select>
/** * 2. 通过部门id获取员工信息 */ public List<Employee> selectEmpByDeptId(int deptId);
<select id="selectEmpByDeptId" resultType="employee"> select id, last_name, email, salary, dept_id from tbl_employee where dept_id = #{deptId} </select>
10.6 resultMap标签详解
- resultMap属性
- id:定义resultMap唯一标识
- type:定义resultMap类型
- resultMap子标签
- id标签:定义表中的【主键】字段与类中属性关联关系
- result标签:定义表中的【非主键】字段与类中属性关联关系
- association标签:自定义映射
- 属性
- property:定义关联属性
- javaType:定义关联属性对应java类型或别名
- select:定义分步查询Sql的Id
- column:定义分步查询Sql的参数
- fetchType:局部延迟加载开关
- lazy:开启局部延迟加载
- eager:关闭局部延迟加载
- 子标签
- id标签:与resultMap的id子标签一致
- result标签:与resultMap的result子标签一致
- 属性
- collection标签
- 属性
- property:定义关联属性
- ofType:定义关联属性对应java类型或别名
- select:定义分步查询Sql的Id
- column:定义分步查询Sql的参数
- fetchType:局部延迟加载开关
- lazy:开启局部延迟加载
- eager:关闭局部延迟加载
- 属性
day04
第十一章 Mybatis延迟加载【懒加载】
11.1 延迟加载概述
-
前提:在分步查询基础上
-
延迟加载:使用数据时加载,不使用数据时不加载
- 使用数据,指的是第二步查询的数据
11.2 延迟加载语法
-
全局设置
<settings> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 设置加载的数据是按需加载【3.4.1及以前版本,默认值:true】--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
-
局部设置【fetchType】
-
属性位置
- association标签内部或collection标签内部
-
属性数值
- eager:关闭局部延迟加载
- lazy【默认值】:开启局部延迟加载
-
示例代码
<association property="dept" select="com.atguigu.mapper.DeptMapper.selectDeptByDeptId" column="dept_id" fetchType="eager"> </association> <collection property="empList" select="com.atguigu.mapper.EmployeeMapper.selectEmpByDeptId" column="dept_id" fetchType="lazy"> </collection>
-
11.3 扩展
- 如果分步查询时,需要多个参数,需要将多个参数封装为map结构即可
- 语法:
第十二章 Mybatis动态SQL【重点】
12.1 动态SQL概述
-
动态SQL:指的是SQL语句可动态变化
-
概述
/** * 通过条件查询员工信息【条件不确定】 private Integer id; //员工id private String lastName; //员工姓名 private String email; //员工邮箱 private Double salary; //员工薪资 Employee对象中属性不为空,就添加到查询条件中 id not null->sql: select id,last_name,email,salary from tbl_employee where id=#{id} lastName not null -> sql select id,last_name,email,salary from tbl_employee where last_name=#{lastName} */ public List<Employee> selectEmpByDynamicOpr(Employee employee);
-
Mybatis中不支持方法重载
- 因为:Mybatis规定方法名与SQL的Id一致【SQL的Id不能重复】导致了方法不能重载
-
Mybatis映射文件中注释
- 推荐使用:
- 不推荐使用:--
-
-
动态数据:使用Thymeleaf动态渲染html【静态页面】
-
Mybatis动态SQL中支持:OGNL
-
OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的
表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等
-
12.2 动态SQL常用标签
-
if标签:主要应用于基本if判断
-
where标签:解决where关键字及多出前面第一个and或or关键字问题
-
trim标签:可以在条件判断完的SQL语句前后添加或者去掉指定的字符
-
prefix: 添加前缀
-
prefixOverrides: 去掉前缀
-
suffix: 添加后缀
-
suffixOverrides: 去掉后缀
-
-
choose标签:主要是用于分支判断,类似于java中的if-else【switch case】,只会满足所有分支中的一个
-
set标签:解决set关键字及多出一个逗号【,】问题
-
foreach标签:类似java中for循环
-
collection: 要迭代的集合
-
item: 当前从集合中迭代出的元素
-
open: 开始字符
-
close:结束字符
-
separator: 元素与元素之间的分隔符
-
index:
-
迭代的是List集合: index表示的当前元素的下标
-
迭代的Map集合: index表示的当前元素的key
-
-
-
sql标签:定义SQL片段
12.3 示例代码
<!-- 通过条件查询员工信息【动态SQL】-->
<select id="selectEmpByDynamicOpr" resultType="employee">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
<where>
<!-- 1=1 -->
<if test="id != null">
id = #{id}
</if>
<if test="lastName != null">
and last_name = #{lastName}
</if>
<if test="email != null">
and email = #{email}
</if>
<if test="salary != null">
and salary = #{salary}
</if>
</where>
</select>
<!-- 测试trim-->
<select id="selectEmpByDynamicTrim" resultType="employee">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="lastName != null">
last_name = #{lastName} and
</if>
<if test="email != null">
email = #{email} and
</if>
<if test="salary != null">
salary = #{salary}
</if>
</trim>
</select>
<!-- 测试choose-->
<select id="selectEmpByDynamicChoose" resultType="employee">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
<where>
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name=#{lastName}
</when>
<when test="email!=null">
email=#{email}
</when>
<when test="salary!=null">
salary=#{salary}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
<update id="updateEmp">
update
tbl_employee
<set>
<if test="lastName != null">
last_name = #{lastName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="salary != null">
salary = #{salary}
</if>
</set>
where
id = #{id}
</update>
<select id="selectEmpsByIds" resultType="employee">
SELECT
id,
last_name,
email,
salary,
dept_id
FROM
tbl_employee
WHERE
id IN(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</select>
<insert id="batchInsertEmp">
INSERT INTO tbl_employee(last_name,email,salary) VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.salary})
</foreach>
</insert>
<sql id="col_emp">
id,
last_name,
email,
salary
</sql>
<sql id="select_tbl_emp">
select
<include refid="col_emp"></include>
from
tbl_employee
</sql>
<!-- 通过条件查询员工信息【动态SQL】-->
<select id="selectEmpByDynamicOpr" resultType="employee">
<include refid="select_tbl_emp"></include>
<where>
<!-- 1=1 -->
<if test="id != null">
id = #{id}
</if>
<if test="lastName != null">
and last_name = #{lastName}
</if>
<if test="email != null">
and email = #{email}
</if>
<if test="salary != null">
and salary = #{salary}
</if>
</where>
</select>
第十三章 Mybatis中缓存机制
13.1 缓存概述
- 生活中缓存
- 下载【缓存】音频、视频
- 节约数据流量
- 提高播放性能
- 下载【缓存】音频、视频
- 程序中缓存【Mybatis缓存】
- 使用缓存机制优势
- 提高查询效率
- 降低服务器压力
- 使用缓存机制优势
13.2 Mybatis中支持三种缓存
- 一级缓存
- 二级缓存
- 第三方缓存
13.3 Mybatis缓存之一级缓存
-
概述:一级缓存【也叫本地缓存(SqlSession级别缓存)】
-
特点
- 一级缓存不能被关闭,可以清空
- 一级默认开启状态
-
缓存原理
- 第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存【key:hashCode+查询的SqlId+编写的sql查询语句+参数(不重复)】
- 以后再次获取相同数据时,先从一级缓存中获取,如未获取到数据,再从数据库中获取数据。
-
一级缓存五种失效情况
-
不同的SqlSession对应不同的一级缓存
-
同一个SqlSession但是查询条件不同
-
同一个SqlSession两次查询期间执行了任何一次增删改操作
- 执行增删改操作后,默认清空一级缓存
- 同一个SqlSession两次查询期间手动清空了缓存
- sqlSession.clearCache();
- 同一个SqlSession两次查询期间提交了事务
- 提交事务,默认清空一级缓存
-
13.4 Mybatis中二级缓存
-
二级缓存概述:二级缓存【(second level cache),全局作用域缓存】,也叫SqlSessionFactory级别缓存
-
二级缓存特点:
- 二级缓存默认关闭,需要手动开启才能使用
- 二级缓存需要提交sqlSession或关闭sqlSession时,才会有效缓存
-
二级缓存,缓存机制
- 第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存,当提交sqlSession或关闭sqlSession时,将数据缓存至二级缓存
- 以后再次获取数据时,先从一级缓存中获取数据,如一级缓存中未获取到数据时,再从二级缓存中获取数据,如二级缓存中也未获取到数据,需要去数据库中获取数据,....
-
二级缓存使用步骤
① 全局配置【mybatis-config.xml】文件中开启二级缓存
② 需要使用二级缓存的映射文件处使用cache配置缓存
③ 注意:POJO需要实现Serializable接口
④ 关闭sqlSession或提交sqlSession时,将数据缓存到二级缓存
-
二级缓存相关属性
-
eviction:设置缓存回收策略
- LRU:最近最少使用的,先移除
- FIFO:先进先出,按照对象进入顺序,去移除对象。
-
flushInternal:设置刷新间隔,单位毫秒
-
size:设置缓存数量,正整数
-
readOnly:设置缓存为只读缓存【不能进行增删改操作】
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false"></cache>
-
-
二级缓存失效情况
- 两次查询期间执行了任何一次增删改操作,清空所有缓存
- sqlSession.clearCache()方法,只能清空一级缓存,无法清空二级缓存
13.5 第三方缓存插件【EhCache】
-
EhCache概述
- EhCache是缓存插件
- EhCache是一个纯Java进程内的缓存框架
-
EhCache使用步骤
-
导入jar包
<!-- mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.3</version> </dependency> <!-- slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.2</version> <scope>test</scope> </dependency>
-
编写配置文件【ehcache.xml】,位置:src/main/resources
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!-- 磁盘保存路径 --> <diskStore path="E:\mybatis\ehcache" /> <defaultCache maxElementsInMemory="1" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache> <!-- 属性说明: l diskStore:指定数据在磁盘中的存储位置。 l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 以下属性是必须的: l maxElementsInMemory - 在内存中缓存的element的最大数目 l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大 l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大 l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) -->
-
在映射文件中,加载第三方缓存插件【EhcacheCache】
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
-
-
注意事项
- 第三方缓存插件,需要开启二级缓存基础上,才能使用第三方缓存
- 设置二级缓存失效,第三方缓存同时失效
day05
第十四章 Mybatis逆向工程
14.1 逆向工程概述
- 逆向工程:数据库中表,会影响应用程序中的代码
- 【表->Java对象(POJO&EmpMapper&EmpMapper.xml)】
- 正向工程:应用程序中的代码,影响数据库中表的数据【Java对象->表】
14.2 MBG简介
- Mybatis Generator:简称MBG
- 是一个专门为MyBatis框架使用者定制的代码生成器
- 可以快速的根据表生成对应的映射文件,接口,以及POJO类
- 支持基本的增删改查,以及QBC风格的条件查询
- MBG只可以生成单表CRUD操作,多表连接查询、存储过程等复杂SQL定义,任然需要程序员手动编写。
14.3 MBG使用
-
导入jar包
<!-- mybatis-generator-core --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.6</version> </dependency>
-
编写核心配置文件
-
名称:【mbg.xml】
-
位置:src/main/resources
-
示例代码
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- id属性:设置一个唯一标识 targetRuntime属性值说明: MyBatis3Simple:基本的增删改查 MyBatis3:带条件查询的增删改查【QBC:Query By Criteria】 --> <context id="simple" targetRuntime="MyBatis3Simple"> <!--设置连接数据库的相关信息--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/db220212" userId="root" password="root"> </jdbcConnection> <!--设置JavaBean【POJO】的生成策略--> <javaModelGenerator targetPackage="com.atguigu.mybatis.mbg.pojo" targetProject="src/main/java"/> <!--设置SQL映射文件的生成策略--> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/> <!--设置Mapper接口的生成策略--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.mbg.mapper" targetProject="src/main/java"/> <!--逆向分析的表--> <table tableName="tbl_employee" domainObjectName="Employee"/> <table tableName="tbl_dept" domainObjectName="Department"/> </context> </generatorConfiguration>
-
-
执行代码生成器,进行测试
@Test public void testMBG() throws Exception{ List<String> warnings = new ArrayList<String>(); boolean overwrite = true; File configFile = new File("src/main/resources/mbg.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); }
14.4 MBG使用风格
-
普通风格:targetRuntime="MyBatis3Simple"
-
QBC风格:targetRuntime="MyBatis3"
第十五章 Mybatis分页插件【PageHelper】
15.1 为什么需要分页
- 提高用户体验度
- 降低服务器压力
15.2 设计分页Page类
自己实现分页思路
- 设计业务bean【Page】
- 47/60 【当前页码/总页数】
- pageNum:当前页码
- pages:总页数【计算得来的,总页数=总数据数量/每页显示数据数量】
- total:总数据数量
- pageSize:每页显示数据数量
- List
:当前页显示数据集合 - 设计业务层【service】,不足:不可重用
15.3 PageHelper概述
-
PageHelper是MyBatis中非常方便的第三方分页插件
-
官方文档:
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md
15.4 PageHelper基本使用
-
使用步骤
-
导入jar包
<!-- pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.0.0</version> </dependency>
-
在mybatis-config.xml中配置分页插件
<plugins> <!--配置PageHelper分页插件--> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
-
查询数据之前,使用PageHelper开启分页
-
PageHelper.startPage(pageNum,pageSize);
- pageNum:当前页码
- pageSize:每页显示数据数量
-
细节
- 分页实现有两条SQL
- 一条查询总数据数量
- 另一条查询当前页显示数据集合
- PageHelper.startPage(pageNum,pageSize)与SQL中的limit index,pageSize
- 开始下标【index】= (pageNum-1)*pageSize
- 分页实现有两条SQL
-
示例代码
//查询数据之前,使用PageHelper开启分页 Page<Object> page = PageHelper.startPage(3, 3); EmployeeExample ee = new EmployeeExample(); List<Employee> employees = mapper.selectByExample(ee); System.out.println(page.getPageNum()+"/"+page.getPages()); System.out.println("总数据数量:"+page.getTotal()); System.out.println("每页显示数据数量:"+page.getPageSize()); for (Object o : page.getResult()) { System.out.println("o = " + o); }
-
-
查询数据之后,将结果封装PageInfo中,使用PageInfo实现分页其他效果
-
PageInfo构造器共有三个,常用构造器如下
/** * 包装Page对象 * * @param list page结果 * @param navigatePages 页码数量[5] 共7页 [1] 2 3 4 5 1 [2] 3 4 5 1 2 [3] 4 5 2 3 [4] 5 6 3 4 [5] 6 7 3 4 5 [6] 7 3 4 5 6 [7] */ public PageInfo(List<T> list, int navigatePages) {}
//PageInfo业务bean基本使用 PageInfo<Employee> pageInfo = new PageInfo<>(employees,5); System.out.println(pageInfo.getPageNum()+"/"+pageInfo.getPages()); System.out.println("总数据数量:"+pageInfo.getTotal()); System.out.println("每页显示数据数量:"+pageInfo.getPageSize()); for (Employee employee : pageInfo.getList()) { System.out.println("employee = " + employee); } System.out.println("页码信息:"); int[] navigatepageNums = pageInfo.getNavigatepageNums(); for (int navigatepageNum : navigatepageNums) { System.out.print(navigatepageNum+" "); } System.out.println("是否有上一页:"+pageInfo.isHasPreviousPage()); System.out.println("上一页是:"+pageInfo.getPrePage()); System.out.println("是否有下一页:"+pageInfo.isHasNextPage()); System.out.println("下一页是:"+pageInfo.getNextPage()); System.out.println("是否是第一页:"+pageInfo.isIsFirstPage()); System.out.println("是否是最后一页:"+pageInfo.isIsLastPage()); System.out.println("导航页的第一个页码是:"+pageInfo.getNavigateFirstPage()); System.out.println("导航页的最后一个页码是:"+pageInfo.getNavigateLastPage()); System.out.println("导航页的总页码是:"+pageInfo.getNavigatePages());
-
-