1. 程式人生 > >SpringBoot31 整合SpringJDBC、整合MyBatis、利用AOP實現多資料來源切換

SpringBoot31 整合SpringJDBC、整合MyBatis、利用AOP實現多資料來源切換

 

一、整合SpringJDBC

1  JDBC

  JDBC(Java Data Base Connectivity,Java 資料庫連線)是一種用於執行 SQL 語句的 Java API,可以為多種關係資料庫提供統一訪問,它一組用 Java 語言編寫的類和介面組成。JDBC 提供了一種基準,據此可以構建更高階的工具和介面,使資料庫開發人員能夠編寫資料庫應用程式。

  1.1 優點

    JDBC 就是一套 Java 訪問資料庫的 API 規範,利用這套規範遮蔽了各種資料庫 API 呼叫的差異性;

    當 Java 程式需要訪問資料庫時,直接呼叫 JDBC API 相關程式碼進行操作,JDBC 呼叫各類資料庫的驅動包進行互動,最後資料庫驅動包和對應的資料庫通訊,完成 Java 程式操作資料庫。

  1.2 缺點

    直接在 Java 程式中使用 JDBC 比較複雜,需要 7 步才能完成資料庫的操作:

      載入資料庫驅動 -> 建立資料庫連線 -> 建立資料庫操作物件 -> 編寫SQL語句 -> 利用資料庫操作物件執行資料庫操作 -> 獲取並操作結果集 -> 關閉連線物件和操作物件,回收資源

    利用JDBC操作資料庫參考博文

  1.3 新技術

    由於JDBC操作資料庫非常複雜,所以牛人們編寫了很多ORM框架,其中Hibernate、Mybatis、SpringJDBC最流行;

    時間又過了N年,

    又有牛人在Hibernate的基礎上開發了SpringDataJPA,在Mybatis的基礎上開發出了MybatisPlus。

 

2 SpringBoot整合SpringJDBC環境搭建

  2.1 建立一個SpringBoot專案

    引入 spring-boot-starter-web 、spring-boot-starter-jdbc、mysql-connector-java 者三個主要依賴;

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
必要依賴

    再引入 devtools、lombok這兩個輔助依賴。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
輔助依賴

    2.1.1 依賴說明   

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xunyji</groupId>
    <artifactId>spring_jdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring_jdbc</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
pom.xml

      檢視pom.xml依賴圖可以知道 spring-boot-starter-jdbc 依賴了spring-jdbc、HikariCP;

      spring-jdbc主要提供JDBC操作相關的介面,HikariCP就是傳說中最快的連線池。

  2.2 資料庫準備

    2.2.1 建立資料表

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `name` varchar(32) DEFAULT NULL COMMENT '使用者名稱',
  `password` varchar(32) DEFAULT NULL COMMENT '密碼',
  `age`  int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
users

    2.2.1 配置資料來源資訊

      坑:Spring Boot 2.1.0 中,com.mysql.jdbc.Driver 已經過期,推薦使用 com.mysql.cj.jdbc.Driver。

      技巧:IDEA是可以連線資料庫的喲,而且還可以反向生成對應的實體類喲;IDEA連線資料庫並生成實體列參考博文

spring.datasource.url=jdbc:mysql://localhost:3306/testdemo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=****
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

    2.2.3 建立實體類

      版案例使用了lombok進行簡化編寫

package com.xunyji.spring_jdbc.model.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author 王楊帥
 * @create 2018-11-18 10:43
 * @desc
 **/
@Data // 自動生成get、set、toString、equals、hashCode、canEaual方法 和 顯示無參構造器
@Builder // 生成builder方法
@NoArgsConstructor // 生成無參構造器
@AllArgsConstructor // 自動生成所有欄位的有參構造器,會覆蓋無參構造器
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
User.java

  2.3 編寫持久層

    2.3.1 持久層介面

package com.xunyji.spring_jdbc.repository;

import com.xunyji.spring_jdbc.model.entity.User;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author 王楊帥
 * @create 2018-11-18 10:53
 * @desc user表對應的持久層介面
 **/
public interface UserRepository {
    Integer save(User user);
    Integer update(User user);
    Integer delete(Integer id);
    List<User> findAll();
    User findById(Integer id);
}
UserRepository.java

    2.3.2 持久層實現類

      技巧:在實現類中依賴注入 JdbcTemplate,它是Spring提供的用於JDBC操作的工具類。

    @Autowired
    private JdbcTemplate jdbcTemplate;
