SpringBoot学习篇10[整合Mybatis注解版、整合阿里Druid连接池、整合Mybatis配置文件版、事务]

1. 环境说明

数据库:MySQL
连接用户名:root
连接密码:123
库名:mybatis
导入以下测试数据:

-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `position` varchar(255) DEFAULT NULL COMMENT '职位',
  `salary` double(10,2) DEFAULT NULL COMMENT '薪资',
  `department` varchar(255) DEFAULT NULL COMMENT '部门',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of employee
-- ----------------------------
BEGIN;
INSERT INTO `employee` VALUES (1, '张三', 'web开发工程师', 15000.00, '研发软件部');
INSERT INTO `employee` VALUES (2, '李四', '嵌入式Linux开发工程师', 14000.00, '研发软件部');
INSERT INTO `employee` VALUES (3, '王五', 'QT开发工程师', 13000.00, '研发软件部');
INSERT INTO `employee` VALUES (4, '赵明', '硬件工程师', 13000.00, '研发硬件部');
COMMIT;

Employee类代码如下

public class Employee {
    private Integer id;
    private String name;
    private String position;
    private Double salary;
    private String department;

    public Employee() {
    }

    public Employee(String name, String position, Double salary, String department) {
        this.name = name;
        this.position = position;
        this.salary = salary;
        this.department = department;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", position='" + position + '\'' +
                ", salary=" + salary +
                ", department='" + department + '\'' +
                '}';
    }
}

2. 整合Mybatis注解版

新建项目时,勾选以下依赖
在这里插入图片描述

2.1 配置Mybatis

	spring:
	  datasource:
	    username: root
	    password: 123
	    #mysql8以上的驱动包需要指定以下时区
	    url: jdbc:mysql://127.0.0.1:23306/mybatis?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
	    driver-class-name: com.mysql.cj.jdbc.Driver
注:
[1] 使用MySQL8以上的驱动包需要使用serverTimezone指定时区,否则会报错
[2] 指定字符编码的目的是为了防止中文乱码。
[3] 使用MySQL8以上的驱动包时,driver-class-name需要指定为com.mysql.cj.jdbc.Driver,而不是原来的com.mysql.jdbc.Driver

2.2 编写接口,实现增删改查操作

新创建接口文件mapper.EmployeeMapper

	@Mapper
	public interface EmployeeMapper {
	
	    //根据id查找单个
	    @Select("select * from employee where id = #{id}")
	    Employee getById(Integer id);
	
	    //查找全部
	    @Select("select * from employee")
	    List<Employee> getAll();
	
	    //添加
	    //使用自增主键
	    @Options(useGeneratedKeys = true, keyProperty = "id")
	    @Insert("insert into  employee(name,position,salary,department) values(#{name},#{position},#{salary},#{department})")
	    int add(Employee employee);
	
	    @Update("update employee set name = #{name},position = #{position},salary = #{salary}, department = #{department} where id = #{id}")
	    int update(Employee employee);
	
	    @Delete("delete from employee where id = #{id}")
	    int delete(Integer id);
	
	}
 	@Mapper注解:标识这个接口是数据库映射接口,同时将其注入到SpringIOC容器
	@Insert、@Delete、@Update、@Select实现增删该查操作
	@Options指定useGeneratedKeys = true,代表自动生成主键;用keyProperty指定主键是哪一个

2.3 编写测试类,测试代码

	@SpringBootTest
	class EmployeeMapperTest {
	
	    @Autowired
	    EmployeeMapper employeeMapper;
	
	    @Test
	    void getById() {
	        Employee employee = employeeMapper.getById(1);
	        System.out.println(employee);
	    }
	
	    @Test
	    void getAll() {
	        List<Employee> employees = employeeMapper.getAll();
	        System.out.println(employees);
	    }
	
	    @Test
	    void add() {
	        Employee employee = new Employee("晓张","射频工程师",14000.00,"研发硬件部");
	        employeeMapper.add(employee);
	    }
	
	    @Test
	    void update() {
	        Employee employee = new Employee("张三","web开发工程师",15000.00,"服务端开发部");
	        employee.setId(1);
	        employeeMapper.update(employee);
	    }
	
	    @Test
	    void delete() {
	        employeeMapper.delete(5);
	    }
	}

