目录
2.8 MyBatis⼯具类SqlSessionUtil的封装
1 MyBatis概述
1.1 框架
Java常用的框架:
- SSM三大框架:Spring+SpringMVC+MyBatis
- SpringBoot
- SpringCloud
- 等等
框架就是对通用代码的封装,提前写好了一堆接口和类,我们可以在做项目的时候直接引入这些接口和类,这样可以大大提高开发效率
框架一般以jar包的形式存在
SSM三大框架建议学习顺序:MyBatis-->Spring-->SpringMVC
1.2 MyBatis在三层架构的位置
在学javaweb的时候我们就详细学过三层架构,大概就是把业务分为三层:
- 表现层:直接根前端打交道(一是接收前端ajax请求,二是返回JSON数据给前端)
- 业务逻辑层:一是处理表现层转发过来的前端请求(也就是具体业务),二是将持久层获取的数据返回到表现层
- 数据访问层(持久层):直接操作数据库完成增删改查(CRUD),并将获取的数据返回到上一层(也就是业务逻辑层)
本次我们学的MyBatis就是持久层框架
当然持久层框架不止MyBatis,常用的还有Spring Data
1.3 JDBC的不足
- SQL语句写死在java程序,如果业务变化,我们需要修改代码,违背了OCP原则
- 给?传值是繁琐的,都是重复的代码,自动化会很舒服
- 将结果集封装到java对象也是繁琐的,自动化会很舒服
1.4 关于MyBatis
MyBatis是一个ORM框架,是一个半自动化的ORM,MyBatis中的SQL语句是需要自己编写的
什么是ORM:
- O(Object):Java虚拟机中的java对象
- R(Relational):关系型数据库
- M(Mapping):将Java虚拟机中的Java对象映射到数据库表中一行记录,或是将数据库表中一行记录映射成Java虚拟机中的一个Java对象
大概的意思就是:假如有个数据库表t_user,表有列名name、age等等,它有一行行数据。假如有一个类为User类,它有属性name、age等等,我可以new对象装数据。这里的数据库表t_user和User类就形成一个对应
MyBatis框架的特点:
- 支持定制化SQL、存储过程、基本映射以及高级映射
- 避免了几乎所有jdbc代码中手动设置参数以及获取结果
- 支持XML开发,也支持注解开发(为了保证SQL语句的灵活,所以MyBatis大部分采用XML开发)
- 接口和java的POJOs映射成数据库中的记录(这里的pojo有的人会使用bean或者domain)
- 体积小,好学等等
2 MyBatis入门程序
2.1 组件版本
- MySQL驱动:8.0.30
- MyBatis:3.5.10
- JDK:Java17
- Logback:1.2.11
2.2 MyBatis入门程序开发步骤
2.2.1 写代码前的准备
先使用Navicat新建一个数据库如下:
设计一个数据库名为t_car,然后随便插入两个数据:
配置idea
先创建一个名为mybatis的空项目,然后设置maven,这里的maven之前设置过了
选择jdk版本
创建一个maven模块,不使用maven骨架
2.2.2 编写mybatis步骤
步骤一:打包方式:jar(不需要war,因为mybatis封装的是jdbc)
步骤二:引入依赖(mybatis和mysql依赖)
这个去这个网站上找https://mvnrepository.com/
<dependencies>
<!--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>
</dependencies>
步骤三:在resources根目录下新建mybatis-config.xml配置文件(参考mybatis手册拷贝)
https://mybatis.org/mybatis-3/zh/index.html 这是mybatis手册中文版网址
这个文件是核心配置文件,主要配置连接数据库的信息等
注意1:放在resources目录中的一般都是资源文件、配置文件,等同于放到类的根路径下
注意2:mybatis核心配置文件文件名不一定是mybatis-config.xml,可以是其它名字,不过大家都是用这个。配置文件位置也可以随意,这里放到resources下相当于放到类的根路径下。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://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="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
注意修改中间的连接数据库的信息部分,这里是我们学jdbc经常配置的
步骤四:在resources根目录下新建CarMapper.xml配置文件(参考mybatis手册)
这个文件是专门写SQL语句的配置文件(建议一个表一个)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写⼀个-->
<mapper namespace="car">
<!--插入一个信息-->
<!--这里的id是这条SQL语句的唯一标识,这个id就代表了这条SQL语句-->
<insert id="insertCar">
insert into t_car
(id,car_num,brand,guide_price,produce_time,car_type)
values
(null,'102','丰⽥mirai',40.30,'2014-10-05','氢能源')
</insert>
</mapper>
注意1:SQL语句最后结尾可以不写分号
注意2:CarMapper.xml文件的名字不是固定的
注意3:CarMapper.xml文件的位置也是随意的
注意4:将CarMapper.xml文件路径配置到mybatis-config.xml中
resources属性会自动从类的根路径下找资源,而CarMapper这个文件恰好在根路径
步骤五: 编写MyBatisIntroduction代码
在MyBatis中,负责执行SQL语句的对象叫SqlSession,SqlSession是专门用来执行SQL语句的,是一个Java程序和数据库之间的一次会话
要想获取SqlSession对象,需要先获取SqlSessionFactory对象,通过SqlSessionFactory工厂来生产SqlSession对象。要想获取SqlSessionFactory对象,需要先获取SqlSessionFactoryBuilder对象,通过SqlSessionFactoryBuilder对象的build方法获取一个SqlSessionFactory对象
实际操作如下:
- 先new一个SqlSessionFactoryBuilder对象
- 再在使用SqlSessionFactoryBuilder对象调用build方法,我们需要给这个方法传个输入流参数,这个输入流指向mybatis-config.xml文件
- 我们可以先new一个FileInputStream获取到这个文件的流,我们还可以使用mybatis的一个工具类:Resources,调用Resources的方法getResourcesAsStream。这个方法默认从根的路径下找资源。得到输入流传给build方法
- 使用SqlSessionFactory对象调用openSession方法得到sqlSession对象
- 使用sqlSession对象调用方法执行SQL语句
- 切记要手动提交,sqlSession默认不提交数据
注意:一般情况下一个数据库对应一个sqlSessionFactory对象
代码如下:
package com.itzw.mybatis;
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.InputStream;
public class MyBatisIntroductionTest {
public static void main(String[] args) throws Exception{
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行SQL
int count = sqlSession.insert("insertCar");//返回的是影响数据库条数
System.out.println("插入几条记录:"+count);
//手动提交
sqlSession.commit();
}
}
2.3 总结上个程序的小细节
- mybatis中SQL语句的结尾“;”可以省略
- 关于Resources.getResourceAsStream():小技巧:但凡遇到resource这个单词,大部分情况下,这种加载资源的方式是从类的根路径下开始查找。这种方式,项目的移植性很强
- 在我们获取mybatis-config.xml这个核心文件时,还可以使用InputStream is = new FileInputStream()。但是这种方式移植性差,违背了OCP原则
- InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(),使用这个也可以获取resources目录下的资源,和Resources.getResourceAsStream一样的效果。我们 通过源码发现,Resources工具类的getResourceAsStream底层就是使用ClassLoader获取到的。
- CarMapper.xml文件的名字和目录也不是固定的。我们还可以使用url属性获取mapper文件,不过不建议,就像上面获取mybatis核心配置文件一样,这样获取移植性差,不建议使用。
2.4 事务管理机制的深度剖析
在mybatis-config.xml文件中,可以通过以下配置进行mybatis的事务管理
<transactionManager type="JDBC"/>
type属性的值有两个:JDBC(jdbc)、MANAGED(managed),不区分大小写
也就是说mybatis中提供了两种事务管理机制
JDBC事务管理器:
mybatis框架自己管理事务,自己采用原生的jdbc代码去管理事务:conn.setAutoCommit(false)用来开启事务--->业务处理--->conn.commit()手动提交事务。
使用JDBC事务管理器的话,底层创建的事务管理器对象是:JdbcTransaction对象
如果编写的代码是以下的代码:
SqlSession sqlSession = sqlSessionFactory.openSession(true);表示没有开启事务,因为这种方式压根不会执行:conn.setAutoCommit(false)。这种情况,只要DML语句执行就会提交。
SqlSession sqlSession = sqlSessionFactory.openSession(),也就是默认不写参数,这表示如果使用的事务管理器是jdbc的话,底层会执行conn.setAutoCommit(false),也就是默认开启事务。
sqlSession.commit(),如果使用的事务管理器是jdbc的话,底层实际上执行的是conn.commit()
不开启事务的方式我们是不建议的
MANAGED事务管理器
mybatis不再负责事务的管理,事务交给其它容器管理,例如:spring
对于我们目前单纯地使用mybatis的情况下,如果配置MANAGED,那么事务是没人管的,没人管表示事务没开启,就是没有事务。当然这也是我们不建议的。
重点:
以后注意,只要autoCommit是true就表示没有开启事务,是false就表示开启了事务
2.5 一个比较完整的mybatis程序
package com.itzw.mybatis;
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 MyBatisCompleteTest {
public static void main(String[] args){
SqlSession sqlSession = null;
try {
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
//获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//执行SQL
int count = sqlSession.insert("insertCar");
System.out.println("执行条数:"+count);
//执行到这里没有异常,提交事务,终止事务
sqlSession.commit();
} catch (IOException e) {
//最好回滚事务
if (sqlSession != null){
sqlSession.rollback();
}
e.printStackTrace();
}finally {
//关闭会话
sqlSession.close();
}
}
}
比上个程序多了:回滚事务和关闭会话还有异常使用try-catch处理
2.6 引入JUnit
2.6.1 开发中junit是怎么使用的
JUnit是专门做单元测试的组件,在实际开发中,单元测试是由我们Java程序员来完成的。测试过程中设计两个概念:期望值和实际值。期望值和实际值相同表示测试通过,不同则报错。
在这里引入JUnit是为了代替main方法
使用JUnit步骤:
第一步:引入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
第二步:在main目录里面编写一个类,类里面有方法
package com.itzw.junit;
public class MathService {
/**
* 求和业务方法
* @param a
* @param b
* @return
*/
public static int sum(int a,int b){
return a + b;
}
/**
* 相减业务方法
* @param a
* @param b
* @return
*/
public static int sub(int a,int b){
return a - b;
}
}
第三步:在test目录里面编写测试方法
- 在这个目录里面创建的包名一般来说是和mian里面的包名一样。test里面的类名一般来说是main里面的类名加上Test。
- 测试方法规范:public void testXxx(){},假如测试的方法是sum,那么测试方法名为testSum()
- 另外要在测试方法上加上@Test注解
package com.itzw.junit;
import org.junit.Assert;
import org.junit.Test;
public class MathServiceTest {
@Test
public void testSum(){
MathService mathService = new MathService();
//实际值
int actual = mathService.sum(1, 2);
//期望值
int expected = 3;
//加断言进行测试
Assert.assertEquals(expected,actual);
}
@Test
public void testSub(){
MathService mathService = new MathService();
//实际值
int actual = mathService.sub(10, 2);
//期望值
int expected = 8;
//加断言进行测试
Assert.assertEquals(expected,actual);
}
}
2.6.2 mybatis中引入junit
和上面一样,最后测试如下:
package com.itzw.mybatis;
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 {
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
//获取SqlSession对象
sqlSession = sqlSessionFactory.openSession();
//执行SQL
int count = sqlSession.insert("insertCar");
System.out.println("执行条数:"+count);
//执行到这里没有异常,提交事务,终止事务
sqlSession.commit();
} catch (IOException e) {
//最好回滚事务
if (sqlSession != null){
sqlSession.rollback();
}
e.printStackTrace();
}finally {
//关闭会话
sqlSession.close();
}
}
}
就是在测试类里面创建一个方法,然后将之前写的代码main里面的部分复制到这里即可。
2.7 MyBatis集成日志框架logback
引入日志框架的目的是为了看清楚MyBatis执行的具体SQL
启用标准日志组件,只需在mybatis-config.xml文件中添加以下配置(参考mybatis手册):
<setting name="logImpl" value="STDOUT_LOGGING"/>
mybatis常见的集成的日志组件有哪些呢?
- SLF4J
- LOG4J
- LOG4J2
- STDOUT_LOGGING
其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志,只要开启即可,怎么开启?上面那个setting配置就是开启了。
注意:使用settings标签要放在environment标签之前。注意顺序,当然你出错idea会提示,不用怕
经过测试STDOUT_LOGGING日志发现它内容很少,我们可以继承其它日志组件例如log4j,logback等
引入logback步骤:
第一步:引入logback相关依赖
<!--logback依赖,这个日志框架实现了slf4j-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>test</scope>
</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.PatternLayoutEncode
r">
<!--格式化输出:%d表示⽇期,%thread表示线程名,%-5level:级别从左显示5
个字符宽度%msg:⽇志消息,%n是换⾏符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logge
r{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>
我们使用这个日志就可以看到SQL语句了
注意:如果使用标准日志组件STDOUT_LOGGING需配置setting,如果是第三方日志组件则不需要配置也行。
2.8 MyBatis⼯具类SqlSessionUtil的封装
每一次获取sqlSession对象代码太繁琐了,封装一个工具类
SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库,所以并不是每次都需要创建一个新的SqlSessionFactory对象,所以我们把SqlSessionFactory对象的创建放在静态代码块中,也就是说类加载时创建而且只创建一次
工具类如下:
package com.itzw.mybatis.util;
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 SqlSessionUtil {
//工具类的构造方法一般是私有化的
//因为工具类的方法都是静态的,直接采用类名即可调用,不需要new对象
//为了防止new对象,构造方法私有化
private SqlSessionUtil() {}
/**
* 类加载时获取sqlSessionFactory对象
*/
private static SqlSessionFactory sqlSessionFactory ;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 每调用一次openSession获得一个新的会话
* @return
*/
public static SqlSession openSession(){
return sqlSessionFactory.openSession();
}
}
测试工具类是否可用:
@Test
public void testInsertCarByUtil(){
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.insert("insertCar");
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
3 使用MyBatis完成CRUD
什么是CRUD
- C:Create 增
- R:Retrieve 查(检索)
- U:Update 改
- D:Delete 删
先准备环境,就是上面我们学到的:
- MyBatis、mysql、junit、logback依赖
- mybatis-config、mapper、logback配置文件
- sqlSession工具类
3.1 insert(Create)
分析之前写的SQL语句存在的问题:值不该写死,应该是用户提供的值,在JDBC中我们用占位符?实现动态存值,这里我们使用#{}。这和JDBC中的?是等效的
我们在后端提供一个map集合,map集合中的key随意取值,不过我们一般见名知意。而map集合的值就是我们想存的值,那么这个key就可以取名为对应值得key,其实就可以根据数据库表的列名取值。然后在mapper.xml文件中,在#{}中的大括号中写入这个key,它会自动获取到这个key对应的值。修改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>
测试方法如下:主要关注map集合和insert方法
@Test
public void testInsetCar(){
SqlSession sqlSession = SqlSessionUtil.openSession();
Map map = new HashMap();
map.put("carNum","1111");
map.put("brand","比亚迪");
map.put("guidePrice",10);
map.put("produceTime","2021-11-11");
map.put("carType","新能源");
int count = sqlSession.insert("insertCar", map);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
既然使用map集合可以封装数据进行插入数据操作,那么使用POJO类可不可以?
下面使用POJO类封装数据:
在封装数据前自然是要写一个类,名为Car。这里的属性应该和数据库表中的字段一一对应,并且建议使用包装类,这样可以防止null的问题,因为基本数据类型不能赋值为null
那SQL语句中的#{}中又改写什么呢?写POJO类定义的属性名称,因为它是和数据库表对应的,写这些属性名也是和数据库表对应的。
经过我们测试发现,为什么写的是属性名呢?因为底层得到这个属性它会去找对应的getter方法,比如#{username},它会去找getUsername方法,而我们创建的getter和setter方法都是符合java规范的,也都是这样命名的,所以我们直接写入属性名称它自然能找到对应的方法。或者我们可以说这个大括号内写的是get方法去掉get并且把首字母小写得到的。
pojo类:
package mybatis.pojo;
public class Car {
private Long id;
private String carName;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
public Car(){}
public Car(Long id, String carName, String brand, Double guidePrice, String produceTime, String carType) {
this.id = id;
this.carName = carName;
this.brand = brand;
this.guidePrice = guidePrice;
this.produceTime = produceTime;
this.carType = carType;
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", carName='" + carName + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
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;
}
}
SQL语句:
insert into t_car (id,car_num,brand,guide_price,produce_time,car_type)
values
(null,#{carName},#{brand},#{guidePrice},#{produceTime},#{carType})
测试类:
@Test
public void testInsertCarByPojo(){
Car car = new Car(null,"123","比亚迪秦",30.0,"2021-4-3","新能源");
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.insert("insertCar", car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
3.2 delete(Delete)
根据id删除数据
SQL语句:
<delete id="deleteById">
delete from t_car where id = #{id}
</delete>
注意:如果占位符只有一个,那么可以随便写内容,但是还是建议见名知意
测试类:
@Test
public void deleteById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
//删除id为12的
//int cout = sqlSession.delete("deleteById", 12);
int cout = sqlSession.delete("deleteById", "13");
System.out.println(cout);
sqlSession.commit();
sqlSession.close();
}
3.3 update(Update)
根据id修改某条信息
SQL语句:
<update id="updateById">
update t_car set
car_num=#{carName},
brand=#{brand},
guide_price=#{guidePrice},
produce_time=#{produceTime},
car_type=#{carType}
where id=#{id}
</update>
测试类:
@Test
public void testUpdateById(){
Car car = new Car(3L,"4716","奔驰",56.6,"2009-9-9","燃油车");
SqlSession sqlSession = SqlSessionUtil.openSession();
sqlSession.update("updateById",car);
sqlSession.commit();
sqlSession.close();
}
3.4 select(Retrieve)
根据id查询一条信息
SQL语句:
<select id="selectById" resultType="com.itzw.mybatis.pojo.Car">
select id,
car_num as carName,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
where
id = #{id}
</select>
注意1:这里要使用resultType属性指定查询结果集封装成什么类型的java
注意2:我们不能直接查询所有列名,要和Car类的属性一致才行,否则查不到信息,我们可以使用as来起别名,和类的属性对应上
测试类:
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
Object car = sqlSession.selectOne("selectById", 1);
System.out.println(car);
sqlSession.close();
}
上面是查询一个,那我们试试查询所有信息:
SQL语句:
<select id="selectAll" resultType="com.itzw.mybatis.pojo.Car">
select id,
car_num as carName,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
</select>
测试类:
@Test
public void testSelectAll(){
SqlSession sqlSession = SqlSessionUtil.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
3.5 关于Mapper文件的namespace
我们新创建一个Mapper文件,名为UserMapper,在里面有和之前的CarMapper文件相同的id,然后在mybatis-config文件中引入这个文件,我们来测试这样还行不行,文件如下:
测试类:
@Test
public void testNamespace(){
SqlSession sqlSession = SqlSessionUtil.openSession();
List<Car> cars = sqlSession.selectList("selectAll");
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
很自然的就出错了。
很显然是因为selectAll这个id在两个Mapper文件中都有
这时namespace就派上了用场,sqlId的完成写法为namespace.id,namespace就是为了防止命名冲突的
4 MyBatis核心配置文件详解
4.1 environment
首先新建一个数据库名为mybatis,在里面创建一个表名为t_car和之前一样
在核心配置文件中我们新建一个环境,如下:
<environments default="powernodeDB">
<environment id="mybatisDB">
<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="123456"/>
</dataSource>
</environment>
<environment id="powernodeDB">
<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="123456"/>
</dataSource>
</environment>
</environments>
上面一共是两个环境,不同的就是一个连接的是powernode数据库,一个连接的是mybatis数据库,而在environments标签中有个default属性,它的值是powernodeDB,也就是powernode那个环境的id值,也就是说默认连接的是powernode数据库。一个环境对应一个SqlSessionFactory对象,也就是一个数据库对应一个SqlSessionFactory对象。
当我们创建SqlSessionFactory对象的时候,我们发现在我们填完mybatis核心配置文件后还能再写个参数,而这个参数就是environment。也就是说我们可以指定创建的SqlSessionFactory对象连接到的数据库是哪一个,而不指定就是默认使用environments中default的值。
我们先测试默认情况下会连接哪个数据库:
@Test
public void testEnvironment() throws Exception{
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
Car car = new Car(null,"4321","劳斯莱斯",222.2,"2004-4-4","燃油车");
sqlSession.insert("insertCar",car);
sqlSession.commit();
sqlSession.close();
}
运行以上代码我们发现正是powernode数据库中的表新增了数据,和我们想的一样
我们再指定数据库为mybatis试试
@Test
public void testEnvironment2() throws Exception{
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"),"mybatisDB");
SqlSession sqlSession = sqlSessionFactory.openSession();
Car car = new Car(null,"1234","aolia",222.2,"2004-4-4","燃油车");
sqlSession.insert("insertCar",car);
sqlSession.commit();
sqlSession.close();
}
果然这次是mybatis数据库新增了数据
4.2 transactionManager
这个属性在前面就讲过,它的作用是配置事务管理器,指定mybatis具体使用什么方式管理事务
其中type属性有两个值:
- 第一个:JDBC
- 第二个:MANAGED
大概就是JDBC会使用原生的JDBC来挂你了事务。而使用MANAGED,mybatis不再负责事务的管理,将事务管理交给其它容器管理:比如Spring
4.3 dataSource
在核心配置文件中,dataSource是如下的:
dataSource配置:
- dataSource被称为数据源
- dataSource作用是什么:为程序提供Connection对象(但凡给程序提供Connection对象的都叫做数据源)
- 数据源实际上是一套规范。JDK中有这套规范:javax.sql.DataSource
- 我们自己也可以编写数据源组件,只要实现javax.sql.DataSource接口就行了。比如自己可以写一个数据库连接池(数据库连接池是提供连接对象的,所以数据库连接池就是一个数据源)
常见的数据源组件有哪些(常见的数据库连接池):
- 阿里巴巴的德鲁伊连接池:druid
- c3p0
- dbcp
type属性用来指定数据源的类型,就是具体指定使用什么方式来获取Connection对象:
- type属性有三个值,必须是三选一
- type="[UNPOOLED|POOLED|JNDI]"
- UNPOOLED:不使用数据库连接池技术,每一次请求都是创建新的Connection对象
- POOLED:使用mybatis自己实现的数据库连接池
- JNDI:集成其它第三方的数据库连接池
JDNI是一套规范,谁实现了这套规范呢?大部分的web容器都实现了JNDI规范:例如Tomcat、Jetty、WebLogic\WebSphere
连接池的优点:每一次获取连接都是从池中拿,效率高。因为每一次只能从池中拿,所以连接对象的创建数量是可控的。
我们打开mybatis帮助文档,发现不止有我们之前配置的那几个属性,还有别的,我们测试一下
正常使用连接池的话,池中有很多参数是需要设置的,设置好参数可以让连接池发挥的更好
下面是一些属性的设置:
<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="123456"/>
<!--poolMaximumActiveConnections:连接池当中对多的正在使用的连接对象数量上限,默认为10 -->
<property name="poolMaximumActiveConnections" value="10"/>
<!--poolTimeToWait:每隔两秒打印日志,并且尝试获取连接对象-->
<property name="poolTimeToWait" value="2000"/>
<!--poolMaximumCheckoutTime:超时时间限制为10秒-->
<property name="poolMaximumCheckoutTime" value="10000"/>
<!--poolMaximumIdleConnections:最多空闲数量-->
<property name="poolMaximumIdleConnections" value="5"/>
</dataSource>
4.4 properties
我们可以在properties标签中配置很多属性。比如配置jdbc连接的信息
<properties>
<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="jdbc.username" value="root"/>
<property name="jdbc.password" value="123456"/>
</properties>
这就相当于map集合,然后在dataSource中获取上面的配置信息:
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
但是这个多少有点多此一举,这只是展示mybatis的灵活性。我们有更好用的方式如下:
在resources目录中新建jdbc.properties文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
然后在mybatis核心配置文件引入这个配置信息,使用properties标签引入:
<!--resouce一定是从类路径下开始查找资源-->
<properties resource="jdbc.properties"></properties>