本文一共约1800读时长大概等于一首黑眼豆豆的《Where Is The Love?》。
这篇文章将重点介绍如何在Spring项目中引入Spring Data JPA,并全面配置持久化层。
DAO层的设计中含有很多样板代码,它应该被简化。这种简化的好处有很多:减少我们需要定义和维护的组件数量;维护数据访问模式的一致性;以及维护配置的一致性。
Spring Data将这一简化又向前推进了一步,使得完全删除DAO的实现成为可能。现在DAO的接口是我们唯一需要明确定义的组件。
为了开始利用JPA的Spring Data编程模型,一个DAO接口需要扩展JPA特定的Repository接口JpaRepository
。这将使Spring Data能够找到这个接口并自动为其创建一个实现。 通过扩展接口,我们得到了标准DAO中可用的CRUD方法。
正如上文所讨论的,通过实现Repository的一个接口,DAO将定义和实现一些基本的CRUD方法和查询。
为了定义更具体的访问方法,Spring JPA支持以下选项:
第三个选项,Specification和Querydsl支持,类似于JPA标准,但使用更灵活和方便的API。这使得整个操作的可读性和可重用性大大增强。在处理大量的固定查询时,这种API的优势将变得更加明显,因为我们有可能通过数量较少的可重用代码块来更简洁地表达这些查询。
最后一种方案的缺点是,它要么涉及到XML,要么让实体类承担查询的负担。
当Spring Data创建一个新的Repository实现时,它分析了所有由接口定义的方法,并试图从方法名称中自动生成查询。虽然这有一些局限性,但这是一种非常强大和优雅的方式,只需少量工作就可以定义新的自定义访问方法。
我们可以看一个例子。如果实体有一个名字字段以及Java Bean标准的getter和setter方法,我们将在DAO接口中定义findByName方法。这将自动生成正确的查询:
public interface IFooDAO extends JpaRepository {
Foo findByName(String name);
}
复制代码
这是一个相对简单的例子。查询创建机制支持更多的关键词:
如果解析器不能将该属性与域对象字段相匹配,我们会看到以下异常。
java.lang.IllegalArgumentException: No property nam found for type class com.jayxu.spring.data.persistence.model.Foo
复制代码
现在让我们看看一个自定义查询,我们将通过@Query注解来定义。
@Query("SELECT f FROM Foo f WHERE LOWER(f.name) = LOWER(:name)")
Foo retrieveByName(@Param("name") String name);
复制代码
Spring管理的DAO的实现是隐藏的,因为我们并不直接使用它。然而,它是一个很简单的实现,即SimpleJpaRepository
,它使用注解定义了事务语义。
更明确地说,这在类的层面上使用了一个只读的@Transactional注解,然后对非只读的方法进行重写。其余的事务语义是默认的,但这些可以很容易地被每个方法手动重写。
在Java中,我们通常使用try-catch语句捕获异常,进行异常处理。但有些时候,我们使用try-catch捕获一个异常,但却不进行异常处理,反而是抛出另一个异常,这就称为异常转译。
现在的问题是:由于Spring Data JPA不依赖于旧的ORM模板(JpaTemplate、HibernateTemplate),而且它们从Spring 5开始就被删除了,我们是否还能让我们的JPA异常被翻译成Spring的DataAccessException层次结构?
答案是,我们当然要这样做。通过在DAO上使用@Repository注解,仍然可以实现异常转译。这个注解使Spring Bean后理器能够用容器中发现的所有PersistenceExceptionTranslator实例告知所有@Repository Bean,并像以前一样提供异常转译。
让我们用一个集成测试来验证异常转译。
@Test(expected = DataIntegrityViolationException.class)
public void givenFooHasNoName_whenInvalidEntityIsCreated_thenDataException() {
service.create(new Foo());
}
复制代码
请记住,异常转译是通过代理完成的。为了让Spring能够围绕DAO类创建代理,这些类必须不被声明为final。
为了激活Spring JPA repository的支持,我们可以使用@EnableJpaRepositories注解并指定包含DAO接口的包。
@EnableJpaRepositories(basePackages = "com.jayxu.spring.data.persistence.repository")
public class PersistenceConfig {
...
}
复制代码
我们可以用XML配置做同样的事情。
复制代码
我们将会在新的的文章中详细讨论如何在Spring中配置JPA。Spring Data还利用了Spring对JPA @PersistenceContext注解的支持。它利用这一点将EntityManager连接到负责创建实际DAO实现的Spring工厂Bean(JpaRepositoryFactoryBean)。
除了已经讨论过的配置外,如果我们使用XML,我们还需要包括Spring Data XML配置。
@Configuration
@EnableTransactionManagement
@ImportResource("classpath*:*springDataConfig.xml")
public class PersistenceJPAConfig {
...
}
复制代码
除了Maven对JPA的配置外,我们还需要添加spring-data-jpa依赖。
org.springframework.data
spring-data-jpa
2.4.0
复制代码
我们还可以使用Spring Boot Starter Data JPA依赖,它将自动为我们配置数据源。 我们需要确保我们要使用的数据库存在于classpath中。在我们的例子中,我们已经添加了H2内存数据库。
org.springframework.boot
spring-boot-starter-data-jpa
2.6.1
com.h2database
h2
1.4.200
复制代码
仅仅通过做这些依赖,我们的应用程序就开始运行了,我们可以用它来进行其他的数据库操作。 标准Spring应用程序的配置现在包含在Spring Boot的自动配置中。 当然,我们可以通过添加我们定制的显式配置来修改自动配置。 Spring Boot提供了一种简单的方法,可以使用application.properties文件中的属性来做到这一点。
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
复制代码
在这个例子中,我们改变了连接的URL和用户名密码。
在这篇文章中,我们使用XML和基于Java的配置,介绍了Spring Data JPA的持久层的配置和实现。 我们讨论了如何定义更高级的自定义查询,以及事务和新jpa命名空间的配置。现在Spring可以以一种崭新的、优雅的方式进行数据访问,快试试吧。