持久层技术
- Jdbc: 手动封装结果集,SQL语句硬编码在Java代码中
- Jdbi: 对Jdbc更高层次的抽象,使得数据库操作更便捷、高效,和 MyBatis 非常类似
- MyBatis: 自动封装结果集,SQL语句写在XML配置文件中(适用于对性能要求高而对数据库可移植性无特定要求)
- JPA: 不用编写SQL语句,可实现跨数据库(适用于对性能要求不高而对数据库可移植性有一定要求)
Jdbc
Jdbc编程
1 | /** |
Jdbc编程的问题
- 数据库连接:使用时就创建,不使用则立即释放,对数据库进行频繁连接的开启和关闭,造成数据库资源的浪费,影响数据库性能。
设想解决方案:使用数据库连接池管理数据库连接。 - SQL语句:SQL语句硬编码在Java代码中,不利于系统的维护。
设想解决方案:将SQL语句编写在XML配置文件中。 - 输入参数:SQL语句的输入参数硬编码在Java代码中,不利于系统的维护。
设想解决方案:将SQL语句的输入参数配置在XML文件中。 - 输出结果:遍历查询结果集数据时,存在硬编码,不利于系统的维护。
设想解决方案:将查询的结果集自动映射成Java对象。
MyBatis配置文件
1 |
|
DAO开发的两种方式
原始DAO开发
1 | public class UserDaoImpl implements UserDao { |
原始DAO开发的问题:(1)DAO接口实现类方法中存在大量模板方法:(2)调用sqlSession方法时statement的id存在硬编码。
Mapper代理开发
Mapper接口遵循以下开发规范,MyBatis就可以自动生成Mapper接口实现类的代理对象,开发人员无需自己编写接口实现类:
- 映射文件Mapper.xml中的namespace为Mapper接口的地址
- 映射文件Mapper.xml中statement的id与Mapper接口中的方法名一致
- 映射文件Mapper.xml中statement的parameterType所指定的输入参数类型与Mapper接口中方法参数的类型一致
- 映射文件Mapper.xml中statement的resultType所指定的输出参数类型与Mapper接口中方法返回值的类型一致
mybatis-config.xml配置文件
Mapper接口
1 | package org.mybatis.core.mapper; |
Mapper映射文件
1 |
|
输入映射
- ${}表示拼接符号,使用字符串拼接,直接参与sql编译,从而不能避免注入攻击。
- #{}表示占位符,使用PreparedStatement,sql语句会预编译在数据库系统中,不仅提高了安全性,还提高了执行效率。
- 如果输入的参数是基本数据类型,那么#{}中用于接收输入参数的变量名称可以任意。
问题:为什么使用PreparedStatement就能防止SQL注入?
原因:SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析、编译和优化,对应的执行计划也会缓存下来并允许数据库以参数化的形式进行查询,当运行时动态地把参数传给PreparedStatement时,即使参数里有敏感字符(如’ OR 1=1’),数据库也会把它作为参数字段的属性值来处理而不会作为一个SQL指令,这样就有效地防止了SQL注入。
输出映射
使用resultType进行输出映射时,只有查询出来的列名和映射的POJO属性名一致时才能映射成功;而如果查询的列名和映射的POJO属性名不一致,则可通过resultMap来设置列名和属性名之间的对应关系来完成映射。
动态SQL
MyBatis的强大特性之一便是它的动态SQL。如果你有使用JDBC或其他类似框架的经验,你就能体会到根据不同条件拼接SQL语句有多么痛苦。
查询缓存
- 缓存在查询时候生效,一般也需要在新增、修改或删除时更新
- Mybatis支持二级缓存机制,对应到不同的executor实现,BaseExecutor(普通的执行器, 包含一级缓存),CachingExecutor(二级缓存)
默认情况下,SELECT是使用缓存的,而INSERT/UPDATE/DELETE是不使用缓存的。
一级缓存
- Mybatis默认是启用一级缓存的,无需对框架进行任何设置。
- SqlSession是线程不安全的,所以在开发中我们一般将其与线程进行绑定或者将其声明在方法内部,目的是让多线程不共享SqlSession。SqlSessionTemplate就是将SqlSession和线程进行绑定,从而保证其线程安全。
- Mybatis的一级缓存生效的范围是SqlSession,是为了在SqlSession没有关闭时,业务需要重复查询相同数据使用的。一旦SqlSession关闭,则由这个SqlSession缓存的数据将会被清空。
- 如果SqlSession执行了commit(insert、delete、update)操作,则会清空SqlSession中的一级缓存,以保证缓存中保存的是最新信息,避免脏读。
1 | 4j |
二级缓存
- Mybatis的二级缓存是Mapper级别(按照namespace分)的,当多个SqlSession去操作同一个Mapper的sql语句时,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
- 每一个namespace的Mapper有一个二级缓存区域,如果两个Mapper的namespace相同,这两个Mapper执行的SQL查询到的数据将保存在相同的二级缓存区域中;当这个namespace中执行了非SELECT语句时,整个namespace中的缓存会被清空。
- 由于MyBatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到其他namespace中的语句对多表查询中涉及的表进行的修改,引发脏数据问题,所以Mybatis的二级缓存不适用于多表查询的情况。
- Mybatis默认是不开启二级缓存的,需要进行手动配置。
1 | 4j |
插件机制
MyBatis允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
1 | .class, method = "query", args = { ({ (type = Executor |
Spring整合MyBatis
1 |
|