spring-boot - 多个@OneToMany实体和内部实体本地化的Spring数据JPA派生查询
问题描述
我正在尝试使用 Spring Data JPA 派生查询执行一项简单的任务,但无法从查询中获得所需的结果。基本上我有一本书,它可以有一个或多个章节,并支持该书和章节的本地化。我想创建一个查询,该查询将基于语言环境获取特定于语言的书籍(带有章节)。这是我的四个实体。
@Entity
@Getter
@Setter
public class Book {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int noOfPages;
/**
* Both mappings below are unidirectional @OneToMany
*/
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "BOOK_ID", referencedColumnName = "ID")
private List<BookTranslation> bookTranslations;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "BOOK_ID", referencedColumnName = "ID")
private List<Chapter> chapters;
/**
* Constructor for JPA
*/
protected Book() {
}
public Book(int noOfPages, List<BookTranslation> bookTranslations, List<Chapter> chapters) {
this.noOfPages = noOfPages;
this.bookTranslations = bookTranslations;
this.chapters = chapters;
}
}
@Entity
@Getter
@Setter
public class BookTranslation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private Language language;
private String name;
/**
* Constructor for JPA
*/
protected BookTranslation() {
}
public BookTranslation(Language language, String name) {
this.language = language;
this.name = name;
}
}
@Entity
@Getter
@Setter
public class Chapter {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int chapterNumber;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "CHAPTER_ID", referencedColumnName = "ID")
private List<ChapterTranslation> chapterTranslations;
/**
* Constructor for JPA
*/
protected Chapter() {
}
public Chapter(int chapterNumber, List<ChapterTranslation> chapterTranslations) {
this.chapterNumber = chapterNumber;
this.chapterTranslations = chapterTranslations;
}
}
@Entity
@Getter
@Setter
public class ChapterTranslation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private Language language;
private String title;
/**
* Constructor for JPA
*/
protected ChapterTranslation() {
}
public ChapterTranslation(Language language, String title) {
this.language = language;
this.title = title;
}
}
public enum Language {
EN, FR
}
下面是示例代码,我用来持久化这些实体。请忽略@GetMapping,这只是一个示例。
@GetMapping("/persist-book")
public void persistBook() {
ChapterTranslation enChapter = new ChapterTranslation(Language.EN, "What is Java persistence?");
ChapterTranslation frChapter = new ChapterTranslation(Language.FR, "Qu'est-ce que la persistance Java?");
List<ChapterTranslation> chapterOneTranslation = new ArrayList<>();
chapterOneTranslation.add(enChapter);
chapterOneTranslation.add(frChapter);
Chapter chapterOne = new Chapter(1, chapterOneTranslation);
List<Chapter> chapters = new ArrayList<>();
chapters.add(chapterOne);
BookTranslation enBook = new BookTranslation(Language.EN, "JPA WikiBook in English");
BookTranslation frBook = new BookTranslation(Language.FR, "JPA WikiBook in French");
List<BookTranslation> bookTranslations = new ArrayList<>();
bookTranslations.add(enBook);
bookTranslations.add(frBook);
Book book = new Book(500, bookTranslations, chapters);
bookRepository.save(book);
}
我的 BookRepository 如下所示:
public interface BookRepository extends CrudRepository<Book, Long> {
List<Book> findBooksByBookTranslations_LanguageAndChapters_ChapterTranslations_Language(Language lang1, Language lang2);
}
我用来检索结果的示例代码。
@GetMapping("/english-book")
public List<Book> retrieveEnglishBook() {
return bookRepository.findBooksByBookTranslations_LanguageAndChapters_ChapterTranslations_Language(
Language.EN, Language.EN
);
}
我的预期输出如下图所示。
我从 Hibernate 日志中注意到的一件事是 Hibernate 总共进行了四个选择查询,而第一个查询输出正是我所需要的。但是,由于这是一个基于方法名称的查询,我认为我无法控制它。
编辑1:在尝试答案之前,我得到了所有返回所有语言环境的书籍,在将我的查询更改为接受的答案中给出的一个之后,我能够获得具有所选语言环境的书籍。
请注意:我还必须将所有集合从使用列表更改为集合,有关此内容的更多信息可以在接受的答案链接中阅读。
解决方案
您描述为所需结果的是单个数据库结果。我猜你的意思是你希望得到所有的书,但只有一种语言的翻译。
您没有描述您实际获得的内容,因此假设您正在获得包含所有可用翻译的书。
您想要的结果超出了派生查询的能力。派生查询的不同谓词都限制了在您的情况下要返回的根实体Book
。他们仍然应该保留所有参考资料。
您可以使用这样的带注释查询来实现您的目标:
public interface BookRepository extends CrudRepository<Book, Long> {
@Query("SELECT b FROM Book b
JOIN FETCH b.bookTranslations as bt
JOIN FETCH b.chapter as c
JOIN FETCH c.chapterTranslation as ct
WHERE bt.language = :lang
AND ct.language = :lang")
List<Book> findBooksByLanguage(Language lang);
}
旁注:只有在结果方法名称与您无论如何命名该方法的名称非常相似时,才应使用查询派生。
推荐阅读
- hive - 在 hive 中处理 char(1) 和 varcar(2) 的 null
- javascript - ./src/index.js 中的错误模块构建失败(来自 ./node_modules/babel-loader/lib/index.js):
- python - isinstance 与 ScalarNode、SequenceNode 和 MappingNode 结合是什么意思?
- mysql - 如何在 MySQL 中为每个 JSON 数据列实现多个 JSON 模式
- huawei-mobile-services - 华为开发者联盟未授予该权限。但我不知道如何仅根据此消息解决它
- android - 为什么我不能将视图绑定放在 onClick 侦听器中?
- python - Python Encryption - 如果我加密相同的消息,我如何获得相同的加密值
- php - 读取xml内容并用php添加一些数字
- angular - 绑定数据数组以形成角度数组
- java - java.io.FileNotFoundException:打开失败:上传 csv 时 EACCES(权限被拒绝)