MyBatis自学笔记
学习来源:B站
https://www.bilibili.com/video/BV1JP4y1Z73S?p=1&vd_source=07c8a1a7d89af39fe20c3a6894f5ff6a
资料来源:百度网盘
链接:https://pan.baidu.com/s/1tIx_hLeENVBP4a4F83C5gg?pwd=3k1d
提取码:3k1d
环境
一、MyBatis概述
1.1 框架
- 在⽂献中看到的framework被翻译为框架
- 框架其实就是对通⽤代码的封装,提前写好了⼀堆接⼝和类,我们可以在做项⽬的时候直接引⼊这些 接⼝和类(引⼊框架),基于这些现有的接⼝和类进⾏开发,可以⼤⼤提⾼开发效率。
- 框架⼀般都以jar包的形式存在。(jar包中有class⽂件以及各种配置⽂件等。)
- SSM三⼤框架的学习顺序:
- ⽅式⼀:MyBatis、Spring、SpringMVC(建议)
- ⽅式⼆:Spring、MyBatis、SpringMVC
1.2 三层架构
- 表现层(UI):直接跟前端打交互(⼀是接收前端ajax请求,⼆是返回json数据给前端)
- 业务逻辑层(BLL):⼀是处理表现层转发过来的前端请求(也就是具体业务),⼆是将从持久层获取的数据返回到表现层。
- 数据访问层(DAL):直接操作数据库完成CRUD,并将获得的数据返回到上⼀层(也就是业务逻辑层)。
- Java持久层框架:(常用)
- MyBatis
- Spring Data(实现了JPA规范)
1.3 JDBC不足
- SQL语句写死在Java程序中,不灵活。改SQL的话就要改Java代码。违背开闭原则OCP。
- 给?传值是繁琐的。能不能⾃动化???
- 将结果集封装成Java对象是繁琐的。能不能⾃动化???
1.4 了解MyBatis
-
MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
-
MyBatis在三层架构中负责持久层的,属于持久层框架。
-
ORM:对象关系映射
-
O(Object):Java虚拟机中的Java对象
-
R(Relational):关系型数据库
-
M(Mapping):将Java虚拟机中的Java对象映射到数据库表中⼀⾏记录,或是将数据库表中 ⼀⾏记录映射成Java虚拟机中的⼀个Java对象。
-
图示:
-
MyBatis属于半⾃动化ORM框架。
-
Hibernate属于全⾃动化的ORM框架。
-
-
MyBatis框架特点:
- ⽀持定制化 SQL、存储过程、基本映射以及⾼级映射
- 避免了⼏乎所有的 JDBC 代码中⼿动设置参数以及获取结果集
- ⽀持XML开发,也⽀持注解式开发。【为了保证sql语句的灵活,所以mybatis⼤部分是采⽤ XML⽅式开发。】
- 将接⼝和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的记录
- 体积⼩好学:两个jar包,两个XML配置⽂件。
- 完全做到sql解耦合。
- 提供了基本映射标签。
- 提供了⾼级映射标签。
- 提供了XML标签,⽀持动态SQL的编写。
- …
二、MyBatis入门程序
只要你会JDBC,MyBatis就可以学。
2.1 版本
软件:
- IntelliJ IDEA:2022.1.4
- Navicat for MySQL:16.0.14
- MySQL数据库:8.0.30
组件:
- MySQL驱动:8.0.30
- MyBatis:3.5.10
- JDK:Java17
- JUnit:4.13.2
- Logback:1.2.11
2.2 MyBatis下载
- 从github上下载,地址:https://github.com/mybatis/mybatis-3
- 将框架以及框架的源码都下载下来,下载框架后解压,打开mybatis⽬录
- 通过以上解压可以看到,框架⼀般都是以jar包的形式存在。我们的mybatis课程使⽤maven,所以这个jar我们不需要。
- 官⽅⼿册需要。
2.3 MyBatis入门程序开发步骤
- 数据库准备,使用Navicat for MySQL建表t_car插入数据
- 创建Project:建议创建Empty Project,设置Java版本以及编译版本等。
- 设置IDEA的maven
- 创建Module:普通的Maven Java模块
-
步骤1:打包⽅式:jar(不需要war,因为mybatis封装的是jdbc。)
<groupId>com.shanglinsong</groupId> <artifactId>mybatis-001-introduction</artifactId> <version>1.0-SNAPSHOT</version> <!--打包方式jar--> <packaging>jar</packaging>
-
步骤2:引⼊依赖(mybatis依赖 + mysql驱动依赖)
<!-- mybatis依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> <!-- mysql依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>
-
步骤3:在resources根⽬录下新建mybatis-config.xml配置⽂件(可以参考mybatis⼿册拷⻉)
<?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.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/powernode"/> <property name="username" value="root"/> <property name="password" value="******"/> </dataSource> </environment> </environments> <mappers> <!--执行XxxMapper.xml文件的路径--> <!--resource属性自动会从类的根路径下开始查找资源。--> <mapper resource="CarMapper.xml"/> </mappers> </configuration>
- 注意1:mybatis核⼼配置⽂件的⽂件名不⼀定是mybatis-config.xml,可以是其它名字。
- 注意2:mybatis核⼼配置⽂件存放的位置也可以随意。这⾥选择放在resources根下,相当于放到了类的根路径下。
-
步骤4:在resources根⽬录下新建CarMapper.xml配置⽂件(可以参考mybatis⼿册拷⻉)
<?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="sls"> <!--insert语句,id是这条sql语句的唯一标识,这个id就代表了这条SQL语句。--> <insert id="insertCar"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,'1003','丰田霸道',30.0,'2000-10-11','燃油车') </insert> </mapper>
-
注意1:sql语句最后结尾可以不写“;”
-
注意2:CarMapper.xml⽂件的名字不是固定的。可以使⽤其它名字。
-
注意3:CarMapper.xml⽂件的位置也是随意的。这⾥选择放在resources根下,相当于放到了类的根路 径下。
-
注意4:将CarMapper.xml⽂件路径配置到mybatis-config.xml:
<mappers> <!--执行XxxMapper.xml文件的路径--> <!--resource属性自动会从类的根路径下开始查找资源。--> <mapper resource="CarMapper.xml"/> </mappers>
-
-
步骤5:编写MyBatisIntroductionTest代码
package com.powernode.mybatis.test; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MyBatisIntroductionTest { public static void main(String[] args) throws IOException { // 获取SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取SqlSessionFactory对象 InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); // Resources.getResourceAsStream默认就是从类的根路径下开始查找资源。 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); // 一般情况下都是一个数据库对应一个SqlSessionFactory对象。 // 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行Sql语句 int count = sqlSession.insert("insertCar"); System.out.println("插入了 " + count + " 条记录"); // 手动提交 sqlSession.commit(); } }
- 注意1:默认采⽤的事务管理器是:JDBC。JDBC事务默认是不提交的,需要⼿动提交。
-
步骤6:运⾏程序,查看运⾏结果,以及数据库表中的数据
2.4 关于MyBatis核心配置文件的名字和路径详解
- 经过测试说明mybatis核⼼配置⽂件的名字是随意的,存放路径也是随意的。
- 虽然mybatis核⼼配置⽂件的名字不是固定的,但通常该⽂件的名字叫做:mybatis-config.xml
- 虽然mybatis核⼼配置⽂件的路径不是固定的,但通常该⽂件会存放到类路径当中,这样让项⽬的移植更 加健壮。
- 在mybatis中提供了⼀个类:Resources【org.apache.ibatis.io.Resources】,该类可以从类路径当 中获取资源,我们通常使⽤它来获取输⼊流InputStream,代码如下:
// 这种方式只能从类路径当中获取资源,也就是说mybatis-config.xml文件需要在类路径下
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
2.5 MyBatis第一个比较完整的代码写法
package com.powernode.mybatis.test;
/**
* 采用正规的方式,写一个完整的MyBatis程序
* @author BenShang
* @version 1.0
* @since 1.0
* */
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
public class MyBatisCompleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 1. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 2. 创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 3. 创建SqlSession对象,开启会话(底层会开启事务)
sqlSession = sqlSessionFactory.openSession();
// 4. 执行sql语句,处理相关业务
int count = sqlSession.insert("insertCar");
System.out.println(count);
// 5. 执行到这里,没有发生任何异常,提交事务,终止事务。
sqlSession.commit();
} catch (IOException e) {
// 发生异常最好回滚事务
if (sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
}finally {
// 6. 关闭会话(释放资源)
if (sqlSession != null) {
sqlSession.close();
}
}
}
}
2.6 引入JUnit
-
JUnit是专⻔做单元测试的组件。
- 在实际开发中,单元测试⼀般是由我们Java程序员来完成的。
- 我们要对我们⾃⼰写的每⼀个业务⽅法负责任,要保证每个业务⽅法在进⾏测试的时候都能通 过。
- 测试的过程中涉及到两个概念:
- 期望值
- 实际值
- 期望值和实际值相同表示测试通过,期望值和实际值不同则单元测试执⾏时会报错。
-
这⾥引⼊JUnit是为了代替main⽅法。
-
使⽤JUnit步骤:
- 第⼀步:引⼊依赖
<!-- junit依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>
-
第⼆步:编写单元测试类【测试⽤例】,测试⽤例中每⼀个测试⽅法上使⽤@Test注解进⾏标注。
-
测试⽤例的名字以及每个测试⽅法的定义都是有规范的:
- 测试⽤例的名字:XxxTest
- 测试⽅法声明格式:
public void test业务⽅法名(){}
// 测试用例 public class CarMapperTest{ // 测试方法 @Test public void testInsert(){} @Test public void testUpdate(){} }
-
-
第三步:可以在类上执⾏,也可以在⽅法上执⾏
- 在类上执⾏时,该类中所有的测试⽅法都会执⾏。
- 在⽅法上执⾏时,只执⾏当前的测试⽅法。
-
编写⼀个测试⽤例,来测试insertCar业务
package com.powernode.mybatis.test; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; public class CarMapperTest { @Test public void testInsertCar(){ SqlSession sqlSession = null; try { // 1. 创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 2. 创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); // 3. 创建SqlSession对象,开启会话(底层会开启事务) sqlSession = sqlSessionFactory.openSession(); // 4. 执行sql语句,处理相关业务 int count = sqlSession.insert("insertCar"); System.out.println(count); // 5. 执行到这里,没有发生任何异常,提交事务,终止事务。 sqlSession.commit(); } catch (IOException e) { // 发生异常最好回滚事务 if (sqlSession != null) { sqlSession.rollback(); } e.printStackTrace(); }finally { // 6. 关闭会话(释放资源) if (sqlSession != null) { sqlSession.close(); } } } }
2.7 引入日志框架logback
- 引⼊⽇志框架的⽬的是为了看清楚mybatis执⾏的具体sql。
- 启⽤标准⽇志组件,只需要在mybatis-config.xml⽂件中添加以下配置:【可参考mybatis⼿册】
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
标准⽇志也可以⽤,但是配置不够灵活,可以集成其他的⽇志组件,例如:log4j,logback等。
-
logback是⽬前⽇志框架中性能较好的,较流⾏的,所以我们选它。
-
引⼊logback的步骤:
- 第⼀步:引⼊logback相关依赖
<!-- 引入logback的依赖,这个日志框架实现了slf4j规范 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency>
- 第⼆步:引⼊logback相关配置⽂件(⽂件名叫做logback.xml或logback-test.xml,放到类路径当中)
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址-->
<property name="LOG_HOME" value="/home"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5
个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5
个字符宽度%msg:日志消息,%n是换⾏符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logge
r{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>100MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!--mybatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
- 再次执⾏单元测试⽅法testInsertCar,查看控制台是否有sql语句输出
2.8 MyBatis工具类SqlSessionUtil的封装
-
每⼀次获取SqlSession对象代码太繁琐,封装⼀个⼯具类
package com.powernode.mybatis.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; /** * MyBatis工具类 * @author Shanglinsong * @version 1.0 * @sine 1.0 */ public class SqlSessionUtil { // 工具类的构造方法一般都是私有化的。 // 工具类中所有的方法都是静态的,直接采用类名调用即可,不需要new对象。 // 为了防止new对象,构造方法私有化 private SqlSessionUtil() { } private static SqlSessionFactory sqlSessionFactory; // 类加载时执行 // SqlSessionUtil工具类在进行第一次加载的时候,解析mybatis-config.xml文件,创建SqlSessionFactory对象。 static{ try { sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); } catch (IOException e) { throw new RuntimeException(e); } } /*public static SqlSession openSession(){ SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库。 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlSession = sqlSessionFactory.openSession(); return sqlSession; }*/ /** * 获取SqlSession对象 * @return SqlSession */ public static SqlSession openSession(){ return sqlSessionFactory.openSession(); } }
-
测试⼯具类,将testInsertCar()改造
@Test public void testSqlSessionUtil(){ SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行sql语句 int count = sqlSession.insert("insertCar"); System.out.println("插入了" + count + "条记录"); // 提交 sqlSession.commit(); // 释放资源 sqlSession.close(); }
第一个MyBatis程序的Readme.txt笔记
开发我的第一个MyBatis程序
1. resources目录:
放在这个目录当中的,一般都是资源文件,配置文件。
直接放到resources目录下的资源,等同于放到了类
的根路径下。
2. 开发步骤
* 第一步:打包方式jar
* 第二步:引入依赖
- mybatis依赖
- mysql驱动依赖
* 第三步:编写mybatis核心配置文件:mybatis-config.xml
注意:
第一:这个文件名不是必须叫做mybatis-config.xml,可以用其他的名字。只是大家都采用这个名字。
第二:这个文件存放的位置也不是固定的,可以随意,但一般情况下,会放到类的根路径下。
mybatis-config.xml文件中的配置信息不理解没关系,先把连接数据库的信息修改以下即可。
其他的别动。
* 第四步:编写XxxxMapper.xml文件
在这个配置文件当中编写SQL语句。
这个文件名也不是固定的,放的位置也不是固定,我们这里给它起个名字,叫做:CarMapper.xml
把它暂时放到类的根路径下。
* 第五步:在mybatis-config.xml文件中指定XxxxMapper.xml文件的路径:
<mapper resource="CarMapper.xml"/>
注意:resource属性会自动从类的根路径下开始查找资源。
* 第六步:编写MyBatis程序。(使用mybatis的类库,编写mybatis程序,连接数据库,做增删改查就行了。)
在MyBatis当中,负责执行SQL语句的那个对象叫做什么呢?
SqlSession
SqlSession是专门用来执行SQL语句的,是一个Java程序和数据库之间的一次会话。
要想获取SqlSession对象,需要先获取SqlSessionFactory对象,通过SqlSessionFactory工厂来生产SqlSession对象。
怎么获取SqlSessionFactory对象呢?
需要首先获取SqlSessionFactoryBuilder对象。
通过SqlSessionFactoryBuilder对象的build方法,来获取一个SqlSessionFactory对象。
mybatis的核心对象包括:
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
SqlSessionFactoryBuilder --> SqlSessionFactory --> SqlSession
3. 从 XML 中构建 SqlSessionFactory
通过官方的这句话,你能想到什么呢?
第一:在MyBatis中一定是有一个很重要的对象,这个对象是:SqlSessionFactory对象。
第二:SqlSessionFactory对象的创建需要XML。
XML是什么?
它一定是一个配置文件。
4. mybatis中有两个主要的配置文件:
其中一个是:mybatis-config.xml,这是核心配置文件,主要配置连接数据库的信息等。(一个)
另一个是:XxxxMapper.xml,这个文件是专门用来编写SQL语句的配置文件。(一个表一个)
t_user表,一般会对应一个UserMapper.xml
t_student表,一般会对应一个StudentMapper.xml
5. 关于第一个程序的小细节
* mybatis中sql语句的结尾";"可以省略。
* Resources.getResourceAsStream
小技巧:以后凡是遇到resource这个单词,大部分情况下,这种加载资源的方式就是从类的根路径下开始加载。(开始查找)
优点:采用这种方式,从类路径当中加载资源,项目的移植性很强。项目从windows移植到linux,代码不需要修改,因为这个资源文件一直都在类路径当中。
* InputStream is = new FileInputStream("d:\\mybatis-config.xml");
采用这种方式也可以。
缺点:可移植性太差,程序不够健壮。可能会移植到其他的操作系统当中。导致以上路径无效,还需要修改java代码中的路径。这样违背了OCP原则。
* 已经验证了:
mybatis核心配置文件的名字,不一定是:mybatis-config.xml。可以是其它名字。
mybatis核心配置文件存放的路径,也不一定是在类的根路径下。可以放到其它位置。但为了项目的移植性,健壮性,最好将这个配置文件放到类路径下面。
* InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
ClassLoader.getSystemClassLoader() 获取系统的类加载器。
系统类加载器有一个方法叫做:getResourceAsStream
它就是从类路径当中加载资源的。
通过源代码分析发现:
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
底层的源代码其实就是:
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("mybatis-config.xml");
* CarMapper.xml文件的名字是固定的吗?CarMapper.xml文件的路径是固定的吗?
都不是固定的。
<mapper resource="CarMapper.xml"/> resource属性:这种方式是从类路径当中加载资源。
<mapper url="file:///d:/CarMapper.xml"/> url属性:这种方式是从绝对路径当中加载资源。
6. 关于mybatis的事务管理机制。(深度剖析)
* 在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理
<transactionManager type="JDBC"/>
* type属性的值包括两个:
JDBC(jdbc)
MANAGED(managed)
type后面的值,只有以上两个值可选,不区分大小写。
* 在mybatis中提供了两种事务管理机制:
第一种:JDBC事务管理器
第二种:MANAGED事务管理器
* JDBC事务管理器:
mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:
conn.setAutoCommit(false); 开启事务。
....业务处理...
conn.commit(); 手动提交事务
使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。
如果你编写的代码是下面的代码:
SqlSession sqlSession = sqlSessionFactory.openSession(true);
表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);
在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。
如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。
* MANAGED事务管理器:
mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。
我不管事务了,你来负责吧。
对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED
那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。
没有人管理事务就是没有事务。
* JDBC中的事务:
如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。
* 重点:
以后注意了,只要你的autoCommit是true,就表示没有开启事务。
只有你的autoCommit是false的时候,就表示开启了事务。
7. 关于mybatis集成日志组件。让我们调试起来更加方便。
* mybatis常见的集成的日志组件有哪些呢?
SLF4J(沙拉风):沙拉风是一个日志标准,其中有一个框架叫做logback,它实现了沙拉风规范。
LOG4J
LOG4J2
STDOUT_LOGGING
....
注意:log4j log4j2 logback都是同一个作者开发的。
* 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准。
只要开启即可。怎么开启呢?在mybatis-config.xml文件中使用settings标签进行配置开启。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
这个标签在编写的时候要注意,它应该出现在environments标签之前。注意顺序。当然,不需要记忆这个顺序。
因为有dtd文件进行约束呢。我们只要参考dtd约束即可。
这种实现也是可以的,可以看到一些信息,比如:连接对象什么时候创建,什么时候关闭,sql语句是怎样的。
但是没有详细的日期,线程名字,等。如果你想使用更加丰富的配置,可以集成第三方的log组件。
* 集成logback日志框架。
logback日志框架实现了slf4j标准。(沙拉风:日志门面。日志标准。)
第一步:引入logback的依赖。
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
第二步:引入logback所必须的xml配置文件。
这个配置文件的名字必须叫做:logback.xml或者logback-test.xml,不能是其它的名字。
这个配置文件必须放到类的根路径下。不能是其他位置。
主要配置日志输出相关的级别以及日志具体的格式。
三、使用MyBatis完成CRUD
- 准备⼯作
- 创建module(Maven的普通Java模块):mybatis-002-crud
- pom.xml
- 打包⽅式jar
- 依赖:
- mybatis依赖
- mysql驱动依赖
- junit依赖
- logback依赖
- mybatis-config.xml放在类的根路径下
- CarMapper.xml放在类的根路径下
- logback.xml放在类的根路径下
- 提供com.powernode.mybatis.utils.SqlSessionUtil⼯具类
- 创建测试⽤例:com.powernode.mybatis.CarMapperTest
3.1 insert(Create, 增)
-
SQL语句中的值不应该写死,值应该是⽤户提供的。
-
在MyBatis中可以这样做:
- 在Java程序中,将数据放到Map集合中
- 在sql语句中使⽤ #{map集合的key} 来完成传值,#{} 等同于JDBC中的 ? ,#{}就是占位符
-
利用Map集合进行insert
- java程序
@Test public void testInsertCarByMap(){ SqlSession sqlSession = SqlSessionUtil.openSession(); // 前端传过来数据了。 // 这个对象我们先使用Map集合进行数据的封装。 HashMap<String, Object> map = new HashMap<>(); map.put("carNum", "1111"); map.put("brand", "比亚迪汉2"); map.put("guidePrice", 10.0); map.put("produceTime", "2010-11-11"); map.put("carType", "电车"); // 执行SQL语句 // insert方法的参数: // 第一个参数:sqlId,从CarMapper.xml文件中复制。 // 第二个参数:封装数据的对象。 int count = sqlSession.insert("insertCar", map); System.out.println("插入了几条数据:" + count); sqlSession.commit(); sqlSession.close(); }
- mapper文档:
<insert id="insertCar"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
- 注意:
- #{} 的⾥⾯必须填写map集合的key,不能随便写。
- 如果采⽤map集合传参,#{} 里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL。
-
使⽤pojo(简单普通的java对象)完成进行insert
- 第⼀步:定义⼀个pojo类Car,提供相关属性。
package com.powernode.mybatis.pojo; /** * 封装汽车的相关信息的pojo类,普通的java类 * @author ShiningSong * @version 1.0 * @since 1.0 */ public class Car { // 数据库表中的字段应该和pojo类的属性一一对应。 // 建议使用包装类,这样可以防止null的问题。 private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String carType; public Car() { } public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) { this.id = id; this.carNum = carNum; this.brand = brand; this.guidePrice = guidePrice; this.produceTime = produceTime; this.carType = carType; } @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}'; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCarNum() { return carNum; } public void setCarNum(String carNum) { this.carNum = carNum; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getGuidePrice() { return guidePrice; } public void setGuidePrice(Double guidePrice) { this.guidePrice = guidePrice; } public String getProduceTime() { return produceTime; } public void setProduceTime(String produceTime) { this.produceTime = produceTime; } public String getCarType() { return carType; } public void setCarType(String carType) { this.carType = carType; } }
- 第二步:java程序
@Test public void testInsertCarByPOJO(){ SqlSession sqlSession = SqlSessionUtil.openSession(); // 封装数据 Car car = new Car(null, "3333", "比亚提秦", 30.0, "2020-11-11", "新能源"); // 执行sql int count = sqlSession.insert("insertCar", car); System.out.println("插入了几条数据:" + count); sqlSession.commit(); sqlSession.close(); }
- 第三步:sql语句
<insert id="insertCar"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
-
注意
- 如果采⽤POJO传参,#{} ⾥写的是get⽅法的⽅法名去掉get之后将剩下的单词⾸字⺟变⼩写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get⽅法不存在会报错。
- 注意:其实传参数的时候有⼀个属性parameterType,这个属性⽤来指定传参的数据类型,不过这个属性是可以省略的
<!-- pojo类 --> <insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert> <!-- Map --> <insert id="insertCar" parameterType="java.util.Map"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
3.2 delete(Delete, 删)
- 需求:根据id进⾏删除。
- mapper文件:
<delete id="deleteById">
delete from t_car where id=#{carNum}
</delete>
- java程序:
@Test
public void testDeleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行sql语句
int count = sqlSession.delete("deleteById", 16);
System.out.println("删除了几条记录:" + count);
sqlSession.commit();
sqlSession.close();
}
- 注意:当占位符只有⼀个的时候,#{} ⾥⾯的内容可以随便写。
3.3 update(Update, 改)
- 需求:修改id=9的Car信息,car_num为102,brand为⽐亚迪汉,guide_price为30.23,produce_time 为2018-09-10,car_type为电⻋
- mapper文件:
<update id="updateById">
update t_car
set car_num=#{carNum}, brand=#{brand}, guide_price=#{guidePrice},
produce_time=#{produceTime}, car_type=#{carType}
where id=#{id}
</update>
- java程序:
@Test
public void testUpdateById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 更新车辆信息
Car car = new Car(9L, "102", "比亚迪汉", 30.23, "2018-09-10", "电车");
// 执行sql语句
int count = sqlSession.update("updateById", car);
System.out.println("更新了几条数据:" + count);
sqlSession.commit();
sqlSession.close();
}
- 使⽤map传数据也是可以的。
3.4 select(Retrieve, 查)
select语句和其它语句不同的是:查询会有⼀个结果集。
-
单条查询
- 需求:查询id为1的Car信息
- mapper文件:
<select id="selectById" resultType="com.powernode.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id=#{id} </select>
-
java程序:
@Test public void testSelectById(){ SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行sql语句 Object car = sqlSession.selectOne("selectById", 1); // 输出结果 System.out.println(car); sqlSession.commit(); sqlSession.close(); }
-
注意:
- 在
<select>
标签中添加resultType属性,⽤来指定查询要转换的类型,不能省略,否则会报错 - 通过测试得知,如果当查询结果的字段名和java类的属性名对应不上的话,可以采⽤as关键字起别名,否则该属性查询结果会为null值
- 在
-
多条查询
- 需求:查询所有的Car信息。
- mapper文件:
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car </select>
- java程序:
@Test public void testSelectAll(){ SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行sql语句 List<Object> cars = sqlSession.selectList("selectAll"); // 输出结果 cars.forEach(car -> System.out.println(car)); sqlSession.commit(); sqlSession.close(); }
3.5 关于SQL Mapper的namespace
-
在SQL Mapper配置⽂件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是 为了防⽌SQL语句的ID冲突的。
-
创建UserMapper.xml⽂件,代码如下:
<?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="car2">
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select id, car_num as carNum, brand, guide_price as guidePrice,
produce_time as produceTime, car_type as carType
from t_car
</select>
</mapper>
- 不难看出,CarMapper.xml和UserMapper.xml⽂件中都有 id=“selectCarAll”
- 将CarMapper2.xml配置到mybatis-config.xml⽂件中。
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="UserMapper.xml"/>
</mappers>
- java程序:
@Test
public void testNamespace(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行sql语句
// List<Object> cars = sqlSession.selectList("selectAll");
// 正确完整的写法:namespace.id
List<Object> cars = sqlSession.selectList("car2.selectAll");
// 输出结果
cars.forEach(car -> System.out.println(car));
sqlSession.commit();
sqlSession.close();
}
总结笔记
使用mybatis完成CRUD
1. 什么是CRUD
C: Create增
R: Retrieve查(检索)
U: Update改
D: Delete删
2. insert
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,'1003','丰田霸道',30.0,'2000-10-11','燃油车');
</insert>
这样写的问题是?
值 显然是写死到配置文件中的。
这个在实际开发中是不存在的。
一定是前端的form表单提交过来数据。然后将值传给sql语句。
例如:JDBC的代码是怎么写的?
String sql = "insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)";
ps.setString(1, xxx);
ps.setString(2, yyy);
....
在JDBC当中占位符采用的是?,在mybatis当中是什么呢?
和?等效的写法是:#{}
在mybatis当中不能使用?占位符,必须使用 #{} 来代替JDBC当中的 ?
#{} 和 JDBC当中的 ? 是等效的。
java程序中使用Map可以给SQL语句的占位符传值:
Map<String, Object> map = new HashMap<>();
map.put("k1", "1111");
map.put("k2", "比亚迪汉");
map.put("k3", 10.0);
map.put("k4", "2020-11-11");
map.put("k5", "电车");
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{k1},#{k2},#{k3},#{k4},#{k5});
注意:#{这里写什么?写map集合的key,如果key不存在,获取的是null}
一般map集合的key起名的时候要见名知意。
map.put("carNum", "1111");
map.put("brand", "比亚迪汉2");
map.put("guidePrice", 10.0);
map.put("produceTime", "2020-11-11");
map.put("carType", "电车");
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType});
java程序中使用POJO类给SQL语句的占位符传值:
Car car = new Car(null, "3333", "比亚迪秦", 30.0, "2020-11-11", "新能源");
注意:占位符#{},大括号里面写:pojo类的属性名
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
把SQL语句写成这个德行:
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,#{xyz},#{brand},#{guidePrice},#{produceTime},#{carType})
出现了什么问题呢?
There is no getter for property named 'xyz' in 'class com.powernode.mybatis.pojo.Car'
mybatis去找:Car类中的getXyz()方法去了。没找到。报错了。
怎么解决的?
可以在Car类中提供一个getXyz()方法。这样问题就解决了。
通过这个测试,得出一个结论:
严格意义上来说:如果使用POJO对象传递值的话,#{}这个大括号中到底写什么?
写的是get方法的方法名去掉get,然后将剩下的单词首字母小写,然后放进去。
例如:getUsername() --> #{username}
例如:getEmail() --> #{email}
....
也就是说mybatis在底层给?传值的时候,先要获取值,怎么获取的?
调用了pojo对象的get方法。例如:car.getCarNum(),car.getCarType(),car.getBrand()
3. delete
* 需求:根据id删除数据
将id=59的数据删除。
实现:
int count = sqlSession.delete("deleteById", 59);
<delete id="deleteById">
delete from t_car where id = #{fdsfd}
</delete>
注意:如果占位符只有一个,那么#{}的大括号里可以随意。但是最好见名知意。
4. update
* 需求:根据id修改某条记录。
实现:
<update id="updateById">
update t_car set
car_num=#{carNum},
brand=#{brand},
guide_price=#{guidePrice},
produce_time=#{produceTime},
car_type=#{carType}
where
id = #{id}
</update>
Car car = new Car(4L, "9999", "凯美瑞", 30.3, "1999-11-10", "燃油车");
int count = sqlSession.update("updateById", car);
5. select(查一个,根据主键查询的话,返回的结果一定是一个。)
* 需求:根据id查询。
实现:
<select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
select * from t_car where id = #{id}
</select>
Object car = sqlSession.selectOne("selectById", 1);
需要特别注意的是:
select标签中resultType属性,这个属性用来告诉mybatis,查询结果集封装成什么类型的java对象。你需要告诉mybatis。
resultType通常写的是:全限定类名。
Car{id=1, carNum='null', brand='宝马520Li', guidePrice=null, produceTime='null', carType='null'}
输出结果有点不对劲:
id和brand属性有值。
其他属性为null。
carNum以及其他的这几个属性没有赋上值的原因是什么?
select * from t_car where id = 1
执行结果:
+----+---------+-----------+-------------+--------------+----------+
| id | car_num | brand | guide_price | produce_time | car_type |
+----+---------+-----------+-------------+--------------+----------+
| 1 | 1001 | 宝马520Li | 10.00 | 2020-10-11 | 燃油车 |
+----+---------+-----------+-------------+--------------+----------+
car_num、guide_price、produce_time、car_type这是查询结果的列名。
这些列名和Car类中的属性名对不上。
Car类的属性名:
carNum、guidePrice、produceTime、carType
那这个问题怎么解决呢?
select语句查询的时候,查询结果集的列名是可以使用as关键字起别名的。
<select id="selectById" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
where
id = #{id}
</select>
起别名之后:
+----+--------+-----------+------------+-------------+---------+
| id | carNum | brand | guidePrice | produceTime | carType |
+----+--------+-----------+------------+-------------+---------+
| 1 | 1001 | 宝马520Li | 10.00| 2020-10-11 | 燃油车 |
+----+--------+-----------+------------+-------------+---------+
6. select(查所有的)
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
</select>
List<Object> cars = sqlSession.selectList("selectAll");
注意:resultType还是指定要封装的结果集的类型。不是指定List类型,是指定List集合中元素的类型。
selectList方法:mybatis通过这个方法就可以得知你需要一个List集合。它会自动给你返回一个List集合。
7. 在sql mapper.xml文件当中有一个namespace,这个属性是用来指定命名空间的。用来防止id重复。
怎么用?
在xml文件中:
<mapper namespace="aaaaaaaaa">
<select id="selectAll" resultType="com.powernode.mybatis.pojo.Car">
select
id,car_num as carNum,brand,guide_price as guidePrice,
produce_time as produceTime,
car_type
from
t_car
</select>
</mapper>
在java程序中的写法:
List<Object> cars = sqlSession.selectList("aaaaaaaaa.selectAll");
实际上,本质上,mybatis中的sqlId的完整写法:
namespace.id
四、MyBatis核心配置文件详解
- 配置文件内容:
<?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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
-
configuration:根标签,表示配置信息。
-
environments:环境(多个),以“s”结尾表示复数,也就是说mybatis的环境可以配置多个数据源。
- default属性:表示默认使⽤的是哪个环境,default后⾯填写的是environment的id。default的值只需要和environment的id值⼀致即可。
-
environment:具体的环境配置(主要包括:事务管理器的配置 + 数据源的配置)
- id:给当前环境⼀个唯⼀标识,该标识⽤在environments的default后⾯,⽤来指定默认环境的选择。
-
transactionManager:配置事务管理器
-
type属性:指定事务管理器具体使⽤什么⽅式,可选值包括两个
-
JDBC:使⽤JDBC原⽣的事务管理机制。底层原理:事务开启
conn.setAutoCommit(false);
开启事务…处理业务…
conn.commit();
事务提交 -
MANAGED:交给其它容器来管理事务,⽐如WebLogic、JBOSS等。如果没有管理事务的 容器,则没有事务。没有事务的含义:只要执⾏⼀条DML语句,则提交⼀次。
-
-
-
dataSource:指定数据源
- type属性:⽤来指定具体使⽤的数据库连接池的策略,可选值包括三个
- UNPOOLED:采⽤传统的获取连接的⽅式,虽然也实现Javax.sql.DataSource接⼝,但是 并没有使⽤池的思想。
- property可以是:
- driver 这是 JDBC 驱动的 Java 类全限定名。
- url 这是数据库的 JDBC URL 地址。
- username 登录数据库的⽤户名。
- password 登录数据库的密码。
- defaultTransactionIsolationLevel 默认的连接事务隔离级别。
- defaultNetworkTimeout 等待数据库操作完成的默认⽹络超时时间(单位:毫秒)
- property可以是:
- POOLED:采⽤传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。
- property可以是(除了包含UNPOOLED中之外):
- poolMaximumActiveConnections 在任意时间可存在的活动(正在使⽤)连接数量,默认值:10
- poolMaximumIdleConnections 任意时间可能存在的空闲连接数。
- 其它…
- property可以是(除了包含UNPOOLED中之外):
- JNDI:采⽤服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不⼀样。如果不是web或者maven的war⼯程,JNDI是不能使⽤的。
- property可以是(最多只包含以下两个属性):
- initial_context 这个属性⽤来在 InitialContext 中寻找上下⽂(即, initialContext.lookup(initial_context))这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
- data_source 这是引⽤数据源实例位置的上下⽂路径。提供了 initial_context 配置时会 在其返回的上下⽂中进⾏查找,没有提供时则直接在 InitialContext 中查找。
- property可以是(最多只包含以下两个属性):
- UNPOOLED:采⽤传统的获取连接的⽅式,虽然也实现Javax.sql.DataSource接⼝,但是 并没有使⽤池的思想。
- type属性:⽤来指定具体使⽤的数据库连接池的策略,可选值包括三个
-
mappers:在mappers标签中可以配置多个sql映射⽂件的路径。
-
mapper:配置某个sql映射⽂件的路径
- resource属性:使⽤相对于类路径的资源引⽤⽅式
- url属性:使⽤完全限定资源定位符(URL)⽅式
4.1 environment
- mybatis-config.xml文件:
<?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="powernode">
<environment id="powernode">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="mybatis">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
- 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="car">
<insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
</mapper>
- java程序:
@Test
public void testEnvironment() throws Exception{
// 获取SqlSessionFactory对象(采用默认的方式获取)
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 这种方式就是获取的默认环境
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
int count = sqlSession.insert("car.insertCar");
System.out.println("插入了几条数据:" + count);
// 提交事务,关闭资源
sqlSession.commit();
sqlSession.close();
SqlSessionFactory sqlSessionFactory1 = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis");
SqlSession sqlSession1 = sqlSessionFactory1.openSession();
// 执行sql
count = sqlSession1.insert("car.insertCar");
System.out.println("插入了几条数据:" + count);
// 提交事务,关闭资源
sqlSession1.commit();
sqlSession1.close();
}
- 课堂笔记
- default表示默认使用的环境。
- 默认环境什么意思?当你使用mybatis创建SqlSessionFactory对象的时候,没有指定环境的话,默认使用哪个环境。
- 其中的一个环境。连接的数据库是powernode
- 一般一个数据库会对应一个SqlSessionFactory对象。
- 一个环境environment会对应一个SqlSessionFactory对象
4.2 transactionManager
-
transactionManager
的type
为MANAGED时:- 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="car"> <insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car"> insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert> </mapper>
- java程序:
@Test public void testTransactionManager() throws IOException { Car car = new Car(); car.setCarNum("133"); car.setBrand("丰田霸道"); car.setGuidePrice(50.3); car.setProduceTime("2020-01-10"); car.setCarType("燃油车"); // 获取SqlSessionFactory对象(采用默认的方式获取) SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 这种方式就是获取的默认环境 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行sql int count = sqlSession.insert("car.insertCarByPOJO", car); System.out.println("插入了几条数据:" + count); // 提交事务,关闭资源 // sqlSession.commit(); sqlSession.close(); }
- 交给容器去管理事务,但⽬前使⽤的是本地程序,没有容器的⽀持,当mybatis找不到容器的⽀持时:没有事务。也就是说只要执⾏⼀条DML语句,则提交⼀次。
-
当事务管理器是:JDBC
-
采⽤JDBC的原⽣事务机制:
开启事务:
conn.setAutoCommit(false);
…处理业务…
提交事务:
conn.commit();
-
-
课堂笔记
- transactionManager标签:
- 作用:配置事务管理器。指定mybatis具体使用什么方式去管理事务。
- type属性有两个值:
第一个:JDBC: 使用原生的JDBC代码来管理事务。
conn.setAutoCommit(false);
…
conn.commit();
第二个:MANAGED:mybatis不再负责事务的管理,将事务管理交给其它的JEE(JavaEE)容器来管理。例如:spring - 大小写无所谓。不缺分大小写。但是不能写其他值。只能是二选一:
jdbc、managed - 在mybatis中提供了一个事务管理器接口:Transaction
该接口下有两个实现类:
JdbcTransaction
ManagedTransaction
如果type=“JDBC”,那么底层会实例化JdbcTransaction对象。
如果type=“MANAGED”,那么底层会实例化ManagedTransaction
4.3 dataSource
type
可选有三种- UNPOOLED
- POOLED
- JNDI
- dataSource配置:
-
dataSource被称为数据源。
-
dataSource作用是什么?为程序提供Connection对象。(但凡是给程序提供Connection对象的,都叫做数据源。)
-
数据源实际上是一套规范。JDK中有这套规范:javax.sql.DataSource(这个数据源的规范,这套接口实际上是JDK规定的。)
-
我们自己也可以编写数据源组件,只要实现javax.sql.DataSource接口就行了。实现接口当中所有的方法。这样就有了自己的数据源。
比如你可以写一个属于自己的数据库连接池(数据库连接池是提供连接对象的,所以数据库连接池就是一个数据源)。 -
常见的数据源组件有哪些呢【常见的数据库连接池有哪些呢】?
阿里巴巴的德鲁伊连接池:druid
c3p0
dbcp
… -
type属性用来指定数据源的类型,就是指定具体使用什么方式来获取Connection对象:
type属性有三个值:必须是三选一。
type=“[UNPOOLED|POOLED|JNDI]”
UNPOOLED:不使用数据库连接池技术。每一次请求过来之后,都是创建新的Connection对象。
POOLED:使用mybatis自己实现的数据库连接池。
JNDI:集成其它第三方的数据库连接池。 -
JNDI是一套规范。谁实现了这套规范呢?大部分的web容器都实现了JNDI规范:
例如:Tomcat、Jetty、WebLogic、WebSphere,这些服务器(容器)都实现了JNDI规范。
JNDI是:java命名目录接口。Tomcat服务器实现了这个规范。
-
这种再重点说⼀下type="POOLED"的时候,它的属性有哪些?
<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!--提醒:正常使用连接池的话,池中有很多参数是需要设置的。设置好参数,可以让连接池发挥的更好。事半功倍的效果。--> <!--具体连接池当中的参数如何配置呢?需要反复的根据当前业务情况进行测试。--> <!--poolMaximumActiveConnections:连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10--> <property name="poolMaximumActiveConnections" value="10"/> <!--每隔2秒打印日志,并且尝试获取连接对象--> <property name="poolTimeToWait" value="2000"/> <!--强行让某个连接空闲,超时时间的设置--> <property name="poolMaximumCheckoutTime" value="10000"/> <!--最多的空闲数量--> <property name="poolMaximumIdleConnections" value="5"/> </dataSource>
-
poolMaximumActiveConnections:最⼤的活动的连接数量。默认值10
- 最⼤的活动的连接数量就是连接池连接数量的上限。
- 默认值10,如果有10个请求正在使⽤这10个连接, 第11个请求只能等待空闲连接。
-
poolMaximumIdleConnections:最⼤的空闲连接数量。默认值5
- 如果已经有了5个空闲连接,当第6个连接要空闲下来的时候,连接池会选择关闭该连接对象,来减少数据库的开销。
-
poolMaximumCheckoutTime:强⾏回归池的时间。默认值20秒。
-
poolTimeToWait:当⽆法获取到空闲连接时,每隔20秒打印⼀次⽇志,避免因代码配置有误,导致傻等。(时⻓是可以配置的)
-
需要根据系统的并发情况,来合理调整连接池最⼤连接数以及最多空闲数量。充分发挥数据库连接池的 性能。
当然,还有其他属性。对于连接池来说,以上几个属性⽐较重要。
-
-
测试一下:
- mybatis-config.xml文件:
<?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="powernode"> <environment id="powernode"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/powernode"/> <property name="username" value="root"/> <property name="password" value="root"/> <!--提醒:正常使用连接池的话,池中有很多参数是需要设置的。设置好参数,可以让连接池发挥的更好。事半功倍的效果。--> <!--具体连接池当中的参数如何配置呢?需要反复的根据当前业务情况进行测试。--> <!--poolMaximumActiveConnections:连接池当中最多的正在使用的连接对象的数量上限。最多有多少个连接可以活动。默认值10--> <property name="poolMaximumActiveConnections" value="3"/> <!--每隔2秒打印日志,并且尝试获取连接对象--> <property name="poolTimeToWait" value="2000"/> <!--强行让某个连接空闲,超时时间的设置--> <property name="poolMaximumCheckoutTime" value="10000"/> <!--最多的空闲数量--> <property name="poolMaximumIdleConnections" value="5"/> </dataSource> </environment> </environments> <mappers> <mapper resource="CarMapper.xml"/> </mappers> </configuration>
- 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="car"> <insert id="insertCar"> insert into t_car values(null,'8888','沃尔沃',30.0,'2000-11-11','燃油车') </insert> </mapper>
- java程序:
@Test public void testDataSource() throws IOException { SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // sqlSessionFactory对象:一个数据一个。不要频繁创建该对象。 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")); /*// 通过sqlSessionFactory对象可以开启多个会话。 // 会话1 SqlSession sqlSession1 = sqlSessionFactory.openSession(); sqlSession1.insert("car.insertCar"); sqlSession1.commit(); sqlSession1.close(); // 因为要测试连接池的效果,关闭是需要的。别忘了,要不然测不出来。 // 会话2 SqlSession sqlSession2 = sqlSessionFactory.openSession(); sqlSession2.insert("car.insertCar"); sqlSession2.commit(); sqlSession2.close();*/ SqlSession sqlSession = null; // 设置最大连接为3,所以调用4个连接时会出现等待现象 for (int i = 0; i < 4; i++) { sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("car.insertCar"); // 不要关闭。 } sqlSession.close(); // 由于没有提交事务,所以数据库无变化 }
- 测试连接池:
- 连接池图示
4.4 properties
-
mybatis提供了更加灵活的配置,连接数据库的信息可以单独写到⼀个属性资源⽂件中.
-
java.util.Properties类。是一个Map集合。key和value都是String类型
- 在properties标签中可以配置很多属性
<properties> <!--这是其中的一个属性--> <!--<property name="属性名" value="属性值"/>--> <property name="jdbc.username" value="root"/> <property name="jdbc.password" value="root"/> </properties>
- 通过文件的方式导入properties信息
<!--resource,一定是从类路径下开始查找资源--> <properties resource="jdbc.properties" /> <!--从绝对路径当中加载资源。绝对路径怎么写?file:///路径--> <properties url="file:///d:/jdbc.properties" />
-
假设在类的根路
径下创建jdbc.properties⽂件,配置如下:
jdbc.username=root
jdbc.password=root
- 在mybatis核⼼配置⽂件中引⼊并使用:
<?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>
<!--resource,一定是从类路径下开始查找资源-->
<properties resource="jdbc.properties" />
<environments default="powernode">
<environment id="powernode">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/powernode"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
</configuration>
- 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="car">
<insert id="insertCar">
insert into t_car values(null,'8888','沃尔沃',30.0,'2000-11-11','燃油车')
</insert>
</mapper>
- java程序:
@Test
public void testProperties(){
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行sql
int count = sqlSession.insert("car.insertCar");
System.out.println("插入了几条记录:" + count);
sqlSession.commit();
sqlSession.close();
}
- 注意:如果不知道mybatis-config.xml⽂件中标签的编写顺序的话,可以有两种⽅式知道它的顺序:
- 第⼀种⽅式:查看dtd约束⽂件。
- 第⼆种⽅式:通过idea的报错提示信息。【⼀般采⽤这种⽅式】
4.5 mapper
- mapper标签⽤来指定SQL映射⽂件的路径,包含多种指定⽅式,这⾥先主要看其中两种:
- 第⼀种:resource,从类的根路径下开始加载【比url常⽤】
<mappers>
<mapper resource="CarMapper.xml"/>
</mappers>
如果是这样写的话,必须保证类的根下有CarMapper.xml⽂件。
如果类的根路径下有⼀个包叫做test,CarMapper.xml如果放在test包下的话,这个配置应该是这样写:
<mappers>
<mapper resource="test/CarMapper.xml"/>
</mappers>
-
第⼆种:url,从指定的url位置加载
假设CarMapper.xml⽂件放在d盘的根下,这个配置就需要这样写:
<mappers>
<mapper url="file:///d:/CarMapper.xml" />
</mappers>
[To be continue…]