diff --git a/pom.xml b/pom.xml index dbbc679..c52369b 100644 --- a/pom.xml +++ b/pom.xml @@ -27,9 +27,8 @@ - org.postgresql - postgresql - runtime + com.h2database + h2 org.projectlombok @@ -40,12 +39,12 @@ io.swagger.core.v3 swagger-annotations - 2.2.4 + 2.2.6 org.springdoc springdoc-openapi-ui - 1.6.11 + 1.6.12 diff --git a/request/requests.http b/request/requests.http new file mode 100644 index 0000000..1107670 --- /dev/null +++ b/request/requests.http @@ -0,0 +1,19 @@ +### Получить первую страницу из 3 элементов +GET http://localhost:8080/api/post?offset=0&limit=3 +Content-Type: application/json + +### Получить вторую страницу из 3 элементов с сортировкой +GET http://localhost:8080/api/post/exampleSort?offset=1&limit=3&sort=createOn +Content-Type: application/json + +### Получить вторую страницу из 3 элементов с сортировкой (enum) +GET http://localhost:8080/api/post/exampleEnumSort?offset=1&limit=3&sort=ID_ASC +Content-Type: application/json + +### Получить вторую страницу из 3 элементов JPQL +GET http://localhost:8080/api/post/exampleJpql?offset=1&limit=3&title=Post +Content-Type: application/json + +### Получить вторую страницу из 3 элементов SQL Native +GET http://localhost:8080/api/post/exampleSqlNative?offset=1&limit=3&title=Post +Content-Type: application/json \ No newline at end of file diff --git a/sql_examples.sql b/sql_examples.sql new file mode 100644 index 0000000..2a713e1 --- /dev/null +++ b/sql_examples.sql @@ -0,0 +1,34 @@ +EXPLAIN ANALYSE +SELECT * +FROM post +ORDER BY create_on + FETCH FIRST 50 ROWS ONLY; + +EXPLAIN ANALYSE +SELECT * +FROM post +ORDER BY create_on +OFFSET 50 ROWS FETCH NEXT 50 ROWS ONLY; + +EXPLAIN ANALYSE +SELECT * +FROM post +ORDER BY create_on +OFFSET 9950 ROWS FETCH NEXT 50 ROWS ONLY; + +CREATE INDEX idx_post_created_on ON post (create_on DESC); + +CREATE INDEX idx_post_created_on ON post (create_on DESC, id DESC); + +EXPLAIN ANALYSE +SELECT * +FROM post +ORDER BY create_on DESC, id DESC + FETCH FIRST 50 ROWS ONLY; + +EXPLAIN ANALYSE +SELECT * +FROM post +WHERE (create_on, id) < ('2022-10-30 00:11:43.224314', '8766d496-44c7-4e48-af29-b19178692cd9') +ORDER BY create_on DESC + FETCH FIRST 50 ROWS ONLY \ No newline at end of file diff --git a/src/main/java/dev/struchkov/example/GeneratorPost.java b/src/main/java/dev/struchkov/example/GeneratorPost.java index eb7ef6a..1901745 100644 --- a/src/main/java/dev/struchkov/example/GeneratorPost.java +++ b/src/main/java/dev/struchkov/example/GeneratorPost.java @@ -1,9 +1,14 @@ package dev.struchkov.example; import lombok.RequiredArgsConstructor; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; @Component @@ -12,14 +17,18 @@ public class GeneratorPost { private final PostRepository postRepository; - public void generate() { + @Transactional + @EventListener + public void generate(ContextRefreshedEvent event) { + final List posts = new ArrayList<>(); for (int i = 0; i < 10000; i++) { final Post post = new Post(); post.setId(UUID.randomUUID()); post.setTitle("Post " + i); post.setCreateOn(LocalDateTime.now().minusDays(1L).plusMinutes(i)); - postRepository.save(post); + posts.add(post); } + postRepository.saveAll(posts); } } diff --git a/src/main/java/dev/struchkov/example/PostController.java b/src/main/java/dev/struchkov/example/PostController.java index 5d856d1..dc579b2 100644 --- a/src/main/java/dev/struchkov/example/PostController.java +++ b/src/main/java/dev/struchkov/example/PostController.java @@ -3,30 +3,67 @@ package dev.struchkov.example; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import javax.validation.constraints.Min; + @RestController @RequestMapping("api/post") @RequiredArgsConstructor public class PostController { private final PostRepository repository; - private final GeneratorPost generatorPost; - - @GetMapping("generate") - public void generate() { - generatorPost.generate(); - } @GetMapping public Page getAll( - @RequestParam("offset") Integer offset, - @RequestParam("limit") Integer limit + @RequestParam(value = "offset", defaultValue = "0") @Min(0) Integer offset, + @RequestParam(value = "limit", defaultValue = "20") Integer limit ) { return repository.findAll(PageRequest.of(offset, limit)); } + @GetMapping("exampleSort") + public Page getAllAndSort( + @RequestParam("offset") Integer offset, + @RequestParam("limit") Integer limit, + @RequestParam("sort") String sortField + ) { + return repository.findAll( + PageRequest.of(offset, limit, Sort.by(Sort.Direction.ASC, sortField)) + ); + } + + @GetMapping("exampleEnumSort") + public Page getAllAndEnumSort( + @RequestParam("offset") Integer offset, + @RequestParam("limit") Integer limit, + @RequestParam("sort") PostSort sort + ) { + return repository.findAll( + PageRequest.of(offset, limit, sort.getSortValue()) + ); + } + + @GetMapping("exampleJpql") + public Page getAllJpql( + @RequestParam("title") String title, + @RequestParam("offset") Integer offset, + @RequestParam("limit") Integer limit + ) { + return repository.findAllByTitleJpql(title, PageRequest.of(offset, limit)); + } + + @GetMapping("exampleSqlNative") + public Page getAllSqlNative( + @RequestParam("title") String title, + @RequestParam("offset") Integer offset, + @RequestParam("limit") Integer limit + ) { + return repository.findAllByTitleNative(title, PageRequest.of(offset, limit)); + } + } diff --git a/src/main/java/dev/struchkov/example/PostRepository.java b/src/main/java/dev/struchkov/example/PostRepository.java index 61e35a1..64bff4a 100644 --- a/src/main/java/dev/struchkov/example/PostRepository.java +++ b/src/main/java/dev/struchkov/example/PostRepository.java @@ -1,9 +1,25 @@ package dev.struchkov.example; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.UUID; public interface PostRepository extends JpaRepository { + Page findAllByTitleLikeIgnoreCase(String titleLike, Pageable pageable); + + @Query("SELECT p FROM Post p WHERE p.title like %:title%") + Page findAllByTitleJpql(@Param("title") String title, Pageable pageable); + + @Query( + nativeQuery = true, + value = "SELECT * FROM POST WHERE TITLE LIKE %?1%", + countQuery = "SELECT count(*) FROM POST WHERE TITLE LIKE %?1%" + ) + Page findAllByTitleNative(String title, Pageable pageable); + } diff --git a/src/main/java/dev/struchkov/example/PostSort.java b/src/main/java/dev/struchkov/example/PostSort.java new file mode 100644 index 0000000..796150f --- /dev/null +++ b/src/main/java/dev/struchkov/example/PostSort.java @@ -0,0 +1,17 @@ +package dev.struchkov.example; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; + +@Getter +@RequiredArgsConstructor +public enum PostSort { + + ID_ASC(Sort.by(Sort.Direction.ASC, "id")), + ID_DESC(Sort.by(Sort.Direction.DESC, "id")), + DATE_ASC(Sort.by(Sort.Direction.ASC, "createOn")); + + private final Sort sortValue; + +} diff --git a/src/main/java/dev/struchkov/example/SpringPaginationApplication.java b/src/main/java/dev/struchkov/example/SpringPaginationApplication.java index be90641..b019511 100644 --- a/src/main/java/dev/struchkov/example/SpringPaginationApplication.java +++ b/src/main/java/dev/struchkov/example/SpringPaginationApplication.java @@ -1,8 +1,11 @@ package dev.struchkov.example; +import org.h2.tools.Server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.context.annotation.Bean; + +import java.sql.SQLException; @SpringBootApplication public class SpringPaginationApplication { @@ -11,4 +14,9 @@ public class SpringPaginationApplication { SpringApplication.run(SpringPaginationApplication.class, args); } + @Bean(initMethod = "start", destroyMethod = "stop") + public Server inMemoryH2DatabaseServer() throws SQLException { + return Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", "9092"); + } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0d4a7c7..c1936d4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,13 +1,18 @@ spring: datasource: - driver-class-name: org.postgresql.Driver - url: jdbc:postgresql://localhost:5432/test - username: ${DB_USERNAME} - password: ${DB_PASSWORD} + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:demo + username: sa + password: password jpa: hibernate: - ddl-auto: update + ddl-auto: create-drop show-sql: true properties: hibernate: - format_sql: true \ No newline at end of file + format_sql: true +logging: + level: + org: + hibernate: + type: trace \ No newline at end of file