上一教程 介绍了如何使用 jOOQ 检索一对多关系的记录。本文将带你了解如何使用 jOOQ 检索多对多关系的记录。
你可以通过 Github 获取到完整的源码。
在示例数据库中,有 bookmarks
(书签)表和 tags
(标签)表。每个书签可以关联多个标签,反之亦然,因此 bookmarks
表和 tags
表之间存在多对多的关系。
让我们看看如何获取书签列表以及与之关联的标签
首先,创建 BookmarkWithTags
record。
package com.sivalabs.bookmarks.models;
import java.util.List;
public record BookmarkWithTags(Long id, String title, String url, List<TagInfo> tags) { public record TagInfo (Long id, String name){} }
使用 MULTISET Value 构造器获取多对多关系 {#使用-multiset-value-构造器获取多对多关系}
使用 jOOQ 的 MULTISET Value Constructor 来获取书签列表和标签。
实现获取书签和标签,如下:
package com.sivalabs.bookmarks.repositories;
import com.sivalabs.bookmarks.models.BookmarkWithTags; import org.jooq.DSLContext; import org.springframework.stereotype.Repository;
import java.util.List;
import static com.sivalabs.bookmarks.jooq.Tables.BOOKMARK_TAG; import static com.sivalabs.bookmarks.jooq.tables.Bookmarks.BOOKMARKS; import static com.sivalabs.bookmarks.jooq.tables.Tags.TAGS; import static org.jooq.Records.mapping; import static org.jooq.impl.DSL.multiset; import static org.jooq.impl.DSL.select;
@Repository public class BookmarkRepository { private final DSLContext dsl;
public BookmarkRepository(DSLContext dsl) { this.dsl = dsl; }
public List<BookmarkWithTags> getBookmarksWithTags() { return dsl .select( BOOKMARKS.ID, BOOKMARKS.TITLE, BOOKMARKS.URL, multiset( select(TAGS.ID, TAGS.NAME) .from(TAGS) .join(BOOKMARK_TAG) .on(BOOKMARK_TAG.TAG_ID.eq(TAGS.ID)) .where(BOOKMARK_TAG.BOOKMARK_ID.eq(BOOKMARKS.ID)) ).as("tags").convertFrom(r -> r.map(mapping(BookmarkWithTags.TagInfo::new))) ) .from(BOOKMARKS) .fetch(mapping(BookmarkWithTags::new)); }
}
测试数据 {#测试数据}
项目中有以下 src/test/resources/test-data.sql
文件,用于将测试数据插入数据库。
# OMITTING OTHER INSERT STATEMENTS FOR BREVITY
INSERT INTO tags(id, name) VALUES (1, 'java'), (2, 'spring-boot'), (3, 'spring-cloud'), (4, 'devops'), (5, 'security') ;
INSERT INTO bookmarks(id, title, url, created_by, created_at) VALUES (1, 'SivaLabs', 'https://sivalabs.in', 1, CURRENT_TIMESTAMP), (2, 'Spring Initializr', 'https://start.spring.io', 2, CURRENT_TIMESTAMP), (3, 'Spring Blog', 'https://spring.io/blog', 2, CURRENT_TIMESTAMP) ;
insert into bookmark_tag(bookmark_id, tag_id) VALUES (1, 1), (1, 2), (1, 3), (2, 2), (3, 2), (3, 3), (3, 4) ;
测试加载多对多关系记录 {#测试加载多对多关系记录}
编写一个测试用例来验证上述方法。
package com.sivalabs.bookmarks.repositories;
import com.sivalabs.bookmarks.models.BookmarkWithTags; 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 static org.assertj.core.api.Assertions.assertThat;
@JooqTest @Import({BookmarkRepository.class}) @Testcontainers @Sql("classpath:/test-data.sql") class BookmarkRepositoryTest {
@Autowired BookmarkRepository bookmarkRepository;
@Container @ServiceConnection static final PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
@Test void getBookmarksWithTags() { var bookmarksWithTags = bookmarkRepository.getBookmarksWithTags(); assertThat(bookmarksWithTags).hasSize(3);
var javaTag = new BookmarkWithTags.TagInfo(1L, &quot;java&quot;); var springBootTag = new BookmarkWithTags.TagInfo(2L, &quot;spring-boot&quot;); var springCloudTag = new BookmarkWithTags.TagInfo(3L, &quot;spring-cloud&quot;); var devopsTag = new BookmarkWithTags.TagInfo(4L, &quot;devops&quot;);
var bookmark1 = new BookmarkWithTags(1L, &quot;SivaLabs&quot;, &quot;https://sivalabs.in&quot;, List.of(javaTag, springBootTag, springCloudTag)); var bookmark2 = new BookmarkWithTags(2L, &quot;Spring Initializr&quot;, &quot;https://start.spring.io&quot;, List.of(springBootTag)); var bookmark3 = new BookmarkWithTags(3L, &quot;Spring Blog&quot;, &quot;https://spring.io/blog&quot;, List.of(springBootTag, springCloudTag, devopsTag));
assertThat(bookmarksWithTags).contains(bookmark1, bookmark2, bookmark3);
}
}
运行测试,测试通过。
总结 {#总结}
本文介绍了如何在 jOOQ 中使用 MULTISET Value Operator 来检索多对多关系的记录。
Ref:https://www.sivalabs.in/spring-boot-jooq-tutorial-fetching-many-to-many-associations/