领先一步
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
中的)。
接下来,我们需要声明一个存储库。最简单的方法是扩展 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 方法无法走得太远。我们决定将查询推导(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
并再次保存它会在数据库中创建另一行。
我们正在进行不区分大小写的 (like) 搜索,因此,我们找到了“Albert”和“Bertram”,但没有找到“Beth”。
关于 Spring Data JDBC,还有更多内容需要学习。继续阅读 Spring Data JDBC 引用和聚合
或者您可以 查看示例、文档,当然还有 源代码。如果您有任何疑问,请 在 StackOverflow 上提问。如果您发现错误或想要请求功能,请 创建问题。