新的Jdbc client已添加到 Spring Framework 6.1 和 Spring Boot 3.2 中,查询创建更加流畅、更易于阅读和理解。
- 一个非常有趣的功能是自动配置:这意味着我们只需要写下应用程序的 Bean,我们就会有一个Jdbc Client的实例。就这么简单!
- 另一个有趣的功能是可以处理不可变对象(如 Record),这是 Java 14 新增的功能,而过去使用Jdbc Template 无法处理 Record。
让我们开始编写代码!
首先,我创建了一个记录(Record)来代表我们应用程序的数据库实体。
public record Product(Long id, String description) {}
|
然后,我创建了一个 @ProductController 控制器来接收 REST 请求:
@RestController @RequestMapping("/products") public class ProductController {
private ProductService service;
public ProductController(ProductService service) { this.service = service; }
@GetMapping("/{id}") public Product findBy(@PathVariable Long id) { var product = service.findBy(id); if (product.isEmpty()) { throw new ProductNotFoundException(id); } return product.get(); }
@GetMapping public List<Product> findAll() { return service.findAll(); }
@PostMapping public void create(@RequestBody Product product) { service.create(product); }
@DeleteMapping("/{id}") public void deleteBy(@PathVariable Long id) { service.delete(id); }
@PutMapping public void update(@RequestBody Product product) { service.update(product); } }
|
如果你想导入所有端点,只需将 collections.json 文件加载到 Postman、Insomnia 或你最喜欢的 api 客户端。
我们的 @Service 层只是 @Repository 调用的一个抽象,因此本文将省略它。
比较
为了了解 Jdbc 客户端在多大程度上提高了代码的流畅性和可读性,让我们对两者进行一下比较。
首先,让我们看看 ProductRepository 使用以前的 JdbcTemplate模板创建方法时的样子:
@Repository public class ProductRepository {
private final JdbcTemplate jdbcTemplate;
public ProductRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
var rowMapper = (rs, rowNum) -> new Product( rs.getString("id"), rs.getString("description") );
public void create(Product product) { var sql = "INSERT INTO product(id, description) values(?,?)"; return jdbcTemplate.update(sql, product.id(), product.description()); } }
|
如果我们以使用新的 Jdbc 客户端 API 的创建方法为例,它看起来会是这样:
@Repository public class ProductRepository {
private final JdbcClient jdbcClient;
public ProductRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; }
public Integer create(Product product) { return jdbcClient .sql("INSERT INTO product (id, description) VALUES (:id, :description)") .param("id", product.id()) .param("description", product.description()) .update(); } }
|
请注意,sql 方法接收 SQL 命令和要从 SQL 中填写的参数。在 param 方法中,参数的值被传递,这些参数可以被链式调用,从而使调用更流畅、更易读。
完整案例
现在是一个使用 Jdbc 客户端对 ProductRepository 进行 CRUD 的完整示例。
@Repository public class ProductRepository {
private final JdbcClient jdbcClient;
public ProductRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; }
public Optional<Product> findBy(Long id) { return jdbcClient .sql("SELECT id, description FROM product WHERE id = :id") .param("id", id) .query(Product.class) .optional(); }
public List<Product> findAll() { return this.jdbcClient.sql("SELECT id, description FROM product") .query(Product.class).list(); }
public Integer create(Product product) { return jdbcClient .sql("INSERT INTO product (id, description) VALUES (:id, :description)") .param("id", product.id()) .param("description", product.description()) .update(); }
public Integer delete(Long id) { return jdbcClient.sql("DELETE FROM product WHERE id = :id") .param("id", id).update(); }
public Integer update(Product product) { return jdbcClient .sql("UPDATE product SET description = :description WHERE id = :id") .param("description", product.description()) .param("id", product.id()) .update(); } }
|
结论
关于使用 Spring Framework 6.1 和 Spring Boot 3.2 中的 Jdbc Client 进行 CRUD 的介绍到此结束。Spring 带来的 api 简化了复杂的任务,并且易于在 Spring 项目中使用和配置。