Ваш код, кажется, предполагает, что вы пытаетесь показать отношение внешнего ключа в классе как идентификатор. JPA на самом деле этого не делает. JPA = Java Persistence Language, т. е. вы представляете отношения между классами Java, которые отражают базу данных.
Таким образом, в базе данных у вас может быть внешний ключ, например 'author_id' в таблице книг, но на стороне JPA/Java это будет класс Author, а не просто long/int.
Я надеюсь, что ниже поможет. Я только что добавил его в main() моего кода, так что он может быть не идеальным, но я также оставил несколько комментариев.
Когда у вас есть Page<Book>
, вы можете сопоставить его с DTO в java.
Поскольку запрос получает книги по идентификатору автора, мы можем предположить, что все они имеют один и тот же идентификатор автора... поэтому нет реальной необходимости пытаться получить эту проекцию в базе данных.
РЕДАКТИРОВАТЬ: Разве вообще невозможно получить ссылку на автора от третьего лица?
т.е. Я не знаю, как вы заполняете Книгу ... но не могли бы вы, когда вы получаете Книгу от третьей стороны, посмотреть, есть ли у вас объект Автора с книгами 'author_id', и не сохранять нового Автора с этим идентификатором, если он не существует уже не существует?
В этом случае вы можете сделать AuthorRepo и просто запросить, например:
Page<Author> findAllBy(Pageable page)
==========================================================================
Кажется, что вы получаете страницу книг по идентификатору автора... у вас действительно должно быть отношение JPA, чтобы показать, что:
@Entity
private class Book{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "book_name")
private String name;
//Technically this could be Many:Many as a book could have 2 authors? If so....@ManyToMany
//For simplicity (and what you seem to want) Many Books have ONE author.
@ManyToOne(fetch = FetchType.LAZY)
private Author author;
}
@Entity
private class Author{
//ID here - omitted for clarity
@Column(name = "authors_name")
String name;
//The Author has many books.
// Mapped by shows the bi-direction relationship. You can then do 'Author.getAuthorsBooks()'
//Lazy means it wont fetch all the books from database/(hibernate wont) when you do AuthorRepo.get()
//and will only do the `JOIN ON Books where` if you do Author.getAuthorsBooks()
@OneToMany(fetch = FetchType.LAZY,mappedBy = "author")
private Set<Book> authorsBooks = new HashSet<>();
}
private interface AuthorRepo extends JpaRepository<Author,Long>{
//Note the JPA syntax.
Page<Book> findAll(Pageable pageable);
}
РЕДАКТИРОВАТЬ: Я написал это только в пустой файл... так что может потребоваться настройка или опечатки и т. д.
Если по какой-то причине у вас НЕ может быть отдельного объекта для автора, вам нужно сохранить свой объект в том виде, в котором он есть в настоящее время... Я бы сделал 2 запроса.
Я чувствую, что вы можете сделать это разными способами.
Если вы ДОЛЖНЫ придерживаться Spring Pageable:
Получите запрос страницы в контроллере и сделайте его новым PageRequest.of(pagenum,size)
и введите его, чтобы выполнить запрос страницы ниже
List<Long> getPageOfUniqueAuthorIds(Pageable pageable);
Это даст страницу идентификаторов авторов.
Затем вы хотите использовать этот список длинных объектов (aithorIds) для выполнения второго запроса.
List<AuthorDTOProjection> getBooksAndAuthorIdsWithAuthorsIdsIn(List<Long> authorIds);
@Entity
@Table(name="book")
public class Book {
@Id
@GeneratedValue
private Long id;
@Column(name="book_name")
private String bookName;
@Column(name="author_id")
private Long authorId;
//Setters and getters
}
private interface BookRepo extends JpaRepository<Book,Long> {
//The countQuery is required by Spring Paging.
//Hibernate will need to use the count query when doing paging on a native query.
@Query(nativeQuery = true,
value = "SELECT DISTINCT(author_id) FROM book b ",
countQuery = "SELECT count(*) \n" +
"FROM (SELECT DISTINCT(author_id) FROM book b) authorIds ")
List<Long> getPageOfUniqueAuthorIds(Pageable pageable);
//This is not paged. You want all books with the author IDs from the page query above.
List<Book> findAllByAuthorIdIn(List<Long> authorIds);
}
Затем вам нужно будет сопоставить Entity с DTO на уровне вашего сервиса.
@Autowired
BookRepo bookRepo;
//This would be from the controller method...not declared here...
Pageable pageableFromController = PageRequest.of(0,10);
List<Long> pageOfUniqueAuthorIds = bookRepo.getPageOfUniqueAuthorIds(pageableFromController);
//Get All the books with Author Ids.
List<Book> books = bookRepo.findAllByAuthorIdIn(pageOfUniqueAuthorIds);
//Your abstract AuthorDTO.
abstract class AuthorDTO implements Serializable {
public abstract Long authorId();
public abstract List<Book> books();
}
//Your Author DTO needs to be implemented so I made a "View".
@AllArgsConstructor
class AuthorView extends AuthorDTO{
private long authorId;
private List<Book> books;
@Override
public Long authorId() {
return authorId;
}
@Override
public List<Book> books() {
return books;
}
}
//Get a List of the authorIds in the List<Books>. Could also use the original Page<Long> authorIds...
//As an author without a book is not possible in your database.
final List<Long> authorIdsInBooks = books.stream().map(it -> it.authorId).distinct().collect(Collectors.toList());
//Map the Ids of authors to an Impl of your abstract DTO. Personally I don't see why the AuthorDTO is abstract.
//I'd have expected just an abstract DTO class called "DTO" or something and then AuthorDTO impl that.
//But as the way you have it this will work. I guess you may want more impl of the AuthorDTO so maybe leave the AuthorDTO as abstract.
//This can be returned to client.
final List<AuthorView> authorViews = authorIdsInBooks.stream()
.map(authorId -> new AuthorView(
authorId,
books.stream().filter(it -> it.authorId.equals(authorId)).collect(Collectors.toList()))
)
.collect(Collectors.toList());
person
Jcov
schedule
13.11.2020