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語句 -> 利用資料庫操作物件執行資料庫操作 -> 獲取並操作結果集 -> 關閉連線物件和操作物件,回收資源
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