SpringBoot 使用JDBC操作資料庫
一、JDBC簡介
JDBC(Java Data Base Connectivity,Java 資料庫連線),說白了 JDBC 就是一套 Java 訪問資料庫的 API 規範;Spring Boot 支援了主流的 ORM 框架:MyBatis、Hibernate 和 Spring JDBC,幾種 ORM 在不同的場景下各有優勢,在 Spring Boot 體系內都有對應的 Starter 包以方便整合
- 直接在Java中使用JDBC的步驟,比價複雜,需要7步完成:
try { // 1、載入資料庫驅動 Class.forName(driver); // 2、獲取資料庫連線 conn = DriverManager.getConnection(url, username, password); // 3、獲取資料庫操作物件 stmt = conn.createStatement(); // 4、定義操作的 SQL 語句 String sql = "select * from user where id = 6"; // 5、執行資料庫操作 rs = stmt.executeQuery(sql); // 6、獲取並操作結果集 while (rs.next()) { // 解析結果集 } } catch (Exception e) { // 日誌資訊 } finally { // 7、關閉資源 }
通過上面的示例可以看出直接使用 JDBC 來操作資料庫比較複雜,因此後期在 JDBC 的基礎上又發展出了很多著名的 ORM 框架,其中最為流行的是 Hibernate、MyBatis 和 Spring JDBC,這裡主要了解一下 Spring JDBC 在 Spring Boot 中的使用
二、單資料來源操作示例
Spring Boot 整合 JDBC 很簡單,需要引入依賴並做基礎配置即可,在開發專案之前需要先建立表,作為專案演示使用。設計一個 User 使用者表,有 id、name、password、age 等欄位,對應的 SQL 指令碼如下:
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;
- 新增配置
- 新增依賴包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
- 演示專案中使用 MySQL 作為資料庫,因此專案中需要引入 MySQL 驅動包,同時引入 spring-boot-starter-jdbc。開啟 pom.xml 檔案,按下快捷鍵:Ctrl + Alt + SHIFT + U,或者單擊右鍵,選擇 Diagrams | Show Dependencies 選項,檢視專案依賴類圖。
- 彈出“類圖”對話方塊後,滾動滑鼠放大檢視,發現 spring-boot-starter-jdbc 直接依賴於 HikariCP 和 spring-jdbc。
-
HikariCP 是 Spring Boot 2.0 預設使用的資料庫連線池,也是傳說中最快的資料庫連線池。
-
spring-jdbc 是 Spring 封裝對 JDBC 操作的工具包。
-
資料來源的配置
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
值得注意的是,在 Spring Boot 2.1.0 中,com.mysql.jdbc.Driver 已經過期,推薦使用 com.mysql.cj.jdbc.Driver。
- 實體類
- 建立表對應的實體類:
public class User {
private Long id;
private String name;
private String password;
private int age;
public User(String name, String password, int age) {
this.name = name;
this.password = password;
this.age = age;
}
// 省略 getter setter
}
- 封裝 Repository
- 建立 UserRepository 定義我們常用的增刪改查介面;
public interface UserRepository {
int save(User user);
int update(User user);
int delete(long id);
List<User> findALL();
User findById(long id);
}
- 建立 UserRepositoryImpl 類實現 UserRepository 類介面;類上使用 @Repository 註解用於標註資料訪問元件,同時在類中注入 JdbcTemplate,其是 Spring 操作 JDBC 提供的工具類。
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
}
- 接下來封裝儲存使用者/更新使用者資訊/刪除使用者資訊/使用者id查詢/獲取使用者列表的方法
@Override
public int save(User user) {
return jdbcTemplate.update("INSERT INTO users(name, password, age) values(?, ?, ?)",
user.getName(), user.getPassword(), user.getAge());
}
@Override
public int update(User user) {
return jdbcTemplate.update("UPDATE users SET name = ? , password = ? , age = ? WHERE id=?",
user.getName(), user.getPassword(), user.getAge(), user.getId());
}
@Override
public int delete(long id) {
return jdbcTemplate.update("DELETE FROM users where id = ? ",id);
}
@Override
public User findById(long id) {
return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", new Object[] { id }, new BeanPropertyRowMapper<User>(User.class));
}
@Override
public List<User> findALL() {
return jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
// return jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper(User.class));
}
這裡使用了 new BeanPropertyRowMapper<User>(User.class) 對返回的資料進行封裝,它可自動將一行資料對映到指定類的例項中,首先將這個類例項化,然後通過名稱匹配的方式,對映到屬性中去。
findALL() 使用了一個新的方式來封裝結果集的返回,建立一個內部類 UserRowMapper。 UserRowMapper 繼承了 RowMapper,RowMapper 可以將資料中的每一行資料封裝成使用者定義的類,實現 RowMapper 介面覆蓋 mapRow 方法,在 mapRow 方法封裝對資料的返回處理。通過上面程式碼可以看出 UserRowMapper 迴圈遍歷了查詢返回的結果集,遍歷的同時按照屬性進行賦值。這樣在查詢使用時只需要傳入 new UserRowMapper() 即可自動解析返回資料。
class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setAge(rs.getInt("age"));
}
}
- 測試
- 接下里我們對封裝好的 UserRepository 進行測試,測試 UserRepository 中的各個方法是否正確。建立 UserRepositoryTests 類,將 userRepository 注入到類中。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
@Autowired
private UserRepository userRepository;
}
//測試插入資料,直接呼叫 userRepository 對應的 save 方法
@Test
public void testSave() {
User user =new User("neo","123456",30);
userRepository.save(user);
}
//按照上面方式分別測試其他方法...
三、多資料來源的使用
在專案中使用多個數據源是很常見的情況,Spring Boot 中多資料來源的使用需要自行封裝。我們在上面示例專案的基礎上進行改造。
- 配置檔案
- 添加了兩個資料來源,一個是 test1 庫,一個是 test2 庫。
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
- 初始化 JDBC
- 在專案啟動的時候讀取配置檔案中的資訊,並對 JDBC 初始化。
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@ConfigurationProperties(prefix="spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name="primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate (
@Qualifier("primaryDataSource") DataSource dataSource ) {
return new JdbcTemplate(dataSource);
}
@Bean(name="secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
這段程式碼表示在啟動的時候根據特定的字首載入不同的資料來源,根據構建好的資料來源再建立不同的 JDBC。
- UserRepository 改造
- 我們對 UserRepository 中的所有方法進行改造,增加一個引數為 JdbcTemplate,如果方法中傳輸了 JdbcTemplate,方法內就會使用傳遞的 JdbcTemplate 進行操作,如果傳遞的 JdbcTemplate 為空,使用預設的 JdbcTemplate 連線操作。
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private JdbcTemplate primaryJdbcTemplate;
@Override
public int save(User user,JdbcTemplate jdbcTemplate) {
if(jdbcTemplate == null){
jdbcTemplate= primaryJdbcTemplate;
}
return jdbcTemplate.update("INSERT INTO users(name, password, age) values(?, ?, ?)",
user.getName(), user.getPassword(), user.getAge());
}
//其他方法省略,詳細內容可以檢視原始碼
}
- 多資料來源測試
- 測試類中注入了兩個不同資料來源的 JdbcTemplate,同時注入 UserRepository。測試使用不同的 JdbcTemplate 插入兩條資料,檢視兩個資料庫中是否都儲存成功。測試前請先建立 test1 和 test2 資料庫,以及兩個資料庫中的使用者表。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
@Autowired
private UserRepository userRepository;
@Autowired
private JdbcTemplate primaryJdbcTemplate;
@Autowired
private JdbcTemplate secondaryJdbcTemplate;
@Test
public void testSave() {
User user =new User("smile","123456",30);
userRepository.save(user,primaryJdbcTemplate);
userRepository.save(user,secondaryJdbcTemplate);
}
}
執行 testSave() 成功後,登入 test1 和 test 2 資料庫檢視 user 表,都存在一條 name 為 smile 的使用者資訊,說明多資料來源插入資料成功,其他方法的測試大體相同。這樣在專案中,我們想使用哪個資料來源操作資料庫時,只需要傳入資料來源對應的 JdbcTemplate 例項即可。
四、總結
通過本節課程的學習,瞭解到使用原生的 JDBC 操作資料庫非常繁瑣,需要開發者自行封裝資料庫連線,執行完成後手動關閉對應的資源,這樣不利於統一規範,也容易出現問題。後期 Spring 針對 JDBC 的使用推出了 Spring JDBC,Spring Boot 在此基礎上又進行了一步封裝,如今在 Spring Boot 專案中 JDBC 操作資料庫非常