Занятие №10
Spring Data
Кернер
Денис
Вспомним прошлое занятие по JPA и Hibernate.
Чтобы работать с данными, мы должны настраивать соединение, работать с менеджером сущностей.
Писать код по сохранению.
Работать с данными еще проще
Вы просто пишете интерфейс репозитория
И все..
dependencies {
// ...
implementation
'org.springframework.boot:spring-boot-starter-data-jpa'
// ...
}
У нас есть книга. У книги есть название, год выпуска.
У книги может быть несколько авторов.
@Entity
@Data
public class Book {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
private String title;
@Column
private String description;
@Column
private Integer year;
}
Для упрощения используем lombok. (статья как подключить)
public interface BookRepository
extends CrudRepository<Book, Long> {
}
Domain Drive Design см. здесь
@SpringBootApplication
@EnableJpaRepositories(
basePackages = "io.github.bael.spring.data")
public class SpringDataApplication {
public class BookServiceImpl implements BookService {
private final BookRepository bookRepository;
@Override
public void save(Book book) {
bookRepository.save(book);
}
@Override
public Optional<Book> findById(Long id) {
return bookRepository.findById(id);
}
}
Create Read Update Delete aka CRUD
@Test
public void testSave() {
Book book = new Book();
book.setTitle("Приключения Тома Сойера");
book.setYear(1876);
bookRepository.save(book);
boolean founded = false;
for (Book iteratedBook : bookRepository.findAll()) {
if (iteratedBook.getTitle()
.equals("Приключения Тома Сойера")
&& iteratedBook.getId() > 0) {
founded = true; }
}
Assert.assertTrue(founded);
}
@NoRepositoryBean
public interface CrudRepository<T, ID>
extends Repository<T, ID> {
/**
* Saves a given entity. Use the returned
instance for further operations as
the save operation might have changed the
* entity instance completely.
*
* @param entity must not be {@literal null}.
* @return the saved entity will
* never be {@literal null}.
*/
<S extends T> S save(S entity);
public interface BookRepository
extends CrudRepository<Book, Long> {
List<Book> findByYear(int year);
}
@Test
public void testFindByYear() {
Assert.assertTrue(
bookRepository.findByYear(1876)
.stream()
.anyMatch(
book ->
book.getYear() == 1876));
}
Проверяем
Ответ:
Proxy + кодогенерация
Spring "предполагает" выборку по названию метода и параметрам.
Keyword | Sample | JPQL snippet |
---|---|---|
|
findByLastnameAndFirstname |
|
|
findByLastnameOrFirstname |
|
Is,Equals |
|
|
|
|
|
|
|
|
|
|
|
Полный перечень см. здесь
PagingAndSortingRepository
public interface BookRepository
extends CrudRepository<Book, Long>,
PagingAndSortingRepository<Book, Long>,
JpaRepository<Book, Long> {
public Page<Book> findAtPage(int pageIndex,
int pageNumber,
Direction direction,
String sortField) {
PageRequest request = PageRequest.of(pageIndex, pageNumber,
direction, sortField);
return bookRepository.findAll(request);
}
select
book0_.id as id1_0_,
book0_.description as descript2_0_,
book0_.title as title3_0_,
book0_.year as year4_0_
from
book book0_
where
book0_.year=1876
@Override
public List<Book> findSame(Book book) {
return bookRepository.findAll(Example.of(book));
}
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Включаем логирование и проверяем работу
Specification API
public class BookSpecifications {
public static Specification<Book> byYearRange(int startYear, int finishYear) {
return yearLessThan(finishYear).and(yearGreaterThan(startYear));
}
public static Specification<Book> yearLessThan(int finishYear) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.lessThanOrEqualTo(root.get("year"), finishYear);
}
public static Specification<Book> yearGreaterThan(int startYear) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.greaterThanOrEqualTo(root.get("year"), startYear);
}
@Query - jpql
@Query("select aob.book from "
+ "AuthorOfBook aob join aob.book "
+ "join aob.author "
+ "where aob.author.id = ?1")
List<Book> findByAuthor(Long authorId);
Hibernate:
select
book1_.id as id1_2_,
book1_.description as descript2_2_,
book1_.title as title3_2_,
book1_.year as year4_2_
from
author_of_book authorofbo0_
inner join
book book1_
on authorofbo0_.book_id=book1_.id
inner join
author author2_
on authorofbo0_.author_id=author2_.id
where
authorofbo0_.author_id=?
@NamedNativeQuery = чистый SQL
Можете вызывать хранимые процедуры и функции базы данных
Пишем свою имплементацию
(имя репозитория + Impl)
public interface BookComplexQueryRepository {
List<Book> complexQueryMethod();
}
@Repository
public class BookRepositoryImpl implements
BookComplexQueryRepository {
@Override
public List<Book> complexQueryMethod() {
...
public interface BookRepository
extends CrudRepository<Book, Long>,
PagingAndSortingRepository<Book, Long>,
JpaRepository<Book, Long>,
BookComplexQueryRepository {
@Test
public void testComplexQuery() {
Assert.assertEquals(2,
bookService.complexQuery().size());
}
@Override
public List<Book> complexQuery() {
return bookRepository.complexQueryMethod();
}