可以看到全部测试通过
在这里插入图片描述
从打印的日志可以看出,SpringBoot底层采用的是HikariPool连接池

2.4 编写service,调用Mapper

接口编写:

public interface IEmployeeService {
    Employee getById(Integer id);

    List<Employee> getAll();

    int add(Employee employee);

    int update(Employee employee);

    int delete(Integer id);
}

接口实现:

@Service
public class EmployeeService implements IEmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;

    @Override
    public Employee getById(Integer id) {
        return employeeMapper.getById(id);
    }

    @Override
    public List<Employee> getAll() {
        return employeeMapper.getAll();
    }

    @Override
    public int add(Employee employee) {
        return employeeMapper.add(employee);
    }

    @Override
    public int update(Employee employee) {
        return employeeMapper.update(employee);
    }

    @Override
    public int delete(Integer id) {
        return employeeMapper.delete(id);
    }
}

2.5 编写Controller调用Service

@RestController
public class EmployeeController {

    @Autowired
    IEmployeeService employeeService;

    @GetMapping("/employees")
    public List<Employee> getAllEmployee()
    {
        return employeeService.getAll();
    }
}

成功获取到数据
在这里插入图片描述

3. 整合阿里Druid连接池

在开始之前先说一下为什么要使用阿里Druid连接池,它有什么优点?

  • 提供强大的监控功能(监控执行的SQL语句以及SQL语句的执行状态、监控Session、查看连接配置等等)
  • 运行稳定,在高并发场景中得到了验证

3.1 引入Druid依赖

在Maven仓库中直接以Druid为关键字进行搜索,Druid Spring Boot Starter就是我们要找的依赖
在这里插入图片描述
将其添加进项目中

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.21</version>
</dependency>

至此,成功引入了Druid依赖

3.2 切换连接池为Druid连接池

直接指定spring.datasource.type即可,将其指定为DruidDataSource

spring:
  datasource:
    username: root
    password: 123
    #mysql8以上的驱动包需要指定以下时区
    url: jdbc:mysql://127.0.0.1:23306/mybatis?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

3.3 配置Druid

