知識庫 / Spring / Spring Boot RSS 訂閱

Spring Boot 初步數據加載快速指南

Spring Boot,Spring Persistence
HongKong
4
02:12 PM · Dec 06 ,2025

1. 概述

Spring Boot 極大地簡化了數據庫變更的管理。如果未採用默認配置,它將會在我們的包中搜索實體並自動創建相應的表。

但有時我們需要對數據庫變更進行更精細的控制。這時,我們可以使用 Spring 中的 data.sqlschema.sql 文件。

2. 數據文件 data.sql 文件

讓我們在此假設我們正在使用 JPA,並在項目中定義一個簡單的 Country 實體:

@Entity
public class Country {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;
    
    @Column(nullable = false)
    private String name;

    //...
}

如果運行我們的應用程序,Spring Boot 將為我們創建一個空表,但不會填充任何內容。

一種簡單的方法是創建一個名為 data.sql 的文件:

INSERT INTO country (name) VALUES ('India');
INSERT INTO country (name) VALUES ('Brazil');
INSERT INTO country (name) VALUES ('USA');
INSERT INTO country (name) VALUES ('Italy');

默認情況下,data.sql 腳本會在 Hibernate 初始化之前執行。我們需要 Hibernate 在創建表並插入數據之前完成初始化。為了實現這一點,我們需要延遲數據源的初始化。我們將使用下面的屬性來實現:

spring.jpa.defer-datasource-initialization=true

當我們使用此文件作為類路徑運行項目時,Spring 會將其拾取並使用它來填充 country 表。

請注意,對於任何基於腳本的初始化,即通過 data.sql 或創建模式 schema.sql (我們將稍後學習) 插入數據,我們需要設置以下屬性:

spring.sql.init.mode=always

對於嵌入式數據庫,如H2,默認情況下設置為始終

3. schema.sql 文件

有時,我們不希望依賴默認的模式創建機制。

在這種情況下,我們可以創建自定義的 schema.sql 文件:

create table USERS(
  ID int not null AUTO_INCREMENT,
  NAME varchar(100) not null,
  STATUS int,
  PRIMARY KEY ( ID )
);

Spring 將會拾取該文件並將其用於創建模式。

當我們使用包含該文件的類路徑運行項目時,雖然 Users 表未作為項目中的實體存在,但 Spring 仍然通過讀取 schema.sql 文件創建了 Users 表,存在於數據庫中。

請注意,如果我們在基於腳本的初始化中進行操作,即通過 schema.sqldata.sql 以及 Hibernate 初始化,那麼同時使用它們可能會導致一些問題。

為了解決這個問題,我們可以通過 Hibernate 禁用 DDL 命令的執行,Hibernate 用於表創建/更新:

spring.jpa.hibernate.ddl-auto=none

這將確保僅使用 schema.sql 執行基於腳本的模式生成。

如果仍然希望同時使用 Hibernate 自動模式生成與基於腳本的模式創建和數據填充,則需要使用:

spring.jpa.defer-datasource-initialization=true

這將確保在執行 Hibernate 模式創建後,還會讀取 schema.sql 進行任何額外的模式更改,並進一步執行 data.sql 以填充數據庫。

如前一節所述,基於腳本的初始化默認僅對嵌入式數據庫執行。要始終使用腳本初始化數據庫,我們需要使用:

spring.sql.init.mode=always

請參考官方 Spring 文檔關於 使用 SQL 腳本初始化數據庫 的內容。

4. 使用Hibernate控制數據庫創建

Spring 提供一個專門用於 Hibernate 的 JPA 屬性,用於生成 DDL:spring.jpa.hibernate.ddl-auto