package com.xunyji.spring_jdbc.repository.impl;

import com.xunyji.spring_jdbc.comm.exception.ExceptionEnum;
import com.xunyji.spring_jdbc.comm.exception.FuryException;
import com.xunyji.spring_jdbc.model.entity.User;
import com.xunyji.spring_jdbc.repository.UserRepository;
import com.xunyji.spring_jdbc.repository.impl.resultmap.UserRowMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author 王楊帥
 * @create 2018-11-18 10:55
 * @desc user表對應的持久層實現類
 **/
@Repository
public class UserRepositoryImpl implements UserRepository {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Boolean save(User user) {
        Integer saveResult = jdbcTemplate.update(
                "INSERT user (name, age, email) VALUES (?, ?, ?)",
                user.getName(), user.getAge(), user.getEmail()
        );
        if (saveResult.equals(1)) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean update(User user) throws Exception {
        Integer updateResult = jdbcTemplate.update(
                "UPDATE user SET name = ?, age = ?, email = ? WHERE id = ?",
                user.getName(), user.getAge(), user.getEmail(), user.getId()
        );
        if (updateResult.equals(1)) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean delete(Long id) {
        Integer deleteResult = jdbcTemplate.update(
                "DELETE FROM user WHERE id = ?",
                id
        );
        if (deleteResult.equals(1)) {
            return true;
        }
        return false;
    }

    @Override
    public List<User> findAll() {
        return jdbcTemplate.query(
                "SELECT * FROM user",
                new UserRowMapper()
        );
    }

    @Override
    public User findById(Long id) {
        try {
            User user = jdbcTemplate.queryForObject(
                    "SELECT id, name, age, email FROM user WHERE id = ?",
                    new Object[]{id},
                    new BeanPropertyRowMapper<>(User.class)
            );
            return user;
        } catch (EmptyResultDataAccessException e) {
            log.info(e.getMessage());
            throw new FuryException(ExceptionEnum.RESULT_IS_EMPTY);
        }
    }
}
UserRepositoryImpl .java

      技巧:新增、更新、刪除都是利用JdbcTemplate的update方法,update方法的返回值是執行成功的記錄數(需在實現類中根據結果判斷是否操作成功);

      技巧:利用JdbcTemplate的queryForObject方法查詢單條記錄,利用JdbcTemplate的query查詢多條記錄;

      技巧:利用JdbcTemplate的queryForObject方法查詢單條記錄時如果查詢不到就會丟擲EmptyResultDataAccessException,需要進行捕獲。

      技巧:查詢記錄時需要對結果集進行封裝,可以直接利用BeanPropertyRowMapper例項進行封裝,或者自定義一個實現了UserRowMapper的實現類

    2.3.3 持久層測試類

package com.xunyji.spring_jdbc.repository.impl;

import com.xunyji.spring_jdbc.model.entity.User;
import com.xunyji.spring_jdbc.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class UserRepositoryImplTest {


    @Autowired
    private UserRepository userRepository;

    private User user;

    @Before
    public void init() {
        user = User.builder()
                .id(81L)
                .age(33)
                .name("warrior")
                .email("[email protected]")
                .build();
    }

    @Test
    public void save() throws Exception {
        Boolean saveNumber = userRepository.save(user);
        log.info("新增結果為:" + saveNumber);
    }

    @Test
    public void update() throws Exception {
        Boolean updateResult = userRepository.update(user);
        log.info("更新結果為:" + updateResult);
    }

    @Test
    public void delete() throws Exception {
        Boolean delete = userRepository.delete(81L);
        log.info("刪除結果為:" + delete);
    }

    @Test
    public void findById() throws Exception {
        User byId = userRepository.findById(8L);
        log.info("獲取的資料資訊為:" + byId);
    }

    @Test
    public void findAll() throws Exception {
        List<User> all = userRepository.findAll();
        log.info("獲取到的列表資料為:" + all);
        all.stream().forEach(System.out::println);
    }

}
UserRepositoryImplTest.java

  2.4 編寫服務層

    待更新...

  2.5 編寫控制層

    待更新...

3 多資料來源

  3.1 硬編碼實現【不推薦】

  3.2 AOP實現【推薦】

     

      

二、整合MyBatis