上一教程 介绍了如何使用 jOOQ 实现基本的 CRUD 操作。本文将带你了解如何使用 jOOQ 检索一对一(One-to-One)关系的记录。
你可以在 Github 上找到完整的源码。
一般来说,在显示记录列表时,只会显示记录的最基本的信息,当点击记录时,才会显示记录的完整信息。
在本示例应用中,用户列表只显示 id
、name
和 email
基本信息。当点击详情时,才显示包含用户偏好(Preferences)的完整信息。
更新 findUserById()
方法,以获取用户偏好设置。
首先,创建 UserPreferences
record。
public record UserPreferences(Long id, String theme, String language) {
}
更新 User
类,使其包含 UserPreferences
。
package com.sivalabs.bookmarks.models;
public record User (
Long id,
String name,
String email,
String password,
UserPreferences preferences
) {
public User(Long id, String name, String email, String password) {
this(id, name, email, password, null);
}
public static User create(Long id, String name, String email, String password) {
return new User(id, name, email, password, null);
}
}
在 SQL 中,可以使用 LEFT OUTER JOIN
查询获取关联数据,如下所示:
SELECT u.id, u.name, u.email, u.password, up.id as "preferences_id", up.theme, up.language
FROM users u LEFT OUTER JOIN user_preferences up ON u.preferences_id = up.id
WHERE u.id = 1;
使用 jOOQ 来实现上述查询。
public Optional<User> findUserById(Long id) {
return dsl
.select(
USERS.ID, USERS.NAME, USERS.EMAIL, USERS.PASSWORD,
USER_PREFERENCES.ID, USER_PREFERENCES.THEME, USER_PREFERENCES.LANGUAGE)
.from(USERS.leftOuterJoin(USER_PREFERENCES).on(USERS.PREFERENCES_ID.eq(USER_PREFERENCES.ID)))
.where(USERS.ID.eq(id))
.fetchOptional(record -> new User(
record.get(USERS.ID),
record.get(USERS.NAME),
record.get(USERS.EMAIL),
record.get(USERS.PASSWORD),
new UserPreferences(
record.get(USER_PREFERENCES.ID),
record.get(USER_PREFERENCES.THEME),
record.get(USER_PREFERENCES.LANGUAGE)
)
));
}
现在,更新测试用例来验证上述方法。
package com.sivalabs.bookmarks.repositories;
import com.sivalabs.bookmarks.models.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.jdbc.Sql;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@JooqTest
@Import({UserRepository.class})
@Testcontainers
@Sql("classpath:/test-data.sql")
class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Container
@ServiceConnection
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@Test
void findUserById() {
Optional<User> userOptional = userRepository.findUserById(1L);
assertThat(userOptional).isPresent();
assertThat(userOptional.get().id()).isEqualTo(1L);
assertThat(userOptional.get().name()).isEqualTo("Admin");
assertThat(userOptional.get().email()).isEqualTo("admin@gmail.com");
assertThat(userOptional.get().password()).isEqualTo("admin");
assertThat(user.preferences().id()).isEqualTo(2L);
assertThat(user.preferences().theme()).isEqualTo("Dark");
assertThat(user.preferences().language()).isEqualTo("EN");
}
}
运行测试,执行通过。
使用隐式 JOIN {#使用隐式-join}
jOOQ 生成的代码提供了一种更好的方法,可以使用相关表(如 USERS.userPreferences().ID
、USERS.userPreferences().THEME
等)的隐式 JOIN 来获取一对一关系。
更新 findUserById()
方法,使用隐式 join。
public Optional<User> findUserById(Long id) {
return dsl
.select(
USERS.ID, USERS.NAME, USERS.EMAIL, USERS.PASSWORD,
USERS.userPreferences().ID, USERS.userPreferences().THEME, USERS.userPreferences().LANGUAGE)
.from(USERS)
.where(USERS.ID.eq(id))
.fetchOptional(record -> new User(
record.get(USERS.ID),
record.get(USERS.NAME),
record.get(USERS.EMAIL),
record.get(USERS.PASSWORD),
new UserPreferences(
record.get(USER_PREFERENCES.ID),
record.get(USER_PREFERENCES.THEME),
record.get(USER_PREFERENCES.LANGUAGE)
)
));
}
使 Row Value 表达式 {#使-row-value-表达式}
还可以使用 jOOQ 的 Row Value 表达式 获取一对一关系,如下所示:
public Optional<User> findUserById(Long id) {
return dsl
.select(
USERS.ID, USERS.NAME, USERS.EMAIL, USERS.PASSWORD,
row(
USERS.userPreferences().ID,
USERS.userPreferences().THEME,
USERS.userPreferences().LANGUAGE
).mapping(UserPreferences::new).as("preferences"))
.from(USERS)
.where(USERS.ID.eq(id))
.fetchOptional()
.map(mapping((userId, name, email, password, preferences) ->
new User(userId, name, email, password, preferences)));
}
如果你希望在其他方法(例如 findUserByEmail()
)中获取相同的信息,你可以将 select
子句和结果映射提取到单独的方法中,就像在 第二节中 "实现 findUserById()
" 中所示的那样。
总结 {#总结}
本文介绍了如何在 Spring Boot 中使用 jOOQ 检索一对一关系的记录。
Ref:https://www.sivalabs.in/spring-boot-jooq-tutorial-fetching-one-to-one-associations/