如果您是 Java 开发人员并且曾经做过任何应用程序开发,那么您很可能会遇到需要为您的应用程序添加持久层的情况。多年来,这意味着包含重型 ORM,例如 Hibernate/JPA 或一些类似的库,以及随之而来的所有怪癖和挑战。一年前,我的一位同事指导我采用Jdbi
一种新的(对我而言)处理数据的方法,从那时起我就没有回头。
Jdbi 舒适地位于 ORM 库和低级JDBC
驱动程序之间,作为一个直观、简洁和轻量级的库,可以与您的持久层进行交互。Jdbi 不提供任何实体管理、中介服务或魔术来管理您的数据。此外,它不提供自动查询组合(如来自QueryDSL或spring-data-jpa
)、类似于 Hibernate 的 DDL 生成,甚至不提供 Java 应用程序服务器标志性的容器管理事务。
此时,您可能会问自己,“我为什么要牺牲所有这些花哨而有用的功能”?虽然这个问题有很多答案,但有几件事真正吸引了我对 Jdbi 的兴趣,下面重点介绍:
下面的小教程将概述一些简单的步骤,让您开始在自己的 spring 项目中使用 Jdbi。这两种工具的功能都非常丰富。本教程将概述如何在 Spring Boot 应用程序中使用 Jdbi3 进行设置,但不会深入探讨每个功能更强大的功能。请继续关注未来的帖子以获取更多信息。
在开始之前,您需要将几个 Jdbi 依赖项添加到您的pom.xml
. 在本教程中,我们将使用我最喜欢的数据库:Postgres。如果您在为此示例创建数据库时需要帮助,可以参考 Postgres 的官方文档。
-
-
-
org.springframework.boot -
spring-boot-starter-data-jdbc -
-
-
-
org.jdbi -
jdbi3-core -
3.6.0 -
-
-
org.jdbi -
jdbi3-sqlobject -
3.6.0 -
-
-
org.jdbi -
jdbi3-postgres -
3.6.0 -
添加库是一种轻量级的方式,可以在不向项目添加不必要的依赖spring-boot-starter-data-jdbc
项的情况下获得所有方便的 spring-boot 自动配置。DataSource
由于Jdbi
只是一个包装器,JDBC
我们不会添加任何不会使用的东西。
在我们开始使用我们的应用程序之前,我们需要告诉 Spring 数据库的连接信息。实现这一点的最简单方法是在我们的src/main/resources/application.yml
. 一般来说,我会硬编码我需要在本地开发的值,application.yml
然后提供prod
配置文件,或者利用 springs externalized configuration model。
- spring:
- datasource:
- # I previously created a "Role" for my postgres database for this tutorial
- username: jdbi-example-spring-boot
- # When developing locally , I tend not to use passwords for ease of use
- password: ""
- url: jdbc:postgresql://localhost/jdbi-example-spring-boot
- driver-class-name: org.postgresql.Driver
我们要让 Jdbi 对应用程序可用,创建一个可以自动装配到任何需要它的服务的 bean。为此,我们可以创建一个新的配置类并注册必要的 bean。
- @Configuration
- public class JdbiConfiguration {
-
- @Bean
- public Jdbi jdbi(DataSource datasource){
- return Jdbi.create(dataSource)
- .installPlugin(new PostgresPlugin())
- .installPlugin(new SqlObjectPlugin());
- }
- }
由于spring-boot-starter-data-jdbc
不需要自己配置DataSource,而是允许springDataSource
直接将之前定义的bean注入到 jdbi()
Bean定义方法中。最后,我们创建Jdbi
bean 并确保初始化正确的插件,以便Jdbi
了解 Postgresjsonb
使用的特定数据类型和操作(即 Postgres 数据)。
我们将要创建一个简单的 POJO 来表示我们的数据。需要注意的是,这根本不需要与我们的数据模型对应,但可以包含对我们的需求有用的任何内容,而无需更改数据模型。Jdbi 不会像在 Hibernate 中那样自动生成任何数据模型。请注意,@Data
注释来自lombok,这是一个适用于任何 Java 开发人员的方便库!
- @Data
- public class User {
- private Long id;
- private String firstName;
- private String lastName;
- private String phoneNumber
- }
我是 Jdbi 定义持久性交互的声明性方法的忠实粉丝。这种方法使用带有注释组合的接口,然后 Jdbi 可以使用这些接口为您生成 DAO 的实现。通过使用声明性方法,您可以清楚地了解 jdbi 执行代码时究竟发生了什么。
- public interface UserDao {
-
- @Transaction
- @SqlUpdate("CREATE TABLE IF NOT EXISTS users(id BIGINT NOT NULL PRIMARY KEY, first_name VARCHAR(48), last_name VARCHAR(48), phone_number VARCHAR(48))")
- void createUserTable();
-
- @Transaction
- @SqlUpdate("INSERT INTO users(id,first_name,last_name,phone_number) VALUES(:id,:firstName,:lastName,:phoneNumber)")
- void createUser(@BindBean User user);
-
- @SqlQuery("SELECT * FROM users")
- @RegisterBeanMapper(User.class)
- List
getUsers(); -
- @SqlQuery("SELECT * FROM users WHERE id = :id")
- @RegisterBeanMapper(User.class)
- User getUser(@Bind("id") Long id);
-
- }
这里发生了很多事情,但是从注释中很容易准确地理解每种方法试图实现的目标。@Transaction
注释告诉 Jdbi 将特定的方法调用包装在事务中。您可以将其他参数传递给此注释以修改事务生命周期,但是对于我们的目的,默认值很好。这两个注解在外观@SqlQUery
上@SqlUpdate
非常相似,都将SQL
字符串作为参数,但是它们决定了非常不同的行为。@SqlUpdate
用于定义以某种方式更改数据的操作。这可以通过SET
, INSERT
,DELETE
等ALTER
操作。这@SqlQuery
另一方面,注解不能以任何方式修改数据,而只能用于检索数据。
使用 Jdbi,您可以在执行时提供参数化SQL
字符串和绑定方法参数SQL
。上面演示了两种方法(但是 Jdbi 提供了更多的绑定方法)@Bind
和@BindBean
. 注释将@Bind
方法参数映射到 中的特定参数SQL
,而@BindBean
注释将使用getters
bean 的 将其所有属性绑定到SQL
.
最后,Jdbi 提供了将行(甚至连接行)映射到所需 bean 或原始类型的简单方法。上面的示例使用@RegisterBeanMapper(User.class)
注释告诉 Jdbi 使用存在的设置器将返回的行转换为User
对象。需要注意的是,这并不User
像 Hibernate 那样 Proxy 类,而你返回的对象是一个真正的 POJO。如果您需要更多地控制 bean 的映射方式,Jdbi 提供了许多额外的策略来进行行、列和集合级别的映射。
- @RestController
- public class UserController {
-
- private Jdbi jdbi;
-
- public UserController(Jdbi jdbi){
- this.jdbi = jdbi;
- jdbi.useExtension(UserDao.class,UserDao::createUserTable);
- }
-
- @PostMapping("/users")
- public User createUser(@RequestBody User user){
- user.setId(System.currentTimeMillis());
- jdbi.useExtension(UserDao.class, dao -> dao.createUser(user));
- return user;
- }
-
- @GetMapping("/users")
- public List
getUsers(){ - return jdbi.withExtension(UserDao.class, UserDao::getUsers);
- }
-
- @GetMapping("/users/{id}")
- public User getUsers(@PathVariable Long id){
- return jdbi.withExtension(UserDao.class, dao -> dao.getUser(id));
- }
-
- }
Jdbi
在控制器中,我们使用闭包进行交互。在我看来,这是与数据库交互的好方法:它明确定义了连接边界,将持久性逻辑与服务层分开,并保证在闭包之外没有副作用。它清晰、简洁和干净的代码将使您的生活更轻松。
我们在这里使用了两种特定的方法:withExtension
和useExtension
. 这些方法将Dao
接口用作“扩展”的参数,然后是闭包的 lambda 函数或方法引用。闭包传递了一个参数,即dao
从我们的接口创建的实例。withExtension
提供了一种返回值的方法,同时useExtension
允许我们简单地对数据库运行一些东西。Jdbi
当然提供了其他检索和更新数据的方法,完整的分类请参考官方文档。
- @SpringBootApplication
- public class JdbiExampleApplication {
- public static void main(String[] args) {
- SpringApplication.run(JdbiExampleApplication.class, args);
- }
- }
现在您已经设置好了 Spring 应用程序,您可以对其进行测试了!您可以发出一些简单的 curl 请求,以确保您的 REST-API 已准备就绪。
创建一个新用户
- curl -XPOST -H 'Content-Type: application/json' 'http://localhost:8080/users' -d '{"firstName":"Joe","lastName":"Shmo","phoneNumber":"714-832-2211"}'
- {
- "id":1589208108922,
- "firstName":"Maria",
- "lastName":"Magee",
- "phoneNumber":"676332415"
- }
检索所有用户
- curl 'http://localhost:8080/users'
- [
- {
- "id":1589208108922,
- "firstName":"Maria",
- "lastName":"Magee",
- "phoneNumber":"676332415"
- }
- ]
检索单个用户永久链接
- curl 'http://localhost:8080/users/1589208108922'
- {
- "id":1589208108922,
- "firstName":"Maria",
- "lastName":"Magee",
- "phoneNumber":"676332415"
- }
至此,您应该了解如何设置一个简单的 spring-boot 应用程序并Jdbi
为持久层供电!如果您有兴趣了解更多有关Jdbi
提供的惊人功能的信息,可以查看官方文档。如果您想继续学习,可以在GitHub上找到本教程的所有源代码。