標準的 Hibernate 屬性值有 create, update, create-drop, validatenone

  • create – Hibernate 首先刪除現有的表,然後創建新的表。
  • update – 基於映射(註解或 XML)的對象模型與現有模式進行比較,然後 Hibernate 根據差異更新模式。即使不再需要現有表或列,它也不會刪除這些表或列。
  • create-drop – 類似於 create,但增加了 Hibernate 在所有操作完成後會刪除數據庫的特性;通常用於單元測試。
  • validate – Hibernate 只驗證表和列是否存在;否則,它會拋出異常。
  • none – 此值有效地關閉了 DDL 生成。

Spring Boot 內部默認此參數值為 create-drop,如果沒有檢測到 schema manager,否則為所有其他情況下的 none

我們必須小心地設置此值或使用其他機制來初始化數據庫。

5. 自定義數據庫模式創建

默認情況下,Spring Boot 會自動創建嵌入式 DataSource 的模式。

如果需要控制或自定義此行為,我們可以使用 spring.sql.init.mode 屬性。該屬性可以取三個值:

  • always – 始終初始化數據庫
  • embedded – 如果使用嵌入式數據庫,則始終初始化。如果未指定屬性值,則此選項為默認值。
  • never – 從未初始化數據庫

值得注意的是,如果我們在使用非嵌入式數據庫,例如 MySQL 或 PostgreSQL,並且希望初始化其模式,則必須將此屬性設置為 always

此屬性在 Spring Boot 2.5.0 版本中引入的;如果使用 Spring Boot 的舊版本,則需要使用 spring.datasource.initialization-mode

6. 使用 @Sql 註解

Spring 還提供了 @Sql 註解——一種聲明式的方式來初始化和填充我們的測試模式。

以下是 @Sql 註解的屬性:

  • config – 用於 SQL 腳本的本地配置。我們在下一部分中將詳細討論它。
  • executionPhase – 還可以指定執行 SQL 腳本的時間。
  • statements – 可以聲明用於執行的 inline SQL 語句。
  • scripts – 可以聲明 SQL 腳本文件的路徑以進行執行。這相當於 value 屬性。

@Sql 註解可以用於類級別或方法級別。

6.1. 在類級別使用 <em @Sql> 註解

可以使用 > 註解在類級別聲明,以為測試填充數據。

下面展示如何使用 > 註解創建新表並加載初始數據,以供集成測試使用:

@Sql({"/employees_schema.sql", "/import_employees.sql"})
public class SpringBootInitialLoadIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void testLoadDataForTestClass() {
        assertEquals(3, employeeRepository.findAll().size());
    }
}

在上述代碼中,我們定義了兩個在測試方法之前執行的 SQL 腳本。@Sql 聲明利用了默認的 BEFORE_TEST_METHOD 執行階段。

Spring 6.1 和 Spring Boot 3.2.0 版本引入了對 executionPhase 參數的類級別支持,並提供了 BEFORE_TEST_CLASSAFTER_TEST_CLASS 常量,用於確定腳本是否在測試類之前或之後運行。

讓我們更新 SpringBootInitialLoadIntegrationTest 類並顯式定義執行階段:

@Sql(scripts = {"/employees_schema.sql", "/import_employees.sql"}, executionPhase = BEFORE_TEST_CLASS)
public class SpringBootInitialLoadIntegrationTest { 
// ...  
}

在這裏,我們通過將 executionPhase 的值設置為 BEFORE_TEST_CLASS 來運行 SQL 腳本,在測試類之前。

此外,AFTER_TEST_CLASS 執行階段有助於在測試類之後加載 SQL 腳本。這在我們需要在測試後清除數據庫時可能很有用。

@Sql(scripts = {"/delete_employees_data.sql"}, executionPhase = AFTER_TEST_CLASS)
public class SpringBootInitialLoadIntegrationTest {
// ...
}

值得注意的是,這種配置不能被方法級別的腳本和語句覆蓋。相反,該腳本將在執行方法級別的腳本和語句的同時執行。

6.2. 在方法級別使用 @Sql 註解

我們將通過註解該方法來加載特定測試用例所需的額外數據:

@Test
@Sql({"/import_senior_employees.sql"})
public void testLoadDataForTestCase() {
    assertEquals(5, employeeRepository.findAll().size());
}