通过配置spring.datasource.druid属性配置Druid

	druid:
	  # 初始连接个数
      initial-size: 5
      # 最小连接池数量
      min-idle: 5
      # 最大连接池数量
      max-active: 20
      # 获取连接时最大等待时间,单位为毫秒
      max-wait: 5000
      # 状态监控
      filter:
        stat:
          # 使能状态监控
          enabled: true
          db-type: mysql
          log-slow-sql: true
          slow-sql-millis: 2000
      # 监控过滤器
      web-stat-filter:
        enabled: true
        # 配置过滤器不拦截哪些uri
        exclusions:
          - "*.js"
          - "*.gif"
          - "*.jpg"
          - "*.png"
          - "*.css"
          - "*.ico"
          - "/druid/*"
      # druid 监控页面
      stat-view-servlet:
        enabled: true
        # 配置监控界面uri
        url-pattern: /druid/*
        reset-enable: false
        # 配置登陆用户名
        login-username: root
        # 配置登陆密码
        login-password: root

在上面的配置文件中:

  • 配置druid.filter.stat的目的是为了实现监控统计功能
  • 监控界面的uri为/druid
  • 登陆用户名为root、登陆密码为root

3.4 数据监控

访问Druid监控页面
在这里插入图片描述
可以看到数据库配置信息
在这里插入图片描述
监控web应用
在这里插入图片描述
监控执行的SQL语句
在这里插入图片描述
至此,Druid整合完毕。

4. 整合Mybatis配置文件版

Mybatis配置文件版与注解版在整合上的区别:

  • 不在Mapper类中写SQL语句,SQL语句写在映射文件中
  • 指定Mybatis核心配置文件的路径和映射文件路径

4.1 改写一下映射接口

@Mapper
public interface EmployeeMapper {

    //根据id查找单个
    Employee getById(Integer id);

    //查找全部
    List<Employee> getAll();

    //添加
    int add(Employee employee);

    int update(Employee employee);

    int delete(Integer id);

}

4.2 创建Mybatis核心配置文件

在resources下新建mybatis文件夹,并在mybatis文件夹下新创建mybatis-config.xml文件
在这里插入图片描述
在新创建的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>

</configuration>

至此,mybatis核心配置文件创建完毕

4.3 创建Mybatis映射接口配置文件

在resources/mybatis目录下创建mapper文件夹,专门存放映射配置文件,并在mapper目录下创建employeeMapper.xml文件:
在这里插入图片描述
employee文件中填入以下内容:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--
    这里的namespace对应接口的全路径名,这样Mapper配置文件和Mapper接口会自动绑定
-->
<mapper namespace="com.springboot.mapper.EmployeeMapper">

    <!--
        数据库中的字段名和对象的属性名一样时,可以省略resultMap
    -->
    <resultMap type="com.springboot.entity.Employee" id="employeeMap">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="position" property="position"/>
        <result column="salary" property="salary"/>
        <result column="department" property="department"/>
    </resultMap>

    <!--
        id和接口方法名写成一致,这样可以自动绑定
    -->
    <select id="getById" resultMap="employeeMap">
		select * from employee where id = #{id}
	</select>

    <select id="getAll" resultMap="employeeMap">
	    select * from employee
	</select>

    <insert id="add">
        insert into employee(name,position,salary,department) values(#{name},#{position},#{salary},#{department})
    </insert>

    <update id="update">
        update employee set name = #{name},position = #{position},salary = #{salary}, department = #{department} where id = #{id}
    </update>

    <delete id="delete">
        delete from employee where id = #{id}
    </delete>
</mapper>
  • mapper配置文件的namespace指定为映射接口的全路径名,则两者将自动绑定
  • 数据库中的字段名和对象的属性名一样时,可以省略resultMap,此时需要将resultMap="employeeMap"改为resultType=“Employee对象的全类名”
  • 映射语句的id和接口方法名写成一致,这样便可以自动绑定

4.4 指定Mybatis配置文件路径

#配置Mybatis相关映射文件路径
mybatis:
  #映射配置文件路径
  mapper-locations: classpath:mybatis/mapper/*.xml
  #核心配置文件路径
  config-location: classpath:mybatis/mybatis-config.xml

全部测试通过,完成!
在这里插入图片描述

5. 事务

5.1 什么是事务,为什么要使用事务

以转账为例,A给B转账10元:
第一步:先从数据库中把A的余额-10
第二步:再从数据库中把B的余额+10

假设,执行第一步成功了,但执行第二步失败了(或执行第一步和第二步中间的某条语句时失败了,导致根本执行不到第二步)。那将会造成A的余额减少了,但B的余额没有增加的问题。

因为中间某条可能发生的错误,导致整个业务逻辑没有正确执行完。之前成功操作的数据并不可靠,需要将其回退。

事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。

我们在往数据库中写数据时,一般都要用到事务管理,Spring对此提供了良好的支持。

5.2 使用事务

使用Spring事务管理只需以下两个步骤:

  1. 在Spring启动类使用@EnableTransactionManagement开启注解事务管理
  2. 在 Service层方法上添加 @Transactional 进行事务管理

开启注解事务管理

@SpringBootApplication
@EnableTransactionManagement
public class SpringMybatis01Application {

    public static void main(String[] args) {
        SpringApplication.run(SpringMybatis01Application.class, args);
    }

}

进行事务管理

@Transactional
public void addMulEmployee()
{
	//假设下面这几个员工必须要一起添加
	Employee employee1 = new Employee("name1","position1",10000D,"department1");
	Employee employee2 = new Employee("name2","position1",10000D,"department1");
	Employee employee3 = new Employee("name3","position1",10000D,"department1");
	Employee employee4 = new Employee("name4","position1",10000D,"department1");
	Employee employee5 = new Employee("name5","position1",10000D,"department1");

	employeeMapper.add(employee1);
	employeeMapper.add(employee2);
	employeeMapper.add(employee3);
	employeeMapper.add(employee4);
	employeeMapper.add(employee5);
}

中间发生异常,前面添加的数据将会回滚,employee1、employee2、employee3将不会出现在数据库中。

employeeMapper.add(employee1);
employeeMapper.add(employee2);
employeeMapper.add(employee3);
//模拟异常情况
int i = 1 / 0;
employeeMapper.add(employee4);
employeeMapper.add(employee5);

注:针对MySQL而言,InnoDB引擎支持事务,MyISAM引擎不支持。

5.3 事务隔离级别

隔离级别是指在发生并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读、不可重复读、幻读。

  • 脏度:事务B读取了事务A未提交的数据,结果事务A回滚了,这就导致事务B读到了不正确的数据,这就是脏读。
  • 不可重复读:事务A读取了数据,然后执行一些逻辑。在此过程中,事务B修改了事务A刚刚读取的数据。当事务A再次读取时,发现数据不匹配了,这就是不可重复读。
  • 幻读:事务A根据查询条件读到了N条数据,事务B插入了M条符合事务A查询条件的数据,导致事务A再次查询就有N+M条数据了,这就是幻读。

接下来看一看Spring为我们提供的几种事务隔离级别

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);

    private final int value;

    private Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

  • DEFAULT:使用数据库的默认隔离级别,大部分数据库使用的隔离级别是READ_COMMITTED
  • READ_UNCOMMITTED:该级别表示事务A可以读取事务B修改但未提交的数据,该隔离级别不能防止脏读和不可重复读。
  • READ_COMMITTED:事务A只能读取事务B已提交的数据,该隔离级别可以防止脏读。
  • REPEATABLE_READ:该隔离级别表示在一个事务执行期间可以多次执行某个相同的查询,每次返回的内容都一样,即使数据有变更,该隔离级别可以防止不可重复读和幻读。
  • SERIALIZABLE:所有事务依次执行,各个事务之间就不可能存在冲突了,该隔离级别可以防止脏读、幻读、不可重复读,但性能很差,一般不用它。

综合考虑,绝大多数情况下使用READ_COMMITTED级别就好。设置方式:

@Transactional(isolation = Isolation.READ_COMMITTED)

对于大多数数据库而言,不用设置就可以,因为默认就是READ_COMMITTED级别

5.4 事务传播行为

概念:在开始一个事务之前,已经存在一个事务。此时可以指定这个要开始的事务的执行行为。
Spring为我们提供了以下事务传播行为:

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}
  • REQUIRED:默认,如果当前存在事务,则加入该事务,否则新创建一个事务。
  • SUPPORTS:如果当前存在事务,则加入该事务,不存在事务,则以非事务方式运行(一般不用)
  • MANDATORY:如果当前存在事务,则加入该事务,如果不存在,则抛出异常。
  • REQUIRES_NEW:创建一个新事务,如果当前存在其他事务,将其他事务挂起(一般不用)。
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在其他事务,则将其他事务挂起(一般不用)。
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常(一般不用)。
  • NESTED:如果当前存在事务,则创建一个新事务,并和原来存在的事务嵌套运行。如果当前不存在事务,则创建一个新事务运行。

事务传播行为采用默认方式最好。如果想要指定,可以通过以下方式指定:

@Transactional(propagation = Propagation.REQUIRED)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值