以前已經介紹了不少在Spring Boot中使用MySQL的案例,包含了Spring Boot最原始的JdbcTemplate、Spring Data JPA以及咱們國內最經常使用的MyBatis。同時,對於一些複雜場景好比:更換Druid數據源,或是多數據源的狀況也都作了介紹。html
不論咱們使用哪個具體實現框架,都離不開對數據庫表結構的管理。而這一類管理一直都存在一個問題:因爲數據庫表元數據存儲於數據庫中,而咱們的訪問邏輯都存在於Git或其餘代碼倉庫中。Git已經幫助咱們完成了代碼的多版本管理,那麼數據庫中的表該如何作好版本控制呢?java
今天咱們就來介紹在Spring Boot中使用Flyway來管理數據庫版本的方法。mysql
Flyway是一個簡單開源數據庫版本控制器(約定大於配置),主要提供migrate、clean、info、validate、baseline、repair等命令。它支持SQL(PL/SQL、T-SQL)方式和Java方式,支持命令行客戶端等,還提供一系列的插件支持(Maven、Gradle、SBT、ANT等)。git
官方網站:https://flywaydb.org/github
本文對於Flyway的自身功能不作過多的介紹,讀者能夠經過閱讀官方文檔或利用搜索引擎得到更多資料。下面咱們具體說說在Spring Boot應用中的應用,如何使用Flyway來建立數據庫以及結構不一致的檢查。web
下面咱們先預設一個開發目標:spring
目標 1 的實現sql
第一步:建立一個基礎的Spring Boot項目,並在pom.xml
中加入Flyway、MySQL鏈接和數據訪問相關的必要依賴(這裏選用spring-boot-starter-jdbc
做爲例子)數據庫
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
第二步:按Flyway的規範建立版本化的SQL腳本。數據結構
src/main/resources
目錄下建立db
目錄,在db
目錄下再建立migration
目錄migration
目錄下建立版本化的SQL腳本V1__Base_version.sql
DROP TABLE IF EXISTS user ; CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(20) NOT NULL COMMENT '姓名', `age` int(5) DEFAULT NULL COMMENT '年齡', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:若是你不想將SQL腳本放到其餘目錄,能夠用spring.flyway.locations
參數來配置。這裏不一樣於1.x版本的配置項flyway.locations
第三步:根據User表的結構,編寫對應的實體定義
@Data @NoArgsConstructor public class User { private Long id; private String name; private Integer age; }
第四步:編寫用戶操做接口和實現
public interface UserService { /** * 新增一個用戶 * * @param name * @param age */ int create(String name, Integer age); /** * 根據name查詢用戶 * * @param name * @return */ List<User> getByName(String name); /** * 根據name刪除用戶 * * @param name */ int deleteByName(String name); /** * 獲取用戶總量 */ int getAllUsers(); /** * 刪除全部用戶 */ int deleteAllUsers(); } @Service public class UserServiceImpl implements UserService { private JdbcTemplate jdbcTemplate; UserServiceImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public int create(String name, Integer age) { return jdbcTemplate.update("insert into USER(NAME, AGE) values(?, ?)", name, age); } @Override public List<User> getByName(String name) { List<User> users = jdbcTemplate.query("select * from USER where NAME = ?", (resultSet, i) -> { User user = new User(); user.setId(resultSet.getLong("ID")); user.setName(resultSet.getString("NAME")); user.setAge(resultSet.getInt("AGE")); return user; }, name); return users; } @Override public int deleteByName(String name) { return jdbcTemplate.update("delete from USER where NAME = ?", name); } @Override public int getAllUsers() { return jdbcTemplate.queryForObject("select count(1) from USER", Integer.class); } @Override public int deleteAllUsers() { return jdbcTemplate.update("delete from USER"); } }
這裏主要介紹Flyway的應用,因此採用這種比較簡單的編寫方式,實際項目應用中,仍是推薦MyBatis的具體操做實現。
第五步:編寫測試用例
@Slf4j @SpringBootTest public class Chapter311ApplicationTests { @Autowired private UserService userSerivce; @Test public void test() throws Exception { userSerivce.deleteAllUsers(); // 插入5個用戶 userSerivce.create("Tom", 10); userSerivce.create("Mike", 11); userSerivce.create("Didispace", 30); userSerivce.create("Oscar", 21); userSerivce.create("Linda", 17); // 查詢名爲Oscar的用戶,判斷年齡是否匹配 List<User> userList = userSerivce.getByName("Oscar"); Assertions.assertEquals(21, userList.get(0).getAge().intValue()); // 查數據庫,應該有5個用戶 Assertions.assertEquals(5, userSerivce.getAllUsers()); // 刪除兩個用戶 userSerivce.deleteByName("Tom"); userSerivce.deleteByName("Mike"); // 查數據庫,應該有5個用戶 Assertions.assertEquals(3, userSerivce.getAllUsers()); } }
注意因爲Spring Boot 2.4應用的junit版本與以前Spring Boot 1.x版本中的不一樣,所以單元測試的編寫略有區別,有興趣的讀者能夠分別查看以前介紹文章和這篇文章中的單元測試的區別,這裏就不細說了。
第六步:運行上面編寫的單元測試,驗證一下效果。
不出意外,單元測試運行ok的話
連上數據庫看看。此時應該多出了這兩張表:
user
表就是咱們維護在SQL腳本中要建立的表flyway_schema_history
表是flyway的管理表,用來記錄在這個數據庫上跑過的腳本,以及每一個腳本的檢查依據。這樣每次應用啓動的時候,就能夠知道哪一個腳本須要運行,或者哪一個腳本發生了變更,運行基礎可能不對,形成數據結構的混亂而阻止運行。目標 2 的實現
有了上面的基礎以後,咱們來講說後續要作表結構的表變更該怎麼操做,這也是以前讀者出現問題最多的狀況,因此在2.x版本教程中特意講一講。
首先,你們在開始使用Flyway以後,對於數據庫表接口的變動就要關閉這幾個途徑:
正確的表結構調整途徑:在flyway腳本配置路徑下編寫新的腳本,啓動程序來執行變動。這樣能夠得到幾個很大的好處:
下面根據一個實際需求來具體操做下。假設咱們如今想對User表增長一個字段:address,用來存儲用戶的通信地址,那麼咱們就須要這樣操做實現。
第一步:建立腳本文件V1_1__alter_table_user.sql
,並寫入增長address
列的語句
ALTER TABLE `user` ADD COLUMN `address` VARCHAR(20) DEFAULT NULL;
對於腳本文件名的基本規則是:
版本號__描述.sql
。固然若是你有更細緻的要求,那麼能夠作更細緻的文件名規劃,具體細節讀者能夠查閱文末參考資料中的官方文檔獲取。
第二步:再次執行單元測試,在控制檯中能夠看到以下日誌:
2021-01-11 16:58:12.025 INFO 37330 --- [ main] o.f.c.i.database.base.DatabaseType : Database: jdbc:mysql://localhost:3306/test (MySQL 8.0) 2021-01-11 16:58:12.063 INFO 37330 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.020s) 2021-01-11 16:58:12.075 INFO 37330 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema `test`: 1 2021-01-11 16:58:12.082 INFO 37330 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema `test` to version "1.1 - alter table user" 2021-01-11 16:58:12.113 INFO 37330 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 1 migration to schema `test` (execution time 00:00.045s)
再查看一下數據中國的內容:
若是你尚未體會到引入Flyway對給咱們的表結構帶來的好處的話,不妨也留言分享下大家的管理方式吧!
更多本系列免費教程連載「點擊進入彙總目錄」
本文的相關例子能夠查看下面倉庫中的chapter3-11
目錄:
若是您以爲本文不錯,歡迎Star
支持,您的關注是我堅持的動力!
歡迎關注個人公衆號:程序猿DD,得到獨家整理的免費學習資源助力你的Java學習之路!另每週贈書不停哦~