在此,SQL腳本在測試方法執行之前被執行。

再次,我們可以使用 BEFORE_TEST_METHODAFTER_TEST_METHOD 常量在方法級別明確定義執行階段:

@Test
@Sql(scripts = {"/import_senior_employees.sql"}, executionPhase = BEFORE_TEST_METHOD)
public void testLoadDataForTestCase() {
    assertEquals(5, employeeRepository.findAll().size());
}

AFTER_TEST_METHOD執行階段有助於在測試方法執行後加載SQL腳本。例如,我們可以利用它在測試方法執行後刪除數據庫表。

默認情況下,在方法級別聲明的@Sql註解會覆蓋類級別聲明的@Sql聲明。在這種情況下,方法級別的@Sql聲明優先於類級別定義的SQL:

@Sql(scripts = {"/employees_schema.sql", "/import_employees.sql"})
public class SpringBootInitialLoadIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test 
    @Sql(scripts = {"/import_senior_employees.sql"})
    public void testLoadDataForTestClass() {
        assertEquals(5, employeeRepository.findAll().size());
   }
}

這裏僅執行 import_seioner_employees.sql,僅在運行測試時才會執行。

但是,我們可以通過使用 @SqlMergeMode 聲明進一步配置此行為,該聲明有助於將方法級別的 @Sql 聲明與類級別的 @Sql 聲明合併。

7. @SqlConfig 

我們可以通過使用@SqlConfig 註解</em title="SqlConfig">來配置我們解析和運行 SQL 腳本的方式。

@SqlConfig 可以聲明在類級別,作為全局配置。或者我們可以使用它來配置特定的@Sql 註解。

讓我們來看一個示例,其中我們還指定了 SQL 腳本的編碼以及執行腳本的事務模式:

@Test
@Sql(scripts = {"/import_senior_employees.sql"}, 
  config = @SqlConfig(encoding = "utf-8", transactionMode = TransactionMode.ISOLATED))
public void testLoadDataForTestCase() {
    assertEquals(5, employeeRepository.findAll().size());
}

讓我們來看一下屬性 @SqlConfig 的各種屬性:

  • blockCommentStartDelimiter – 用於標識 SQL 腳本文件中註釋的起始分隔符
  • blockCommentEndDelimiter – 用於表示 SQL 腳本文件中註釋的結束分隔符
  • commentPrefix – 用於標識 SQL 腳本文件中單行註釋的前綴
  • dataSource – 指定腳本和語句將要運行的 javax.sql.DataSource bean 的名稱
  • encoding – SQL 腳本文件的編碼;默認值為平台編碼
  • errorMode – 在運行腳本時遇到錯誤時使用的模式
  • separator – 用於分隔單個語句的字符串;默認值為 “–“
  • transactionManager – 指定將用於事務的 PlatformTransactionManager bean 的名稱
  • transactionMode – 在事務模式下執行腳本時使用的模式

8.  

Java 8 及更高版本允許使用重複註解。我們可以利用此功能來處理 註解。對於 Java 7 及以下版本,存在一個容器註解——

使用 註解,我們將聲明多個 註解:

@SqlGroup({
  @Sql(scripts = "/employees_schema.sql", 
    config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)),
  @Sql("/import_employees.sql")})
public class SpringBootSqlGroupAnnotationIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void testLoadDataForTestCase() {
        assertEquals(3, employeeRepository.findAll().size());
    }
}

9. 結論

在本文中,我們瞭解到如何利用 <em>schema.sql</em><em>data.sql</em> 文件來設置初始模式並填充數據。

我們還探討了如何使用 <em>@Sql</em><em>@SqlConfig</em><em>&nbsp;@SqlGroup&nbsp;</em> 註解來加載測試數據。

請注意,這種方法更適合基本和簡單的場景,任何高級數據庫處理都需要更高級和精細的工具,例如 Liquibase 或 Flyway。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.