一、Mybatis
1.1 简介
- 优秀的持久层框架
1.2 持久层
- 持久化:将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
二、第一个案例
2.1 导入依赖包与资源路径
2.1.1 导入依赖包
1 | <!-- mysql驱动 --> |
2.1.2 配置maven资源路径
- 在pom文件中添加以下内容,防止项目运行时,代码层的xml文件由于不在默认的resource资源目录中,导致访问失败而报错,父/子模块的pom都可以添加进去
1 | <!-- 配置资源导出,防止dao层中的xml无法访问而报错 --> |
2.2 配置Mybatis.xml文件
- 在resource下创建Mybatis.xml
1 | <!-- |
2.3 编写代码
2.3.1 编写工具类
1 | public class MybatisUtils { |
2.3.1 添加实体类
- pojo层
1 | public class User { |
2.3.2 编写接口
- 无需编写实现类,Mybatis会帮我们实现
1 | public interface UserDao { |
2.4 Mapper配置文件
- dao层下创建UserMapper.xml文件
- 注意这个mapper文件需要在mybatis-config进行配置,否则程序运行会因为找不到mapper文件而报错
1 | <!-- |
2.5 获取结果
1 | public class UserDaoTest { |
三、增删改查
3.1 namespace
- dao层命名统一为xxxMapper.xxx
3.2 Mapper配置文件解析
- namespace:表示dao层定义的接口路径
- id:表示接口中的方法名
- resultType:Sql语句执行的返回值类型
- parameterType:传入的参数类型
1 | <!--绑定命名空间,绑定自己的dao层接口--> |
3.3 CRUD
3.3.1 编写sql语句
1 | <mapper namespace="com.kuang.dao.UserMapper"> |
3.3.2 编写接口方法
- 在接口中定义对应方法
1 | public interface UserMapper { |
3.3.3 测试方法
1 | // 查询测试 |
四、Map和模糊查询
4.1 Map实现CRUD
4.1.1 优势
- 相对于原来的传pojo对象的方式,map方式更为传参更为灵活
4.1.2 编写sql语句
1 | <mapper namespace="com.kuang.dao.UserMapper"> |
4.1.3 编写接口方法
1 | public interface UserMapper { |
4.1.4 测试方法
1 |
|
4.2 模糊查询
4.2.1 编写sql语句
1 | <mapper namespace="com.kuang.dao.UserMapper"> |
五、环境配置
5.1 核心配置文件内容
- 配置标签的位置顺序依次往下,不能随便写在某个位置,否则会报错
- properties(属性)
- setting(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseldProvider(数据库厂商标识)
- mappers(映射器)
5.2 properties(属性)
- 新建properties配置文件
1 | driver=com.mysql.jdbc.Driver |
- 在核心配置文件中引入外部配置文件,如db.properties
- 需要注意的是,这个properties标签需要写在configuration标签下的第一个位置,因为configuration中的标签有顺序要求,否则会报错
1 | <!--也可以在properties中配置对应的属性,但默认会先从外部的配置文件开始读取--> |
5.3 typeAliases(类型别名)
5.3.1 自定义别名
- 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
- 设置完简化后的别命,在使用时可以直接使用,比如在sql语句配置文件的returnType就可以直接使用别名,使配置更加简洁清晰
- 当这样配置时,别名可以用在任何使用它的地方,这种方式一般在实体类较少的情况下使用
1 | <typeAliases> |
5.3.2 扫描包生成别名
- 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
- 这种可以在实体类比较多的时候使用
1 | <!-- |
5.3.3 注解别名
- 还可以使用注解设置别名
- 在使用扫描包生成别名时,如果非要改成自定义的,可以在对应类上加上注解
1 |
|
5.3.4 基本类型的别名
- 基本类型前加下划线:如int别名:_int
- 包装类型首字母小写:如Integer别名:int
5.4 setting(设置)
5.5 其他配置
5.6 mappers(映射器)
- 在核心配置文件中配置mapper配置文件所在路径
- 方式一:使用mapper文件绑定【推荐】
1 | <!-- 使用相对于类路径的资源引用 --> |
- 方式二:使用class绑定
1 | <!-- 使用映射器接口实现类的完全限定类名 --> |
- 使用除方式一外的其他方式时的注意点:
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一包下
六、生命周期和作用域
6.1 SqlSessionFactoryBuilder
- 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了
6.2 SqlSessionFactory
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 类似数据库连接池
6.3 SqlSession
- 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 连接到连接池的一个请求
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中
- 每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它
1 | try (SqlSession session = sqlSessionFactory.openSession()) { |
6.4 获取映射实例
1 | try (SqlSession session = sqlSessionFactory.openSession()) { |
七、ResultMap结果集映射
7.1 简介
- 解决实体类属性名和数据库表字段名不一致的问题
7.2 结果映射
1 | <!-- |
八、日志
8.1 日志工厂
- 如果一个数据库操作出现了异常,我们最好是能看到操作的sql语句是啥,方便我们排错
- 因此针对以上问题,我们可以使用日志的形式输出我们数据库操作的sql语句
- Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
8.2 日志配置
- 需要在核心配置文件中进行配置
- 配置完后直接运行即可,就可以在控制台输出日志信息
1 | <settings> |
8.3 Log4j
8.3.1 核心配置
1 | <settings> |
8.3.2 导入Log4j
1 | <!-- https://mvnrepository.com/artifact/log4j/log4j --> |
8.3.3 log4j配置文件
- 在resources下创建log4j.properties配置文件
- 这里面可以配置许多日志的输出格式
1 | ### 设置### |
8.3.4 Log4j的使用
直接运行就可以在控制台输出日志
也可以编写语句
1 | public class UserDaoTest { |
九、分页实现
9.1 Limit分页
1 | SELECT * FROM user LIMIT startIndex,pageSize; |
9.2 Mybatis分页
9.2.1 定义接口方法
1 | public interface UserMapper { |
9.2.1 mapper配置文件
1 | <select id="getUserByLimit" parameterType="map" resultType="com.kuang.pojo.User"> |
9.2.3 测试
1 |
|
十、注解开发
- 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心
- 本质:反射机制
- 底层:动态代理
1 | public interface UserMapper{ |
十一、Mybatis执行流程
十二、Lombok
十三、复杂查询处理
13.1 数据库环境
- 学生表:id、name、tid(外键)
- 教师表:id、name
- 一个教师教多个学生:一对多
- 多个学习选同一个老师:多对一
- 学生类
1 |
|
- 教师类
1 |
|
13.1 多对一处理
13.1.1 方式一:查询嵌套
1 | <!--绑定命名空间,绑定自己的dao层接口--> |
13.1.2 方式二:结果嵌套
- 连表查询,不想方式二分成两个查询语句
1 | <mapper namespace="com.kuang.dao.StudentMapper"> |
13.1.3 结果
- 以上两种方式的结果输出相同
13.2 一对多处理
13.2.1 方式一:查询嵌套
1 | <mapper namespace="com.kuang.dao.TeacherMapper"> |
13.2.2 方式二:结果嵌套
1 | <mapper namespace="com.kuang.dao.TeacherMapper"> |
13.2.3 结果
- 以上两种方式结果相同
13.3 小结
- 多对一:association(关联)
- 一对多:collection(集合),和上面都是在returnMap结果映射中使用
- javaType:指定实体类中属性的类型
- ofType:指定映射到List或集合中的pojo类型,泛型中约束的类型(一般在collection中会使用到)
- 在没有为pojo类起别名时,returnType直接使用类名会报错ClassNotFound,此时必须包的全类名
- colume属性对应的是select语句指定的导出表的列名,如果有列的别名,就写别名
- 以上两种方式都可实现效果,视情况而定实现方式
十四、动态Sql
14.1 简介
- 对sql语句添加条件判断,根据条件判断生成不同的sql语句
14.2 动态sql标签
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
- sql
14.3 if
- if表示如果查询的map中存在这个字段,就拼接if中的语句,否则不进行拼接
1 | <select id="findActiveBlogWithTitleLike" parameterType="map" |
这种语句会存在一种缺陷,它只是机械的语句拼接
下面这种情况就可能拼接出:SELECT FROM BLOG WHERE AND title like #{title}或者SELECT FROM BLOG WHERE
- 这就造成了sql语句的语法错误,无法完成查询,需要用到下面的where标签
- where标签可以自动的去除一些字段,比如多余的AND或者符号“,”
1 | <select id="findActiveBlogLike" |
14.4 choose
- choose类似于java中的switch,选择语句,满足其中一条则拼接对应的语句
- 如果都不满足则拼接otherwise中的语句
1 | <select id="findActiveBlogLike" |
14.5 trim(where、set)
14.5.1 where
- 将所有匹配条件放入到where标签中
- 解决了if标签的机械的语句拼接问题
1 | <select id="findActiveBlogLike" |
14.5.2 set
- 完成update的操作标签
- 也是用if进行条件判断拼接sql语句
1 | <update id="updateAuthorIfNecessary"> |
14.5.3 trim
- trim可以完成一些自定义的操作
- 比如自定where和set标签,意思就是trim可以替换where和set标签
1 | <trim prefix="WHERE" prefixOverrides="AND |OR "> |
14.6 foreach
- 这里是从POST表中查询需要的多条数据
- 这里的collection指定的是我们传入的列表,这里返回的就是列表中指定的id对应的数据
- 加入说list中的id列表为(1,2,3),那么就将满足这些id的数据全部获取到:(id=1,name=…)、(id=2,name=…)、(id=3,name=…)
1 | <select id="selectPostIn" resultType="domain.blog.Post"> |
14.7 sql
- 用于封装动态sql语句
- 使用include标签引入外部sql标签的id
1 | <sql id="if-title-author"> |
十五、缓存
15.1 缓存介绍
- 多次重复查询会消耗服务器连接数据库的资源
- 缓存:存放一次查询的结果,下次查询就不用查数据库了
- 使用缓存的情况:经常查询且不经常改变的数据
15.2 一级缓存
15.2.1 简介
- 一级缓存是默认开启的
- 只在一次SqlSession中有效,也就是SqlSession打开到关闭之间,进行查询就会存在缓存,下次查询相同数据就不会再执行一次sql语句,直接从缓存中抽取数据
15.2.2 缓存失效的情况
- 查询不同的内容
- 增删改操作会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存
15.3 二级缓存
15.3.1 简介
- 二级缓存也叫全局缓存,这是由于一级缓存的作用域太低,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没有了,现在需要的是,会话关闭时,一级缓存的数据可以保存到二级缓存中
- 新的会话查询,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
15.3.2 开启二级缓存
- 在核心配置文件添加以下标签,开启全局缓存
1 | <settings> |
- 配置完后还需要再需要使用二级缓存的Mapper配置文件下添加cache标签
- eviction为缓存策略
- flushInterval为缓存刷新时间
- size为最大数引用数
- readOnly为是否只读
1 | <mapper namespace="xxx"> |
- 使用cache标签,不添加缓存策略时,需要将实体类实现序列化,如下
1 | public class User implements Serializable{ |
15.3.3 小结
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中
15.4 缓存原理
- 每个SqlSession从开启到关闭都会启动一个一级缓存
- SqlSession关闭后一级缓存会升级为二级缓存
- 用户进行查询时会先看二级缓存是否存在查询的内容,再去一级缓存,缓存中没有要查找的对象时,就执行查询数据库的操作
15.5 自定义缓存
15.5.1 ehcache
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
15.5.2 引入依赖
1 | <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> |
15.5.3 cache配置
1 | <mapper namespace="xxx"> |