1、MyBatis简介
1.1 什么是MyBatis
1.持久层框架;
2.几乎避免所有的JDBC代码和手动设置参数以及获取结果集的过程;
3.使用简单的XML或注解来配置和映射原生信息,将接口和Java的实体类映射为数据库中的记录;
4.MyBatis官方文档:https://mybatis.org/mybatis-3/zh/index.html
5.GitHUb:https://github.com/mybatis/mybatis-3
1.2 持久化
- 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
- 内存断电后数据会丢失;
- 内存过于昂贵;
1.3 持久层
- 完成持久化工作的代码层;
1.4 MyBatis的优点
- 简单易学;
- 灵活:不会对应用程序或者数据库的现有设计强加任何影响;SQL语句写在XML里,便于统一管理和优化;
- 解除SQL语句与程序代码的耦合:通过提供DAO层,将业务逻辑与数据访问逻辑分离。是系统的设计更加清晰,更易维护,更易单元测试;
- 提供XML标签,支持编写动态SQL;
2、使用MyBatis的步骤
2.1 搭建环境
- 进行数据库准备工作;
2.2 导入MyBatis
- 导入相关jar包;
2.3 编写代码
- 编写MyBatis核心配置文件;
- 编写MyBatis工具类,核心代码如下:
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //获取SqlSession连接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }
- 创建实体类,对应数据库表结构;
- 编写Mapper接口;
- 编写Mapper.xml;
2.4 测试
- 编写测试类;
3、CRUD操作
3.1 namespace
- XML中namespace为接口完整包名;
3.2 select、insert、update、delete
- 增删改操作需要提交事务;
- 编写模糊查询Like语句时,应在Java语句中添加通配符,而不是在SQL中拼接通配符,后者可能引起SQL注入;
4、配置解析
4.1 核心配置文件
- mybatis-config.xml 系统核心配置文件
- MyBatis 的配置文件包含了影响 MyBatis 行为的设置和属性信息。
- 能配置的内容如下:
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器) <!-- 注意元素节点的顺序!顺序不对会报错 -->
4.2 environments元素
1.可以配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定);
2.子元素节点:environment,具体的一套环境,通过设置id进行区别,id保证唯一!
3.子元素节点:数据源(dataSource)使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。数据源是必须配置的。有三种内建的数据源类型:
unpooled: 这个数据源的实现只是每次被请求时打开和关闭连接。 pooled: 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。 jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
- 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等…
4.3 mappers元素
- mappers:映射器,定义映射SQL语句文件,告诉 MyBatis 到哪里去找到这些语句;
- 引入资源方式:
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>
<!-- 使用映射器接口实现类的完全限定类名 需要配置文件名称和接口名称一致, 并且位于同一目录下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 但是需要配置文件名称和接口名称一致, 并且位于同一目录下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
3.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.kuang.mapper.UserMapper"> </mapper>
namespace和子元素的id联合保证唯一 , 区别不同的mapper;
namespace的命名必须跟某个接口同名;
接口中的方法与映射文件中sql语句id应该一一对应;
namespace命名规则 : 包名+类名;
4.4 Properties优化
- 在资源目录下新建一个db.properties:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8 username=root password=123456
2.将文件导入properties 配置文件:
<configuration> <!--导入properties文件--> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
4.5 typeAlias优化
- 类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>
4.6 settings
- 官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings
- 一个完整的settings元素的示例如下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
4.7 typeHandlers
官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#typeHandlers
4.8 objectFactory
官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#objectFactory
5、生命周期与作用域
5.1 MyBatis执行过程
5.2 作用域(scope)
1.SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
2.SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
3.由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
4.如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。所以 SqlSession 的最佳的作用域是请求或方法作用域
6、ResultMap
- 要解决的问题:属性名和字段名不一致
6.1 解决方案
- 为列名指定别名;
- 采用ResultMap:
<resultMap id="UserMap" type="User"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="name" property="name"/> <result column="pwd" property="password"/> </resultMap> <select id="selectUserById" resultMap="UserMap"> select id , name , pwd from user where id = #{id} </select>
7、日志
7.1 日志工厂
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
SLF4J、Apache Commons Logging、Log4j 2、Log4j、JDK logging
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序
查找)。 如果一个都未找到,日志功能就会被禁用。
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
7.2 Log4j
1.Log4j是Apache的一个开源项目;
2.通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件…
3.我们也可以控制每一条日志的输出格式;
4.通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
5.使用步骤:
a. 导入log4j的包:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
b. 配置文件编写:
将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
c. setting设置日志实现:
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
- d. 在程序中使用Log4j进行输出;
e. 测试;
8、分页
8.1 为什么需要分页?
如果查询大量数据的时候,使用分页进行查询,可以每次处理小部分数据,这样对数据库压力就在可控范围内。
8.2 使用Limit进行分页
- 修改Mapper文件:
<select id="selectUser" parameterType="map" resultType="user"> select * from user limit #{startIndex},#{pageSize} </select>
2.Mapper接口,参数为map:
//选择全部用户实现分页 List<User> selectUser(Map<String,Integer> map);
3.在测试类中传入参数测试,起始位置 = (当前页面 - 1 ) * 页面大小:
//分页查询 , 两个参数startIndex , pageSize @Test public void testSelectUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int currentPage = 1; //第几页 int pageSize = 2; //每页显示几个 Map<String,Integer> map = new HashMap<String,Integer>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize); List<User> users = mapper.selectUser(map); for (User user: users){ System.out.println(user); } session.close(); }
8.3 RowBounds分页
- mapper接口:
//选择全部用户RowBounds实现分页 List<User> getUserByRowBounds();
2.mapper文件:
<select id="getUserByRowBounds" resultType="user"> select * from user </select>
3.测试类,在这里,我们需要使用RowBounds类:
@Test public void testUserByRowBounds() { SqlSession session = MybatisUtils.getSession(); int currentPage = 2; //第几页 int pageSize = 2; //每页显示几个 RowBounds rowBounds = new RowBounds((currentPage- 1) * pageSize, pageSize); //通过session.**方法进行传递rowBounds,[此种方式现在已经不推荐使用了] List<User> users = session.selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null, rowBounds); for (User user: users){ System.out.println(user); } session.close(); }
8.4 PageHelper
9、使用注解开发
利用注解开发就不需要mapper.xml映射文件了。
9.1 使用步骤
- 我们在我们的接口中添加注解:
//查询全部用户 @Select("select id,name,pwd password from user") public List<User> getAllUser();
2.在mybatis的核心配置文件中注入:
<!--使用class绑定接口--> <mappers> <mapper class="com.kuang.mapper.UserMapper"/> </mappers>
3.我们去进行测试:
@Test public void testGetAllUser() { SqlSession session = MybatisUtils.getSession(); //本质上利用了jvm的动态代理机制 UserMapper mapper = session.getMapper(UserMapper.class); ● List<User> users = mapper.getAllUser(); for (User user : users){ System.out.println(user); } session.close(); }
9.2 本质上利用了jvm的动态代理机制
9.3 Mybatis详细的执行流程
9.4 使用注解增删改查
9.5 关于@Param
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
a. 在方法只接受一个参数的情况下,可以不使用@Param。
b. 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
c. 如果参数是 JavaBean , 则不能使用@Param。
d. 不使用@Param注解时,参数只能有一个,并且是Javabean。
9.6 #与$的区别
- “#{}“的作用主要是替换预编译语句(PrepareStatement)中的占位符”?” 【推荐使用】
INSERT INTO user (name) VALUES (#{name}); INSERT INTO user (name) VALUES (?);
2."${}"的作用是直接进行字符串替换,可能引起SQL注入
INSERT INTO user (name) VALUES ('${name}'); INSERT INTO user (name) VALUES ('Kuo-Teng');
10、一对多与多对一的处理
10.1 按查询嵌套处理;
10.2 按结果嵌套处理;
11、动态SQL
官方文档:https://mybatis.org/mybatis-3/zh/dynamic-sql.html
12、缓存
12.1 简介
1.什么是缓存 [ Cache ]?
存在内存中的临时数据。将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2.为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。
3.什么样的数据能使用缓存?
经常查询并且不经常改变的数据。
12.2 Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大地提升查询效率;
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存;
3.默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存);
4.二级缓存需要手动开启和配置,他是基于namespace级别的缓存;
5.为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存;
12.3 一级缓存
- 一级缓存也叫本地缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
12.4 一级缓存的失效
- 一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
- 一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
- a. sqlSession不同;
b. sqlSession相同,查询条件不同;
c. sqlSession相同,两次查询之间执行了增删改操作;
d. sqlSession相同,手动清除一级缓存;
12.5 二级缓存
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
a. 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
b. 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
c. 新的会话查询信息,就可以从二级缓存中获取内容;
d. 不同的mapper查出的数据会放在自己对应的缓存(map)中;
官方文档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
12.6 结论
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据;
- 查出的数据都会被默认先放在一级缓存中;
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中;
12.7 缓存原理