引入 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 社区的所有近期活动。

查看全部