From 5153125d41ec2db047d779a97e540ef886ca31b3 Mon Sep 17 00:00:00 2001 From: ElinaValieva Date: Sun, 7 Mar 2021 14:17:44 +0300 Subject: [PATCH] Feat: spring-data with lazy load entities --- pom.xml | 1 + spring-data/.gitignore | 33 ++++++++++ spring-data/pom.xml | 63 +++++++++++++++++++ .../spring/data/LibraryController.java | 40 ++++++++++++ .../spring/data/SpringDataApplication.java | 13 ++++ .../spring/data/model/LibraryRepository.java | 17 +++++ .../spring/data/model/PersonRepository.java | 17 +++++ .../spring/data/model/entity/Author.java | 28 +++++++++ .../spring/data/model/entity/Book.java | 42 +++++++++++++ .../spring/data/model/entity/Person.java | 45 +++++++++++++ .../spring/data/model/entity/Reader.java | 29 +++++++++ .../data/model/entity/UserPermission.java | 23 +++++++ .../spring/data/service/LibraryService.java | 51 +++++++++++++++ .../spring/data/service/PersonService.java | 51 +++++++++++++++ .../src/main/resources/application.properties | 7 +++ .../SpringDataApplicationTests.java | 13 ++++ 16 files changed, 473 insertions(+) create mode 100644 spring-data/.gitignore create mode 100644 spring-data/pom.xml create mode 100644 spring-data/src/main/java/com/example/spring/data/LibraryController.java create mode 100644 spring-data/src/main/java/com/example/spring/data/SpringDataApplication.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/LibraryRepository.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/PersonRepository.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/entity/Author.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/entity/Book.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/entity/Person.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/entity/Reader.java create mode 100644 spring-data/src/main/java/com/example/spring/data/model/entity/UserPermission.java create mode 100644 spring-data/src/main/java/com/example/spring/data/service/LibraryService.java create mode 100644 spring-data/src/main/java/com/example/spring/data/service/PersonService.java create mode 100644 spring-data/src/main/resources/application.properties create mode 100644 spring-data/src/test/java/com/example/springdata/SpringDataApplicationTests.java diff --git a/pom.xml b/pom.xml index 337c5ae..3e2c06f 100644 --- a/pom.xml +++ b/pom.xml @@ -19,5 +19,6 @@ spring-annotation spring-testing spring-cache + spring-data \ No newline at end of file diff --git a/spring-data/.gitignore b/spring-data/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/spring-data/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/spring-data/pom.xml b/spring-data/pom.xml new file mode 100644 index 0000000..ca6901f --- /dev/null +++ b/spring-data/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.3 + + + com.spring + spring-data + 1.0.0 + spring-data + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/spring-data/src/main/java/com/example/spring/data/LibraryController.java b/spring-data/src/main/java/com/example/spring/data/LibraryController.java new file mode 100644 index 0000000..089d6d8 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/LibraryController.java @@ -0,0 +1,40 @@ +package com.example.spring.data; + +import com.example.spring.data.model.entity.Book; +import com.example.spring.data.model.entity.Person; +import com.example.spring.data.service.LibraryService; +import com.example.spring.data.service.PersonService; +import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@AllArgsConstructor +public class LibraryController { + + private final LibraryService libraryService; + private final PersonService personService; + + @GetMapping(value = "/join") + public ResponseEntity> getAll() { + return ResponseEntity.ok(libraryService.getAllByJoinFetch()); + } + + @GetMapping(value = "/limited") + public ResponseEntity> getLimitedFieldsInfo() { + return ResponseEntity.ok(libraryService.getAllByLimitLazyFields()); + } + + @GetMapping(value = "/entity-graph") + public ResponseEntity> getByEntityGraph() { + return ResponseEntity.ok(personService.findPersons()); + } + + @GetMapping(value = "/entity-graph-name") + public ResponseEntity> getByEntityGraphByName() { + return ResponseEntity.ok(personService.findPersonByName()); + } +} diff --git a/spring-data/src/main/java/com/example/spring/data/SpringDataApplication.java b/spring-data/src/main/java/com/example/spring/data/SpringDataApplication.java new file mode 100644 index 0000000..194aaa1 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/SpringDataApplication.java @@ -0,0 +1,13 @@ +package com.example.spring.data; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringDataApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringDataApplication.class, args); + } + +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/LibraryRepository.java b/spring-data/src/main/java/com/example/spring/data/model/LibraryRepository.java new file mode 100644 index 0000000..45c98c1 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/LibraryRepository.java @@ -0,0 +1,17 @@ +package com.example.spring.data.model; + +import com.example.spring.data.model.entity.Book; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface LibraryRepository extends JpaRepository { + + @Query(value = "select b from Book b left join fetch b.authors left join fetch b.readers") + List findAllWithJoinFetch(Sort sort); + + @Query(value = "select new Book(b.id, b.name, b.description) from Book b") + List findAllWithLimitedFields(); +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/PersonRepository.java b/spring-data/src/main/java/com/example/spring/data/model/PersonRepository.java new file mode 100644 index 0000000..7a03fd0 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/PersonRepository.java @@ -0,0 +1,17 @@ +package com.example.spring.data.model; + +import com.example.spring.data.model.entity.Person; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface PersonRepository extends JpaRepository { + + @Query(value = "select new Person(p.id, p.name, p.info, p.permissions) from Person p left join fetch p.permissions") + List findAll(); + + @EntityGraph(value = "Person.all") + List findAllByNameLike(String name); +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/entity/Author.java b/spring-data/src/main/java/com/example/spring/data/model/entity/Author.java new file mode 100644 index 0000000..2e69c65 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/entity/Author.java @@ -0,0 +1,28 @@ +package com.example.spring.data.model.entity; + +import lombok.*; + +import javax.persistence.*; + +@Entity +@Builder +@Data +@ToString(exclude = {"book", "person"}) +@AllArgsConstructor +@NoArgsConstructor +public class Author { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private String description; + + @ManyToOne(fetch = FetchType.LAZY) + private Book book; + + @ManyToOne(fetch = FetchType.LAZY) + private Person person; +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/entity/Book.java b/spring-data/src/main/java/com/example/spring/data/model/entity/Book.java new file mode 100644 index 0000000..7029e47 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/entity/Book.java @@ -0,0 +1,42 @@ +package com.example.spring.data.model.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + + +@Entity +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@NamedEntityGraph(name = "Book.authors", + attributeNodes = @NamedAttributeNode("authors") +) +public class Book { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private String description; + + @OneToMany(mappedBy = "book", cascade = CascadeType.ALL) + private Set authors = new HashSet<>(); + + @OneToMany(mappedBy = "book", cascade = CascadeType.ALL) + private Set readers = new HashSet<>(); + + public Book(Long id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/entity/Person.java b/spring-data/src/main/java/com/example/spring/data/model/entity/Person.java new file mode 100644 index 0000000..e2b308b --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/entity/Person.java @@ -0,0 +1,45 @@ +package com.example.spring.data.model.entity; + +import com.example.spring.data.model.entity.Author; +import com.example.spring.data.model.entity.UserPermission; +import lombok.*; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Entity +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@NamedEntityGraphs({ + @NamedEntityGraph( + name = "Person.all", includeAllAttributes = true + ) +}) +public class Person { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private String info; + + @OneToMany(mappedBy = "person", cascade = CascadeType.ALL) + private List permissions = new ArrayList<>(); + + @OneToMany(mappedBy = "person", cascade = CascadeType.ALL) + private Set authors = new HashSet<>(); + + public Person(Long id, String name, String info, List permissions) { + this.id = id; + this.name = name; + this.info = info; + this.permissions = permissions; + } +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/entity/Reader.java b/spring-data/src/main/java/com/example/spring/data/model/entity/Reader.java new file mode 100644 index 0000000..79586bb --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/entity/Reader.java @@ -0,0 +1,29 @@ +package com.example.spring.data.model.entity; + +import lombok.*; + +import javax.persistence.*; + +@Entity +@Builder +@Data +@ToString(exclude = {"book"}) +@AllArgsConstructor +@NoArgsConstructor +public class Reader { + + @Id + @GeneratedValue + private Long id; + + private String name; + + private String lastName; + + private String address; + + private String description; + + @ManyToOne(fetch = FetchType.LAZY) + private Book book; +} diff --git a/spring-data/src/main/java/com/example/spring/data/model/entity/UserPermission.java b/spring-data/src/main/java/com/example/spring/data/model/entity/UserPermission.java new file mode 100644 index 0000000..2e51aaf --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/model/entity/UserPermission.java @@ -0,0 +1,23 @@ +package com.example.spring.data.model.entity; + +import lombok.*; + +import javax.persistence.*; + +@Entity +@Data +@Builder +@ToString(exclude = {"person"}) +@AllArgsConstructor +@NoArgsConstructor +public class UserPermission { + + @Id + @GeneratedValue + private Long id; + + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + private Person person; +} diff --git a/spring-data/src/main/java/com/example/spring/data/service/LibraryService.java b/spring-data/src/main/java/com/example/spring/data/service/LibraryService.java new file mode 100644 index 0000000..8ef9c92 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/service/LibraryService.java @@ -0,0 +1,51 @@ +package com.example.spring.data.service; + +import com.example.spring.data.model.LibraryRepository; +import com.example.spring.data.model.entity.Author; +import com.example.spring.data.model.entity.Book; +import com.example.spring.data.model.entity.Reader; +import lombok.AllArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +@AllArgsConstructor +public class LibraryService { + + private final LibraryRepository libraryRepository; + + @PostConstruct + void init() { + libraryRepository.saveAll(IntStream.range(0, 10) + .mapToObj(i -> Book.builder() + .name("Book " + i) + .description("Book description " + i) + .authors(Collections.singleton(Author.builder() + .name("Author " + i) + .description("Author description") + .build())) + .readers(Collections.singleton(Reader.builder() + .name("Reader " + i) + .lastName("Last name" + i) + .address("Address") + .description("Description") + .build())) + .build()) + .collect(Collectors.toList())); + } + + public List getAllByJoinFetch() { + return libraryRepository.findAllWithJoinFetch(Sort.by(Sort.Direction.DESC, "id")); + } + + public List getAllByLimitLazyFields() { + return libraryRepository.findAllWithLimitedFields(); + } + +} diff --git a/spring-data/src/main/java/com/example/spring/data/service/PersonService.java b/spring-data/src/main/java/com/example/spring/data/service/PersonService.java new file mode 100644 index 0000000..b49d971 --- /dev/null +++ b/spring-data/src/main/java/com/example/spring/data/service/PersonService.java @@ -0,0 +1,51 @@ +package com.example.spring.data.service; + +import com.example.spring.data.model.PersonRepository; +import com.example.spring.data.model.entity.Author; +import com.example.spring.data.model.entity.Person; +import com.example.spring.data.model.entity.UserPermission; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +@AllArgsConstructor +public class PersonService { + + private final PersonRepository personRepository; + + @PostConstruct + public void init() { + personRepository.saveAll(IntStream.range(0, 10) + .mapToObj(i -> Person.builder() + .name("User " + i) + .info("Info " + i) + .permissions(Arrays.asList( + UserPermission.builder() + .name("READ") + .build(), + UserPermission.builder() + .name("WRITE") + .build())) + .authors(Collections.singleton(Author.builder() + .name("Author " + i) + .description("Author description") + .build())) + .build()) + .collect(Collectors.toList())); + } + + public List findPersons() { + return personRepository.findAll(); + } + + public List findPersonByName() { + return personRepository.findAllByNameLike("%User%"); + } +} diff --git a/spring-data/src/main/resources/application.properties b/spring-data/src/main/resources/application.properties new file mode 100644 index 0000000..cbfe9af --- /dev/null +++ b/spring-data/src/main/resources/application.properties @@ -0,0 +1,7 @@ +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect + +logging.level.org.hibernate.SQL=DEBUG diff --git a/spring-data/src/test/java/com/example/springdata/SpringDataApplicationTests.java b/spring-data/src/test/java/com/example/springdata/SpringDataApplicationTests.java new file mode 100644 index 0000000..626e13a --- /dev/null +++ b/spring-data/src/test/java/com/example/springdata/SpringDataApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.springdata; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringDataApplicationTests { + + @Test + void contextLoads() { + } + +}