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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章