如果您是 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 的官方文档。
<dependencies>
<!-- Additional Spring Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- Jdbi Dependencies -->
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-sqlobject</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-postgres</artifactId>
<version>3.6.0</version>
</dependency>
</dependencies>
添加库是一种轻量级的方式,可以在不向项目添加不必要的依赖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<User> 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<User> 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上找到本教程的所有源代码。