JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板
Posted LRcoding
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板相关的知识,希望对你有一定的参考价值。
1. MyBatis入门
是一个基于 Java 的持久层框架,将接口和 Java的 POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录 【ORM】
1.1 认识框架
框架(Framework)是一个框子—约束性,也是一个架子—支撑性。框架类似于一个毛坯房,由开发人员在毛坯房基础上进行装修
使用框架可以减少开发时间、降低开发难度、保证设计质量,还可以起到约束,可以降低程序员之间沟通和日后维护的成本
1.2 ORM
JDBC缺点:需要手动的完成面向对象的Java语言和面向关系的数据库之间数据的转换(得到了一个结果集,将数据挨着取出来封装到一个对象中)
ORM(Object-Relational Mapping)对象关系映射:在关系型数据库和对象之间做一个映射,就可以像操作对象一样操作数据库了
持久化:将内存中的数据保存到关系型数据库中
持久层:专注于实现数据持久化应用领域(内存到数据库,数据库到内存等)的某个特定系统的一个逻辑层面
1.3 认识 MyBatis
MyBatis 是一个半自动 ORM框架(SQL语句由用户书写),其本质是对 JDBC的封装。使用 MyBatis 重点需要程序员编写 SQL,不需要写JDBC代码
Hibernate 是一个全自动的ORM框架(SQL语句、解析、执行全由Hibernate完成),不需要手写SQL,但是缺少了灵活性,SQL的调优也比较麻烦
2. MyBatis快速上手
2.1 搭建项目环境
项目总体框架:
-
引入 jar包(此处手动导入[从MyBatis官网下载]、后期使用maven自动导入即可)
-
准备配置文件 (mybatis-cfg.xml 官方源码配置文件的名字)----- 位置在 src 包下
<?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> <!-- 配置参数(log4j使用) --> <!-- 环境参数配置 --> <environments default="mysql"> <environment id="mysql"> <!-- 配置事务管理器 --> <transactionManager type="JDBC"></transactionManager> <!-- 数据源:连接池类型的 --> <dataSource type="POOLED"> <!-- name的值相当于是固定的 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 告诉 MyBatis 映射文件的位置(映射文件使用) --> </configuration>
注意
:- XML文件需要提供dtd或者xsd文件,来定义XML文件的标签结构
- 数据库四个连接参数的name属性的值来自
org.apache.ibatis.datasource.unpooled
包(mybatis的类包)下类UnpooledDataSource。
-
准备映射文件
<?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.lwclick.mapper.EmpMapper"> </mapper>
-
在 配置文件 mybatis-cfg.xml 中,指定映射文件的位置
<!-- 告诉 MyBatis 映射文件的位置 --> <mappers> <mapper resource="com/lwclick/mapper/EmpMapper.xml"></mapper> </mappers>
-
引入 log4j 进行功能测试(请使用最新版,避免漏洞)
-
在配置文件中,加入配置
<!-- 配置参数 --> <settings> <!-- 指定所使用的日志框架 --> <setting name="logImpl" value="LOG4J"/> </settings>
-
创建 log4j 的配置文件 ----- 位置在 src 包下
# 定义日志的全局级别 log4j.rootLogger=error,stdout,logfile # 设置日志的局部级别 # 如果访问该包下的资源,日志级别精确到 debug级 log4j.logger.com.lwclick.mapper=debug # 接口级别日志 # log4j.logger.com.lwclick.mapper.EmpMapper=debug # 方法级别日志 # log4j.logger.com.lwclick.mapper.EmpMapper.findBy log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=d:/sys.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%dyyyy-MM-dd HH:mm:ss %l %F %p %m%n
-
2.2 完成 select操作
首先需要先创建实体类 Employee:
public class Employee
private int empno;
private String ename;
private String job;
private Date hireDate;
private double sal;
// getter / setter / toString() / 有参,无参构造方法
环境搭建完成后,完成 select操作,只涉及到映射文件和测试类
2.2.1 查询所有操作
映射文件:
<!-- 返回值如果是 List,resultType 要写集合的元素的类型,也就是泛型类型 -->
<select id="selectAll" resultType="com.lwclick.entity.Employee">
SELECT * FROM emp
</select>
测试类(使用 JUnit 测试框架):
public class TestEmployee
// JUnit 的 @Test注解
@Test
public void testSelectAll() throws IOException
// 1. 创建 SqlSessionFactory
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
// 2. 使用 SqlSessionFactory创建一个 SqlSession
SqlSession sqlSession = factory.openSession();
// 3. 完成数据库操作并得到结果
List<Employee> list = sqlSession.selectList("com.lwclick.mapper.EmpMapper.selectAll"); // 映射文件的 namespace名 + SQL语句的 id
// 4. 关闭资源
sqlSession.close();
// 5. 输出结果
list.forEach(System.out::println);
常见异常:
- 配置文件中的mappers中没有指定映射文件的路径或者namespace+id写错了
- Cause: java.lang.IllegalArgumentException:Mapped Statements collection does not contain value for com.lwclick.mapper.EmpMapper.selectAll
- 缺少了ResultType或者ResultMap的指定
- Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.lwclick.mapper.EmpMapper.selectAll’. It’s likely that neither a Result Type nor a Result Map was specified.
2.2.2 通过id查询
映射文件:
<select id="selectById" parameterType="int" resultType="com.lwclick.entity.Employee">
SELECT * FROM emp WHERE empno = #param1
</select>
测试类:
@Test
public void testSelectById() throws IOException
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
// 7839为参数
Employee employee = sqlSession.selectOne("com.lwclick.mapper.EmpMapper.selectById", 7839);
sqlSession.close();
System.out.println(employee);
2.2.3 通过参数查询
映射文件:
<!-- 如果参数是一个 JavaBean,会根据参数拼接 【getter方法】 并调用 -->
<select id="selectEmpByParams" parameterType="com.lwclick.entity.Employee" resultType="com.lwclick.entity.Employee">
SELECT * FROM emp WHERE job = #job AND sal >= #sal
</select>
测试类:
@Test
public void testSelectEmpByParams() throws IOException
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
Employee emp = new Employee();
emp.setJob("CLERK");
emp.setSal(3000);
List<Employee> list = sqlSession.selectList("com.lwclick.mapper.EmpMapper.selectEmpByParams", emp);
sqlSession.close();
list.forEach((e) ->
System.out.println(e);
);
2.2.4 总结
映射文件中细节:
-
每个SQL语句的 id必须唯一
-
parameterType(参数类型),可以省略,MyBatis会自动推断类型; 如果提供,类型必须正确
取值只能是一个类型,所以后续都省略此参数
-
resultType(返回值类型)
- 返回值是集合,取值为集合的元素泛型类型的完整路径名
- 返回值是对象,取值为对应类的完整路径名
-
方法参数的传递
- 参数是基本类型(目前sqlSession这种方式),使用 #param1
- 参数是引用数据类型,使用 #属性名 接收数据,底层调用的是其getter方法。如果没有getter方法,就会直接找同名属性。
自动映射(Auto Mapping):
- 按照名称自动把结果集中的数据映射到对象属性中(如果某个属性没有 setter方法,那就直接找同名的属性)
- 如果名称不一样,最简单的是MySQL别名的方式, select sal as salary,保证与实体类中的属性名称一致
2.3 完成DML操作
2.3.1 insert 操作
注意事项
:
- DML操作的底层调用executeUpdate(),返回值都是int类型,无需指定
- 执行DML操作,默认事务手动提交,此时有两种解决方式
- 使用自动提交
手动执行 commit() 或者 rollback() 结束事务
【推荐】
映射文件:
<!-- DML操作,底层调用 executeUpdate(),返回值都是 int,无需指定 -->
<insert id="saveEmp">
INSERT INTO emp VALUES (null, #ename, #job, #hireDate, #sal)
</insert>
测试类:
@Test
public void testSaveEmp() throws IOException
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
// 自动事务提交 SqlSession sqlSession = factory.openSession(true);
SqlSession sqlSession = factory.openSession();
Employee emp = new Employee("zhangsan", "clerk", Date.valueOf("1999-12-03"), 3000);
int n = sqlSession.insert("com.lwclick.mapper.EmpMapper.saveEmp", emp);
// 手动提交事务!!!!!
sqlSession.commit();
sqlSession.close();
System.out.println(n);
2.3.2 update操作
映射文件:
<update id="updateEmp">
UPDATE emp SET job = #job, sal = #sal WHERE empno = #empno
</update>
测试类:
@Test
public void testUpdateEmp() throws IOException
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
Employee emp = new Employee();
emp.setJob("clerk");
emp.setSal(5000);
emp.setEmpno(9839);
int n = sqlSession.update("com.lwclick.mapper.EmpMapper.updateEmp", emp);
// 手动提交事务
sqlSession.commit();
sqlSession.close();
System.out.println(n);
2.3.3 delete操作
映射文件:
<delete id="deleteEmp">
DELETE FROM emp WHERE empno = #param1
</delete>
测试类:
@Test
public void testDeleteEmp() throws IOException
InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
int n = sqlSession.delete("com.lwclick.mapper.EmpMapper.deleteEmp", 7839);
// 手动提交事务
sqlSession.commit();
sqlSession.close();
System.out.println(n);
2.3.4 总结
其实一个SqlSession的update、delete、insert中任意一个方法均可完成所有DML操作。底层都是调用的update方法,就好比JDBC中的executeUpdate()可以完成DML操作一样。
3. MyBatis功能详解
3.1 关于 jar包
编号 | 元素列表 | 作用 |
---|---|---|
1 | mybatis-3.5.2.jar | Mybatis核心jar包(包含全部的功能) |
2 | ant-1.10.3.jar; ant-launcher-1.10.3.jar | 将软件编译、测试、部署等步骤联系在一起加以自动化的一个软件构建工具 |
3 | asm-7.0.jar | 代码生成,代码混淆,代码转换等等以字节码为操作目标的工作,一定程度上类似javac的功能 |
4 | cglib-3.2.10.jar | 实现动态代理的技术,延迟加载时使用 |
5 | javassist-3.24.1-GA.jar | 可用来检查、”动态”修改及创建 Java类。功能与JDK自带反射功能类似,但比反射功能更强大 |
6 | ognl-3.2.10.jar | 对象导航图语言的缩写,功能强大的表达式语言工具包。在动态SQL和$param中使用 |
7 | commons-logging-1.2.jar | 日志包 |
8 | slf4j-api-1.7.26.jar | 日志包 |
9 | slf4j-log4j12-1.7.26.jar | 日志包 |
10 | log4j-1.2.17.jar | 日志包 |
11 | log4j-api-2.11.2.jar | 日志包 |
12 | log4j-core-2.11.2.jar | 日志包 |
3.2 核心 API
- SqlSessionFactoryBuilder: 根据配置或者代码生成SqlSessionFactory(将各个工厂生产的东西组装起来),采用的是分步构建的构建者模式
- 当SqlSessionFactory创建后,该对象就没有存在的必要了
- SqlSessionFactory:生产SqlSession,使用的是工厂模式
- 在应用执行期间一直存在,需要一直生产SqlSession
- SqlSession:可获取Mapper的接口,也可以发送SQL语句并返回结果(相当于Connection)
- 每个线程都有自己的 SqlSession实例,是线程不安全的,不能被共享
- Mapper:由一个Java接口和XML文件(或者注解)构成,需要给出对应的SQL和映射规则,负责发送SQL去执行并返回结果
- 关闭了 SqlSession也就关闭了由其所产生的Mapper
3.3 关于配置文件
<?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>
<!-- 配置参数 -->
<settings>
<!-- 指定所使用的日志框架 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="mysql">
<environment id="mysql">
<!-- 配置事务管理器 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 数据源:连接池类型的 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 告诉 MyBatis 映射文件的位置 -->
<mappers>
<mapper resource="com/lwclick/mapper/EmpMapper.xml"></mapper>
</mappers>
</configuration>
mybatis-cfg.xml 配置文件中,配置项的顺序是有规定的,在 dtd文件中进行了规定
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
environment配置项中的配置参数:
-
transactionManager(事务管理器,有两种取值 JDBC | MANAGED)
- JDBC:简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的
连接
来管理事务范围。 - MANAGED:几乎没做什么,从来不提交或回滚一个连接。会让
容器(如Spring)
来管理事务的整个生命周期
- JDBC:简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的
-
dataSource(数据源,有三种取值 UNPOOLED | POOLED | JNDI)
-
UNPOOLED:每次请求都要打开连接,不用就关闭,未采用连接池
- driver、url、username、password
- defaultTransactionIsolationLevel(默认的连接事务隔离级别)
注意
:由上图可知,property 的name取值,必须是图中的属性名 -
POOLED:连接池方式(目前就采用这种)
- poolMaximumActiveConnections:在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
- poolMaximumIdleConnections:任意时间可能存在的空闲连接数
- 其他一些参数。。。
-
JNDI:使用 Spring这类容器时,容器可以集中或在外部配置数据源
-
mappers配置项的配置:
- mapper:告诉MyBatis 去哪找映射文件
- resource:资源文件的地址,使用 / 不是用 . ( resource=“com/lwclick/mapper/EmpMapper.xml” )
- url:全限定路径,资源文件在本地的绝对路径,以file开始
- class:接口的全限定地址,使用 . 不是用 / (class=“com.lwclick.mapper.EmpMapper”) — EmpMapper为接口
- package:包下的所有接口,不用再一个个的指定(必须是接口)
- name:包的名字( name=“com.lwclick.mapper” )
3.4 关于日志管理
MyBatis 的内置日志工厂(LogFactory)提供日志功能,内置日志工厂将日志交给以下某一个工具做代理(顺序查找)
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
- NO_LOGGING
在项目中把日志工具环境配置好之后,不用在MyBatis进行配置也可以让日志生效,最好还是使用 setting进行配置(防止使用默认的日志框架)
<settings>
<!-- 指定所使用的日志框架 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
其中 setting 的name的取值是固定的
4. 功能的完善
4.1 定义别名
4.1.1 单个文件配置
在 mybatis-cfg.xml 配置文件中,使用 typeAliases
定义别名
<!-- 定义类型的别名 -->
<typeAliases>
<typeAlias type="com.lwclick.entity.Employee" alias="employee"></typeAlias>
</typeAliases>
那么在映射文件的 resultType中,就可以直接使用 employee了
<sele以上是关于JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板的主要内容,如果未能解决你的问题,请参考以下文章
JavaLearn#(27)MyBatis进阶:Mapper代理(接口绑定)多参数传递模糊查询分页自增主键回填动态SQL一级缓存二级缓存
JavaLearn#(27)MyBatis进阶:Mapper代理(接口绑定)多参数传递模糊查询分页自增主键回填动态SQL一级缓存二级缓存
JavaLearn#(28)MyBatis高级:无级联查询级联查询(立即加载结果映射延迟加载)多表连接查询MyBatis注解MyBatis运行原理面试题
JavaLearn#(20)注解元注解模拟MyBatis注解JDK新特性数据库建模UML建模