引入 Spring Data JDBC

工程 | Jens Schauder | 2018年9月17日 | ...

隨著即將釋出的 Lovelace GA 版本,我們將推出一個新的 Spring Data 模組:Spring Data JDBC

Spring Data JDBC 背後的理念是在訪問關係資料庫時避免 JPA 的複雜性。JPA 提供了諸如延遲載入、快取和髒檢查等功能。雖然這些在你需要時非常有用,但它們實際上會使思考 JPA 及其行為變得比應有的更困難。

延遲載入可能會在你意料之外觸發耗費資源的語句,或者可能丟擲異常而失敗。當你實際上想比較同一個實體的兩個版本時,快取可能會礙事;而髒狀態則使得難以找到一個所有持久化操作都經過的單一入口點。

Spring Data JDBC 旨在提供一個更簡單的模型。它不會有快取、髒檢查或延遲載入。相反,SQL 語句僅在你呼叫倉庫方法時才會發出。方法返回的物件在方法返回之前會被完全載入。沒有“會話”,也沒有實體的代理。所有這些都應該使 Spring Data JDBC 更容易理解和推斷。

當然,這種更簡單的方法也會帶來一些限制,這些將在未來的文章中介紹。此外,這只是第一個版本,所以有很多我們想要並計劃實現的功能,但為了儘快交付產品,我們不得不推遲。

我們來看一個簡單的例子。

首先,我們需要一個實體

class Customer {
    @Id
    Long id;
    String firstName;
    LocalDate dob;
}

注意,你不需要 getter 或 setter。如果你更喜歡使用它們,完全沒問題。實際上,唯一的要求是實體有一個使用 Id 註解的屬性(即 @org.springframework.data.annotation.Id,而不是 javax.persistence 的)。

接下來,我們需要宣告一個倉庫(repository)。最簡單的方法是繼承 CrudRepository

interface CustomerRepository extends CrudRepository<Customer, Long> {}

最後,我們需要配置 ApplicationContext 來啟用倉庫的建立

@Configuration
@EnableJdbcRepositories (1)
public class CustomerConfig extends JdbcConfiguration { (2)

    @Bean
    NamedParameterJdbcOperations operations() { (3)
        return new NamedParameterJdbcTemplate(dataSource());
    }

    @Bean
    PlatformTransactionManager transactionManager() { (4)
        return new DataSourceTransactionManager(dataSource());
	}

    @Bean
    DataSource dataSource(){ (5)
        return new EmbeddedDatabaseBuilder()
                .generateUniqueName(true)
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("create-customer-schema.sql")
                .build();
    }
}

讓我們逐步過一下配置。

  1. EnableJdbcRepositories 啟用倉庫的建立。由於它需要一些 Bean 的存在,所以我們需要其餘的配置。

  2. 繼承 JdbcConfiguration 會向 ApplicationContext 新增一些預設的 Bean。你可以重寫其方法來定製 Spring Data JDBC 的某些行為。目前,我們使用預設實現。

  3. 真正重要的部分是 NamedParameterJdbcOperations,它在內部用於向資料庫提交 SQL 語句。

  4. 嚴格來說,事務管理器不是必需的。但那樣你就沒有對跨越單個語句的事務的支援了,沒人想這樣,對吧?

  5. Spring Data JDBC 不直接使用 DataSource,但是,由於 TransactionManagerNamedParameterJdbcOperations 都需要它,將其註冊為一個 Bean 是確保兩者使用同一例項的簡單方法。

這就是開始使用它所需的一切。現在我們在測試中試用一下

@RunWith(SpringRunner.class)
@Transactional
@ContextConfiguration(classes = CustomerConfig.class)
public class CustomerRepositoryTest {

    @Autowired CustomerRepository customerRepo;

    @Test
    public void createSimpleCustomer() {

        Customer customer = new Customer();
        customer.dob = LocalDate.of(1904, 5, 14);
        customer.firstName = "Albert";

        Customer saved = customerRepo.save(customer);

        assertThat(saved.id).isNotNull();

        saved.firstName = "Hans Albert";

        customerRepo.save(saved);

        Optional<Customer> reloaded = customerRepo.findById(saved.id);

        assertThat(reloaded).isNotEmpty();

        assertThat(reloaded.get().firstName).isEqualTo("Hans Albert");
    }
}

@Query 註解

只使用 CrudRepository 的基本 CRUD 方法可能不足夠。我們決定將查詢派生(Query Derivation),即 Spring Data 根據方法名生成查詢的常用功能,推遲到後續版本。在此之前,你可以使用簡單的 @Query 註解在倉庫方法上指定查詢

@Query("select id, first_name, dob from customer where upper(first_name) like '%' || upper(:name) || '%' ")
List<Customer> findByName(@Param("name") String name);

注意,如果你使用 -parameters 編譯標誌進行編譯,則不需要 @Param 註解。

如果你想執行更新或刪除語句,可以在方法上新增 @Modifying 註解。

讓我們建立另一個測試來嘗試新方法。

@Test
public void findByName() {

    Customer customer = new Customer();
    customer.dob = LocalDate.of(1904, 5, 14);
    customer.firstName = "Albert";

    Customer saved = customerRepo.save(customer);

    assertThat(saved.id).isNotNull();

    customer.id= null; (1)
    customer.firstName = "Bertram";

    customerRepo.save(customer);

    customer.id= null;
    customer.firstName = "Beth";

    customerRepo.save(customer);

    assertThat(customerRepo.findByName("bert")).hasSize(2); (2)
}
  1. 由於 Java 物件與其對應行之間的關聯僅在於其 Id 和型別,將 Id 設定為 null 並再次儲存會建立資料庫中的另一行。

  2. 我們進行的是不區分大小寫(類似)的搜尋,因此我們找到了 "Albert" 和 "Bertram",但沒有找到 "Beth"。

結束語

關於 Spring Data JDBC 還有更多內容可學習。請繼續閱讀 Spring Data JDBC References and Aggregates

或者你可以檢視示例文件,當然還有原始碼。如果你有問題,請在 StackOverflow 上提問。如果你發現了 Bug 或想提出功能請求,請建立 Issue

訂閱 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

快人一步

VMware 提供培訓和認證,助你加速前進。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部