快人一步
VMware 提供培訓和認證,助你加速前進。
瞭解更多隨著即將釋出的 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();
}
}
讓我們逐步過一下配置。
EnableJdbcRepositories
啟用倉庫的建立。由於它需要一些 Bean 的存在,所以我們需要其餘的配置。
繼承 JdbcConfiguration
會向 ApplicationContext
新增一些預設的 Bean。你可以重寫其方法來定製 Spring Data JDBC 的某些行為。目前,我們使用預設實現。
真正重要的部分是 NamedParameterJdbcOperations
,它在內部用於向資料庫提交 SQL 語句。
嚴格來說,事務管理器不是必需的。但那樣你就沒有對跨越單個語句的事務的支援了,沒人想這樣,對吧?
Spring Data JDBC 不直接使用 DataSource
,但是,由於 TransactionManager
和 NamedParameterJdbcOperations
都需要它,將其註冊為一個 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)
}
由於 Java 物件與其對應行之間的關聯僅在於其 Id
和型別,將 Id
設定為 null
並再次儲存會建立資料庫中的另一行。
我們進行的是不區分大小寫(類似)的搜尋,因此我們找到了 "Albert" 和 "Bertram",但沒有找到 "Beth"。
關於 Spring Data JDBC 還有更多內容可學習。請繼續閱讀 Spring Data JDBC References and Aggregates
或者你可以檢視示例、文件,當然還有原始碼。如果你有問題,請在 StackOverflow 上提問。如果你發現了 Bug 或想提出功能請求,請建立 Issue。