diff --git a/persistence-modules/spring-boot-persistence-4/pom.xml b/persistence-modules/spring-boot-persistence-4/pom.xml index 7a6c2d2b17..78cd75234a 100644 --- a/persistence-modules/spring-boot-persistence-4/pom.xml +++ b/persistence-modules/spring-boot-persistence-4/pom.xml @@ -42,6 +42,26 @@ spring-boot-starter-test test + + io.hypersistence + hypersistence-utils-hibernate-62 + ${hypersistence-utils.version} + + + com.vladmihalcea + db-util + ${db.util.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + org.projectlombok + lombok + ${lombok.version} + @@ -58,6 +78,10 @@ 5.9.3 17 17 + 1.0.7 + 3.7.0 + 2.16.0 + 1.18.28 \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/ParametrizationAware.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/ParametrizationAware.java new file mode 100644 index 0000000000..08b12f38ab --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/ParametrizationAware.java @@ -0,0 +1,14 @@ +package com.baeldung.listvsset; + +import java.lang.reflect.ParameterizedType; +import java.util.Arrays; +import java.util.List; + +public abstract class ParametrizationAware { + + public List> getParametrizationClass() { + ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); + return Arrays.stream(type.getActualTypeArguments()) + .map(s -> ((Class) s)).toList(); + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/Service.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/Service.java new file mode 100644 index 0000000000..2a700de09b --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/Service.java @@ -0,0 +1,63 @@ +package com.baeldung.listvsset; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class Service extends ParametrizationAware { + + private final JpaRepository repository; + + public Service(JpaRepository repository) { + this.repository = repository; + } + + public JpaRepository getRepository() { + return repository; + } + + public int countNumberOfRequestsWithFunction(ToIntFunction> function) { + return function.applyAsInt(repository.findAll()); + } + + + public Optional getUserById(Long id) { + return repository.findById(id); + } + + public void deleteAll() { + repository.deleteAll(); + } + + public List saveAll(Iterable entities) { + return repository.saveAll(entities); + } + + public List findAll() { + return repository.findAll(); + } + + public Optional getUserByIdWithPredicate(long id, Predicate predicate) { + Optional user = repository.findById(id); + user.ifPresent(predicate::test); + return user; + } + + public int getUserByIdWithFunction(Long id, ToIntFunction function) { + + Optional optionalUser = repository.findById(id); + if(optionalUser.isPresent()) { + return function.applyAsInt(optionalUser.get()); + } else { + return 0; + } + } + + public void save(S entity) { + repository.save(entity); + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Application.java new file mode 100644 index 0000000000..8e48e25d58 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Comment.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Comment.java new file mode 100644 index 0000000000..482ddc3dc5 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Comment.java @@ -0,0 +1,25 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Data; + +@Data +@Entity +public class Comment { + + @Id + private Long id; + + private String text; + + @ManyToOne + private User author; + + @JsonBackReference + @ManyToOne + private Post post; + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Group.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Group.java new file mode 100644 index 0000000000..0f02d3025c --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Group.java @@ -0,0 +1,23 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import java.util.List; +import lombok.Data; + +@Data +@Entity(name = "interest_group") +@Table(name = "interest_group") +public class Group { + + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + private List members; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Post.java new file mode 100644 index 0000000000..301301a1ff --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Post.java @@ -0,0 +1,32 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.List; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "post", fetch = FetchType.EAGER) + private List comments; + + @JsonBackReference + @ManyToOne + private User author; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/PostRepository.java new file mode 100644 index 0000000000..ae84e4bc29 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Profile.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Profile.java new file mode 100644 index 0000000000..9e29eec2aa --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/Profile.java @@ -0,0 +1,28 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToOne; +import lombok.Data; + +@Entity +@Data +public class Profile { + + @Id + private Long id; + + @Lob + private String biography; + private String website; + private String profilePictureUrl; + + @JsonBackReference + @OneToOne(mappedBy = "profile") + @JoinColumn(unique = true) + private User user; +} + diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/User.java new file mode 100644 index 0000000000..48f733e27a --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/User.java @@ -0,0 +1,36 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.util.List; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToOne(cascade = CascadeType.ALL) + private Profile profile; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected List posts; + + + @ManyToMany(mappedBy = "members", fetch = FetchType.EAGER) + private List groups; +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserRepository.java new file mode 100644 index 0000000000..174c495bfe --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserService.java new file mode 100644 index 0000000000..acea27d2fa --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/fulldomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.list.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Application.java new file mode 100644 index 0000000000..81d82f5d43 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Group.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Group.java new file mode 100644 index 0000000000..0460f77a60 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Group.java @@ -0,0 +1,23 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import java.util.List; +import lombok.Data; + +@Data +@Entity(name = "interest_group") +@Table(name = "interest_group") +public class Group { + + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + private List members; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupRepository.java new file mode 100644 index 0000000000..b14b210fc7 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupService.java new file mode 100644 index 0000000000..3f84410f94 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/GroupService.java @@ -0,0 +1,29 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import java.util.List; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GroupService { + + private final GroupRepository groupRepository; + + @Autowired + public GroupService(GroupRepository groupRepository) { + this.groupRepository = groupRepository; + } + + public Optional findById(Long aLong) { + return groupRepository.findById(aLong); + } + + public List findAll() { + return groupRepository.findAll(); + } + + public S save(S entity) { + return groupRepository.save(entity); + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Post.java new file mode 100644 index 0000000000..faef97b212 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/Post.java @@ -0,0 +1,23 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonBackReference + @ManyToOne + private User author; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/PostRepository.java new file mode 100644 index 0000000000..1c11f13690 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/User.java new file mode 100644 index 0000000000..a58f6dd9e5 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/User.java @@ -0,0 +1,46 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.util.List; +import java.util.Objects; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected List posts; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + + return Objects.equals(id, user.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserRepository.java new file mode 100644 index 0000000000..e3c7b471da --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserService.java new file mode 100644 index 0000000000..439b9b6445 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/moderatedomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.list.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Application.java new file mode 100644 index 0000000000..dfa61d66b8 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Post.java new file mode 100644 index 0000000000..35c436357f --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/Post.java @@ -0,0 +1,23 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonBackReference + @ManyToOne + private User author; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/PostRepository.java new file mode 100644 index 0000000000..95fc9ccf16 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/User.java new file mode 100644 index 0000000000..6495761c18 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/User.java @@ -0,0 +1,26 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.util.List; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected List posts; +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserRepository.java new file mode 100644 index 0000000000..bba1de8723 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import org.springframework.context.annotation.Lazy; +import org.springframework.data.jpa.repository.JpaRepository; + +@Lazy(value = false) +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserService.java new file mode 100644 index 0000000000..7b7f42f2b3 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/list/simpledomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.list.simpledomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Application.java new file mode 100644 index 0000000000..6eb23eac7a --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Comment.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Comment.java new file mode 100644 index 0000000000..ab935de4ee --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Comment.java @@ -0,0 +1,44 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import java.util.Objects; +import lombok.Data; + +@Data +@Entity +public class Comment { + + @Id + private Long id; + + private String text; + + @ManyToOne + private User author; + + @JsonBackReference + @ManyToOne + private Post post; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Comment comment = (Comment) o; + + return Objects.equals(id, comment.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Group.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Group.java new file mode 100644 index 0000000000..de0ba9a59d --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Group.java @@ -0,0 +1,43 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import java.util.Objects; +import java.util.Set; +import lombok.Data; + +@Data +@Entity(name = "interest_group") +@Table(name = "interest_group") +public class Group { + + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + private Set members; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Group group = (Group) o; + + return Objects.equals(id, group.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Post.java new file mode 100644 index 0000000000..7c4913fbec --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Post.java @@ -0,0 +1,52 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.Objects; +import java.util.Set; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "post", fetch = FetchType.EAGER) + private Set comments; + + @JsonBackReference + @ManyToOne + private User author; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Post post = (Post) o; + + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/PostRepository.java new file mode 100644 index 0000000000..adc22019c9 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Profile.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Profile.java new file mode 100644 index 0000000000..914702a373 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/Profile.java @@ -0,0 +1,28 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.OneToOne; +import lombok.Data; + +@Entity +@Data +public class Profile { + + @Id + private Long id; + + @Lob + private String biography; + private String website; + private String profilePictureUrl; + + @JsonBackReference + @OneToOne(mappedBy = "profile") + @JoinColumn(unique = true) + private User user; +} + diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/User.java new file mode 100644 index 0000000000..a0efbac57a --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/User.java @@ -0,0 +1,56 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.util.Objects; +import java.util.Set; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToOne(cascade = CascadeType.ALL) + private Profile profile; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected Set posts; + + + @ManyToMany(mappedBy = "members", fetch = FetchType.EAGER) + private Set groups; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + + return Objects.equals(id, user.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserRepository.java new file mode 100644 index 0000000000..5812881c89 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserService.java new file mode 100644 index 0000000000..c14e4a5fa4 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/fulldomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.set.fulldomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Application.java new file mode 100644 index 0000000000..ae999bd269 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Group.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Group.java new file mode 100644 index 0000000000..6331183ac9 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Group.java @@ -0,0 +1,23 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import java.util.Set; +import lombok.Data; + +@Data +@Entity(name = "interest_group") +@Table(name = "interest_group") +public class Group { + + @Id + private Long id; + + private String name; + + @ManyToMany(fetch = FetchType.EAGER) + private Set members; +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupRepository.java new file mode 100644 index 0000000000..5cb4690adb --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupService.java new file mode 100644 index 0000000000..3a8a4fcaa3 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/GroupService.java @@ -0,0 +1,29 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import java.util.List; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GroupService { + + private final GroupRepository groupRepository; + + @Autowired + public GroupService(GroupRepository groupRepository) { + this.groupRepository = groupRepository; + } + + public Optional findById(Long aLong) { + return groupRepository.findById(aLong); + } + + public List findAll() { + return groupRepository.findAll(); + } + + public void save(Group group) { + groupRepository.save(group); + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Post.java new file mode 100644 index 0000000000..02449a3402 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/Post.java @@ -0,0 +1,43 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import java.util.Objects; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonBackReference + @ManyToOne + private User author; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Post post = (Post) o; + + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/PostRepository.java new file mode 100644 index 0000000000..6387cee108 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/User.java new file mode 100644 index 0000000000..89f9736c41 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/User.java @@ -0,0 +1,46 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.util.Objects; +import java.util.Set; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected Set posts; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + + return Objects.equals(id, user.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserRepository.java new file mode 100644 index 0000000000..bf54e0d11b --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserService.java new file mode 100644 index 0000000000..59c8d5dd36 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/moderatedomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.set.moderatedomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Application.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Application.java new file mode 100644 index 0000000000..15d780965a --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Application.java @@ -0,0 +1,8 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Post.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Post.java new file mode 100644 index 0000000000..69c137e350 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/Post.java @@ -0,0 +1,43 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import java.util.Objects; +import lombok.Data; + +@Entity +@Data +public class Post { + + @Id + private Long id; + + @Lob + private String content; + + @JsonBackReference + @ManyToOne + private User author; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Post post = (Post) o; + + return Objects.equals(id, post.id); + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/PostRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/PostRepository.java new file mode 100644 index 0000000000..867144e1b1 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/PostRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/User.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/User.java new file mode 100644 index 0000000000..481ef14499 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/User.java @@ -0,0 +1,27 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.util.Set; +import lombok.Data; + +@Data +@Entity(name = "simple_user") +@Table(name = "simple_user") +public class User { + + @Id + private Long id; + private String username; + private String email; + + @JsonManagedReference + @OneToMany(cascade = CascadeType.ALL, mappedBy = "author", fetch = FetchType.EAGER) + protected Set posts; + +} \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserRepository.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserRepository.java new file mode 100644 index 0000000000..959a7b3357 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserRepository.java @@ -0,0 +1,9 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import org.springframework.context.annotation.Lazy; +import org.springframework.data.jpa.repository.JpaRepository; + +@Lazy(value = false) +public interface UserRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserService.java b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserService.java new file mode 100644 index 0000000000..36e9804b36 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/main/java/com/baeldung/listvsset/eager/set/simpledomain/UserService.java @@ -0,0 +1,20 @@ +package com.baeldung.listvsset.eager.set.simpledomain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class UserService extends com.baeldung.listvsset.Service { + + public UserService(JpaRepository repository) { + super(repository); + } + + @Override + public UserRepository getRepository() { + return ((UserRepository) super.getRepository()); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/BaseNPlusOneIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/BaseNPlusOneIntegrationTest.java new file mode 100644 index 0000000000..4e669dadc4 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/BaseNPlusOneIntegrationTest.java @@ -0,0 +1,76 @@ +package com.baeldung.listvsset; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.baeldung.listvsset.util.DatabaseUtil; +import com.baeldung.listvsset.util.JsonUtils; +import io.hypersistence.utils.jdbc.validator.SQLStatementCountValidator; +import jakarta.annotation.PostConstruct; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(properties = { + "spring.jpa.generate-ddl=true", + "spring.jpa.show-sql=false", + "spring.datasource.url=jdbc:h2:mem:~/test;DATABASE_TO_UPPER=false" +}) +abstract public class BaseNPlusOneIntegrationTest extends ParametrizationAware { + + @Autowired + protected List> services; + @Autowired + protected DatabaseUtil databaseUtil; + @Autowired + protected JsonUtils jsonUtils; + + private final Map, Service> serviceMap = new HashMap<>(); + + @PostConstruct + void init() { + for (Service service : services) { + Class parametrization = service.getParametrizationClass().get(0); + serviceMap.put(parametrization, service); + } + } + + @BeforeEach + void setUp() { + addUsers(); + SQLStatementCountValidator.reset(); + System.out.println("************************************************"); + System.out.println("\n\n\n\n\n"); + + } + + @AfterEach + void tearDown() { + System.out.println("\n\n\n\n\n"); + System.out.println("************************************************"); + databaseUtil.truncateAllTables(); + SQLStatementCountValidator.reset(); + } + + @Test + void givenCorrectConfigurationWhenStartContextThenRepositoryIsPresent() { + assertThat(getService()).isNotNull(); + } + + @Test + void givenCorrectDatabaseWhenStartThenDatabaseIsNotEmpty() { + List result = getService().findAll(); + assertThat(result).isNotEmpty(); + } + + protected Service getService() { + Class parametrization = getParametrizationClass().get(0); + return (Service) serviceMap.get(parametrization); + } + + protected abstract void addUsers(); +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/JsonUtilTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/JsonUtilTest.java new file mode 100644 index 0000000000..3e14b3c963 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/JsonUtilTest.java @@ -0,0 +1,56 @@ +package com.baeldung.listvsset; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.baeldung.listvsset.eager.list.fulldomain.Application; +import com.baeldung.listvsset.eager.list.fulldomain.Comment; +import com.baeldung.listvsset.eager.list.fulldomain.Group; +import com.baeldung.listvsset.eager.list.fulldomain.Post; +import com.baeldung.listvsset.eager.list.fulldomain.Profile; +import com.baeldung.listvsset.eager.list.fulldomain.User; +import com.baeldung.listvsset.util.JsonUtils; +import com.baeldung.listvsset.util.TestConfig; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}) +class JsonUtilTest { + + @Autowired + private JsonUtils jsonUtils; + + @Test + void givenFileWhenConvertingToUsersThenConversionIsCorrect() { + List users = jsonUtils.getUsers(User.class); + assertThat(users).isNotEmpty(); + boolean wentThroughEverything = false; + for (User user : users) { + assertThat(user.getGroups()).isNotNull(); + Profile profile = user.getProfile(); + if (profile != null) { + assertThat(user.getId()).isEqualTo(profile.getUser().getId()); + } + for (Post post : user.getPosts()) { + assertThat(user.getId()).isEqualTo(post.getAuthor().getId()); + if (post.getComments() != null) { + wentThroughEverything = true; + for (Comment comment : post.getComments()) { + assertThat(post.getId()).isEqualTo(comment.getPost().getId()); + } + } + } + } + assertTrue(wentThroughEverything); + } + + @Test + void givenFileWhenConvertingToGroupsThenConversionIsCorrect() { + List groups = jsonUtils.getGroups(Group.class); + assertThat(groups).isNotEmpty(); + } + + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerFullDomainIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerFullDomainIntegrationTest.java new file mode 100644 index 0000000000..3e7c46f47c --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerFullDomainIntegrationTest.java @@ -0,0 +1,129 @@ +package com.baeldung.listvsset.list; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.list.fulldomain.Application; +import com.baeldung.listvsset.eager.list.fulldomain.Comment; +import com.baeldung.listvsset.eager.list.fulldomain.Group; +import com.baeldung.listvsset.eager.list.fulldomain.Post; +import com.baeldung.listvsset.eager.list.fulldomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.ToIntFunction; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerFullDomainIntegrationTest extends BaseNPlusOneIntegrationTest { + + public static final String POSTS = "posts"; + public static final String USERS = "users"; + + @ParameterizedTest + @MethodSource + void givenEagerListBasedUser_WhenFetchingAllUsers_ThenIssueNPlusOneRequests(ToIntFunction> function) { + int numberOfRequests = getService().countNumberOfRequestsWithFunction(function); + assertSelectCount(numberOfRequests); + } + + static Stream givenEagerListBasedUser_WhenFetchingAllUsers_ThenIssueNPlusOneRequests() { + return Stream.of( + Arguments.of((ToIntFunction>) s -> { + int result = 2 * s.size() + 1; + List posts = s.stream().map(User::getPosts) + .flatMap(List::stream) + .toList(); + + result += posts.size(); + return result; + }) + ); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedUser_WhenFetchingOneUser_ThenUseDFS(Long id) { + int numberOfRequests = getService().getUserByIdWithFunction(id, this::countNumberOfRequests); + assertSelectCount(numberOfRequests); + } + + private int countNumberOfRequests(User user) { + HashMap> visitedMap = new HashMap<>(); + visitedMap.put(POSTS, new HashSet<>()); + visitedMap.put(USERS, new HashSet<>()); + int result = 1; + visitedMap.get(USERS).add(user.getId()); + List posts = user.getPosts(); + result += 1; + result += explorePosts(posts, visitedMap); + return result; + + } + + private int explorePosts(List posts, HashMap> visitedMap) { + int result = 0; + if (posts == null || posts.isEmpty()) { + return result; + } + for (Post post : posts) { + if (!visitedMap.get(POSTS).contains(post.getId())) { + result++; + visitedMap.get(POSTS).add(post.getId()); + List commenters = post.getComments().stream().map(Comment::getAuthor).toList(); + result += exploreUsers(commenters, visitedMap); + } + } + return result; + } + + private int exploreUsers(List users, HashMap> visitedMap) { + int result = 0; + if (users == null || users.isEmpty()) { + return result; + } + for (User user : users) { + if (!visitedMap.get(USERS).contains(user.getId())) { + ++result; + visitedMap.get(USERS).add(user.getId()); + result += explorePosts(user.getPosts(), visitedMap); + ++result; + } + } + return result; + } + + protected void addUsers() { + List groups = jsonUtils.getGroups(Group.class); + databaseUtil.saveAll(groups); + List users = jsonUtils.getUsers(User.class); + Map> comments = new HashMap<>(); + for (User user : users) { + for (Post post : user.getPosts()) { + if (!comments.containsKey(post.getId())) { + comments.put(post.getId(), new ArrayList<>()); + } + comments.get(post.getId()).addAll(post.getComments()); + post.setComments(Collections.emptyList()); + } + } + databaseUtil.saveAll(users); + // Handle non-existent users while adding comments + databaseUtil.mergeAll(jsonUtils.getUsers(User.class)); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerModerateDomainIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerModerateDomainIntegrationTest.java new file mode 100644 index 0000000000..5e5549122f --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerModerateDomainIntegrationTest.java @@ -0,0 +1,87 @@ +package com.baeldung.listvsset.list; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertDeleteCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertInsertCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.reset; +import static org.assertj.core.api.Assertions.assertThat; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.list.moderatedomain.Application; +import com.baeldung.listvsset.eager.list.moderatedomain.Group; +import com.baeldung.listvsset.eager.list.moderatedomain.GroupService; +import com.baeldung.listvsset.eager.list.moderatedomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerModerateDomainIntegrationTest extends BaseNPlusOneIntegrationTest { + + @Autowired + private GroupService groupService; + + @Test + void givenEagerListBasedUser_whenFetchingAllUsers_thenIssueNPlusOneRequests() { + List users = getService().findAll(); + assertSelectCount(users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedUser_whenFetchingOneUser_thenIssueOneRequest(Long id) { + getService().getUserById(id); + assertSelectCount(1); + } + + @Test + void givenEagerListBasedGroup_whenFetchingAllGroups_thenIssueNPlusMPlusOneRequests() { + List groups = groupService.findAll(); + Set users = groups.stream().map(Group::getMembers).flatMap(List::stream).collect(Collectors.toSet()); + assertSelectCount(groups.size() + users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedGroup_whenFetchingAllGroups_thenIssueNPlusOneRequests(Long groupId) { + Optional group = groupService.findById(groupId); + assertThat(group).isPresent(); + assertSelectCount(1 + group.get().getMembers().size()); + } + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedGroup_whenRemoveUser_thenRecreateGroup(Long groupId) { + Optional optionalGroup = groupService.findById(groupId); + assertThat(optionalGroup).isPresent(); + Group group = optionalGroup.get(); + List members = group.getMembers(); + assertSelectCount(1 + members.size()); + if (!members.isEmpty()) { + reset(); + members.remove(0); + groupService.save(group); + assertSelectCount(1 + members.size() + 1); + assertDeleteCount(1); + assertInsertCount(members.size()); + } + } + + protected void addUsers() { + List users = jsonUtils.getUsers(User.class); + databaseUtil.saveAll(users); + List groups = jsonUtils.getGroupsWithMembers(Group.class); + databaseUtil.saveAll(groups); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerSimpleDomainIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerSimpleDomainIntegrationTest.java new file mode 100644 index 0000000000..0c14ce8e50 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/list/NPlusOneEagerSimpleDomainIntegrationTest.java @@ -0,0 +1,67 @@ +package com.baeldung.listvsset.list; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertUpdateCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.reset; +import static org.assertj.core.api.Assertions.assertThat; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.list.simpledomain.Application; +import com.baeldung.listvsset.eager.list.simpledomain.Post; +import com.baeldung.listvsset.eager.list.simpledomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerSimpleDomainIntegrationTest extends BaseNPlusOneIntegrationTest { + + @Test + void givenEagerListBasedUser_WhenFetchingAllUsers_ThenIssueNPlusOneRequests() { + List users = getService().findAll(); + assertSelectCount(users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedUser_WhenFetchingOneUser_ThenIssueOneRequest(Long id) { + getService().getUserById(id); + assertSelectCount(1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedUser_whenDeletePost_ThenIssueSingleUpdate(Long id) { + Optional optionalUser = getService().getUserById(id); + assertSelectCount(1); + optionalUser.ifPresent(user -> { + List posts = user.getPosts(); + int originalNumberOfPosts = posts.size(); + reset(); + if (!posts.isEmpty()) { + posts.get(0).setAuthor(null); + getService().save(user); + assertSelectCount(1); + assertUpdateCount(1); + getService().getUserById(id).ifPresent(updatedUser -> { + assertThat(updatedUser.getPosts()).hasSize(originalNumberOfPosts - 1); + }); + } + + }); + } + + protected void addUsers() { + List users = jsonUtils.getUsers(User.class); + databaseUtil.saveAll(users); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerFullDomainJoinIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerFullDomainJoinIntegrationTest.java new file mode 100644 index 0000000000..001398607d --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerFullDomainJoinIntegrationTest.java @@ -0,0 +1,106 @@ +package com.baeldung.listvsset.set; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.set.fulldomain.Application; +import com.baeldung.listvsset.eager.set.fulldomain.Comment; +import com.baeldung.listvsset.eager.set.fulldomain.Group; +import com.baeldung.listvsset.eager.set.fulldomain.Post; +import com.baeldung.listvsset.eager.set.fulldomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerFullDomainJoinIntegrationTest extends BaseNPlusOneIntegrationTest { + + public static final String POSTS = "posts"; + public static final String USERS = "users"; + + @Test + void givenEagerSetBasedUser_WhenFetchingAllUsers_ThenIssueNPlusOneRequests() { + List users = getService().findAll(); + assertSelectCount(users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerUserWhenFetchingOneUserThenIssueNPlusOneRequestsWithCartesianProduct(Long id) { + HashMap> visitedMap = new HashMap<>(); + visitedMap.put(POSTS, new HashSet<>()); + visitedMap.put(USERS, new HashSet<>()); + int numberOfRequests = getService() + .getUserByIdWithFunction(id, user -> { + int result = 1; + visitedMap.get(USERS).add(user.getId()); + Set posts = user.getPosts(); + result += explorePosts(posts, visitedMap); + return result; + }); + assertSelectCount(numberOfRequests); + } + + private int explorePosts(Set posts, HashMap> visitedMap) { + int result = 0; + if (posts == null || posts.isEmpty()) { + return result; + } + for (Post post : posts) { + if (!visitedMap.get(POSTS).contains(post.getId())) { + visitedMap.get(POSTS).add(post.getId()); + List commenters = post.getComments().stream().map(Comment::getAuthor).toList(); + result += exploreUsers(commenters, visitedMap); + } + } + return result; + } + + private int exploreUsers(List users, HashMap> visitedMap) { + int result = 0; + if (users == null || users.isEmpty()) { + return result; + } + for (User user : users) { + if (!visitedMap.get(USERS).contains(user.getId())) { + ++result; + visitedMap.get(USERS).add(user.getId()); + result += explorePosts(user.getPosts(), visitedMap); + } + } + return result; + } + + protected void addUsers() { + List groups = jsonUtils.getGroups(Group.class); + databaseUtil.saveAll(groups); + List users = jsonUtils.getUsers(User.class); + Map> comments = new HashMap<>(); + for (User user : users) { + for (Post post : user.getPosts()) { + if (!comments.containsKey(post.getId())) { + comments.put(post.getId(), new ArrayList<>()); + } + comments.get(post.getId()).addAll(post.getComments()); + post.setComments(Collections.emptySet()); + } + } + databaseUtil.saveAll(users); + // Handle non-existent users while adding comments + databaseUtil.mergeAll(jsonUtils.getUsers(User.class)); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerModerateDomainIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerModerateDomainIntegrationTest.java new file mode 100644 index 0000000000..55b7bfb287 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerModerateDomainIntegrationTest.java @@ -0,0 +1,83 @@ +package com.baeldung.listvsset.set; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertDeleteCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.reset; +import static org.assertj.core.api.Assertions.assertThat; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.set.moderatedomain.Application; +import com.baeldung.listvsset.eager.set.moderatedomain.Group; +import com.baeldung.listvsset.eager.set.moderatedomain.GroupService; +import com.baeldung.listvsset.eager.set.moderatedomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerModerateDomainIntegrationTest extends BaseNPlusOneIntegrationTest { + + @Autowired + private GroupService groupService; + + @Test + void givenEagerSetBasedUser_whenFetchingAllUsers_thenIssueNPlusOneRequests() { + List users = getService().findAll(); + assertSelectCount(users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerSetBasedUser_whenFetchingOneUser_thenIssueOneRequest(Long id) { + getService().getUserById(id); + assertSelectCount(1); + } + + @Test + void givenEagerSetBasedGroup_whenFetchingAllGroups_thenIssueNPlusOneRequests() { + List groups = groupService.findAll(); + assertSelectCount(groups.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerSetBasedGroup_whenFetchingAllGroups_thenCreateCartesianProductInOneQuery(Long groupId) { + groupService.findById(groupId); + assertSelectCount(1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedGroup_whenRemoveUser_thenIssueOnlyOneDelete(Long groupId) { + Optional optionalGroup = groupService.findById(groupId); + assertThat(optionalGroup).isPresent(); + Group group = optionalGroup.get(); + if (!group.getMembers().isEmpty()) { + reset(); + Set newMembers = group.getMembers().stream().skip(1).collect(Collectors.toSet()); + group.setMembers(newMembers); + groupService.save(group); + assertSelectCount(1); + assertDeleteCount(1); + } + } + + protected void addUsers() { + List users = jsonUtils.getUsers(User.class); + databaseUtil.saveAll(users); + List groups = jsonUtils.getGroupsWithMembers(Group.class); + databaseUtil.saveAll(groups); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerSimpleDomainIntegrationTest.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerSimpleDomainIntegrationTest.java new file mode 100644 index 0000000000..e6161fa57b --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/set/NPlusOneEagerSimpleDomainIntegrationTest.java @@ -0,0 +1,68 @@ +package com.baeldung.listvsset.set; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertUpdateCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.reset; +import static org.assertj.core.api.Assertions.assertThat; + +import com.baeldung.listvsset.BaseNPlusOneIntegrationTest; +import com.baeldung.listvsset.eager.set.simpledomain.Application; +import com.baeldung.listvsset.eager.set.simpledomain.Post; +import com.baeldung.listvsset.eager.set.simpledomain.User; +import com.baeldung.listvsset.util.TestConfig; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = {Application.class, TestConfig.class}, properties = { + "hibernate.show_sql=true", + "logging.level.org.hibernate.SQL=debug", + "logging.level.org.hibernate.orm.jdbc.bind=trace" +}) +class NPlusOneEagerSimpleDomainIntegrationTest extends BaseNPlusOneIntegrationTest { + + @Test + void givenEagerSetBasedUser_WhenFetchingAllUsers_ThenIssueNPlusOneRequests() { + List users = getService().findAll(); + assertSelectCount(users.size() + 1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerSetBasedUser_WhenFetchingOneUser_ThenIssueOneRequest(Long id) { + getService().getUserById(id); + assertSelectCount(1); + } + + @ParameterizedTest + @ValueSource(longs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) + void givenEagerListBasedUser_whenDeletePost_ThenIssueSingleUpdate(Long id) { + Optional optionalUser = getService().getUserById(id); + assertSelectCount(1); + optionalUser.ifPresent(user -> { + Set posts = user.getPosts(); + int originalNumberOfPosts = posts.size(); + reset(); + if (!posts.isEmpty()) { + posts.iterator().next().setAuthor(null); + getService().save(user); + assertSelectCount(1); + assertUpdateCount(1); + getService().getUserById(id).ifPresent(updatedUser -> { + assertThat(updatedUser.getPosts()).hasSize(originalNumberOfPosts - 1); + }); + } + + }); + } + + protected void addUsers() { + List users = jsonUtils.getUsers(User.class); + databaseUtil.saveAll(users); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DataSourceWrapper.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DataSourceWrapper.java new file mode 100644 index 0000000000..6ca7c45391 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DataSourceWrapper.java @@ -0,0 +1,35 @@ +package com.baeldung.listvsset.util; + +import io.hypersistence.utils.logging.InlineQueryLogEntryCreator; +import javax.sql.DataSource; +import net.ttddyy.dsproxy.listener.ChainListener; +import net.ttddyy.dsproxy.listener.DataSourceQueryCountListener; +import net.ttddyy.dsproxy.listener.logging.SLF4JQueryLoggingListener; +import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; + +@Component +public class DataSourceWrapper implements BeanPostProcessor { + + public Object postProcessBeforeInitialization(Object bean, String beanName) { + return bean; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof DataSource originalDataSource) { + ChainListener listener = new ChainListener(); + SLF4JQueryLoggingListener loggingListener = new SLF4JQueryLoggingListener(); + loggingListener.setQueryLogEntryCreator(new InlineQueryLogEntryCreator()); + listener.addListener(loggingListener); + listener.addListener(new DataSourceQueryCountListener()); + return ProxyDataSourceBuilder + .create(originalDataSource) + .name("DS-Proxy") + .listener(listener) + .build(); + } + return bean; + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DatabaseUtil.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DatabaseUtil.java new file mode 100644 index 0000000000..1e6e75511f --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/DatabaseUtil.java @@ -0,0 +1,47 @@ +package com.baeldung.listvsset.util; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class DatabaseUtil { + + public static final String TRUNCATE_QUERY_FORMAT = "TRUNCATE TABLE %s"; + @PersistenceContext + private EntityManager entityManager; + + @Transactional + public void truncateAllTables() { + switchForeignKeysOff(); + entityManager.createNativeQuery("SHOW TABLES", String.class) + .getResultList() + .forEach(this::truncateTable); + switchForeignKeysOn(); + } + + @Transactional + public void saveAll(List entities) { + entities.forEach(s -> entityManager.persist(s)); + } + + @Transactional + public void mergeAll(List entities) { + entities.forEach(s -> entityManager.merge(s)); + } + + private int truncateTable(Object s) { + return entityManager + .createNativeQuery(TRUNCATE_QUERY_FORMAT.formatted(s)).executeUpdate(); + } + + private void switchForeignKeysOn() { + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate(); + } + + private void switchForeignKeysOff() { + entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate(); + } +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/JsonUtils.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/JsonUtils.java new file mode 100644 index 0000000000..c8577da58d --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/JsonUtils.java @@ -0,0 +1,51 @@ +package com.baeldung.listvsset.util; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.IOException; +import java.util.List; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JsonUtils { + + @Value("users.json") + private File userFile; + + @Value("groups.json") + private File groupFile; + + @Value("groups_with_members.json") + private File groupsWithMembersFile; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + static { + MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public List getUsers(Class userType) { + return getObjects(userType, userFile); + } + + public List getGroups(Class userType) { + return getObjects(userType, groupFile); + } + + public List getGroupsWithMembers(Class groupType) { + return getObjects(groupType, groupsWithMembersFile); + } + + private List getObjects(Class userType, File userFile1) { + try { + return MAPPER.readValue(userFile1, MAPPER.getTypeFactory() + .constructCollectionType(List.class, userType)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/TestConfig.java b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/TestConfig.java new file mode 100644 index 0000000000..82ee56b35b --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/java/com/baeldung/listvsset/util/TestConfig.java @@ -0,0 +1,24 @@ +package com.baeldung.listvsset.util; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestConfig { + + @Bean + public DataSourceWrapper dataSourceWrapper() { + return new DataSourceWrapper(); + } + + @Bean + public DatabaseUtil databaseUtil() { + return new DatabaseUtil(); + } + + @Bean + JsonUtils jsonUtils() { + return new JsonUtils(); + } + +} diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/groups.json b/persistence-modules/spring-boot-persistence-4/src/test/resources/groups.json new file mode 100644 index 0000000000..9ccad3f79a --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/groups.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "name": "Bitwolf" + }, + { + "id": 2, + "name": "Solarbreeze" + }, + { + "id": 3, + "name": "Bamity" + }, + { + "id": 4, + "name": "Hatity" + }, + { + "id": 5, + "name": "Veribet" + }, + { + "id": 6, + "name": "Tampflex" + }, + { + "id": 7, + "name": "Zontrax" + }, + { + "id": 8, + "name": "Aerified" + }, + { + "id": 9, + "name": "Konklab" + }, + { + "id": 10, + "name": "Tampflex" + }, + { + "id": 11, + "name": "Mat Lam Tam" + }, + { + "id": 12, + "name": "Alpha" + }, + { + "id": 13, + "name": "Quo Lux" + }, + { + "id": 14, + "name": "Kanlam" + }, + { + "id": 15, + "name": "Veribet" + }, + { + "id": 16, + "name": "Ventosanzap" + }, + { + "id": 17, + "name": "Opela" + }, + { + "id": 18, + "name": "Alphazap" + }, + { + "id": 19, + "name": "Lotlux" + }, + { + "id": 20, + "name": "Alpha" + }, + { + "id": 21, + "name": "Andalax" + }, + { + "id": 22, + "name": "Bitchip" + }, + { + "id": 23, + "name": "Cookley" + }, + { + "id": 24, + "name": "Konklab" + }, + { + "id": 25, + "name": "Konklab" + }, + { + "id": 26, + "name": "Lotstring" + }, + { + "id": 27, + "name": "Konklux" + }, + { + "id": 28, + "name": "Temp" + }, + { + "id": 29, + "name": "Voyatouch" + }, + { + "id": 30, + "name": "Trippledex" + }, + { + "id": 31, + "name": "Sonsing" + }, + { + "id": 32, + "name": "Andalax" + }, + { + "id": 33, + "name": "Voltsillam" + }, + { + "id": 34, + "name": "Stronghold" + }, + { + "id": 35, + "name": "Hatity" + }, + { + "id": 36, + "name": "Andalax" + }, + { + "id": 37, + "name": "Holdlamis" + }, + { + "id": 38, + "name": "Bamity" + }, + { + "id": 39, + "name": "Voltsillam" + }, + { + "id": 40, + "name": "Aerified" + }, + { + "id": 41, + "name": "Regrant" + }, + { + "id": 42, + "name": "Subin" + }, + { + "id": 43, + "name": "Tin" + }, + { + "id": 44, + "name": "Voltsillam" + }, + { + "id": 45, + "name": "Trippledex" + }, + { + "id": 46, + "name": "Solarbreeze" + }, + { + "id": 47, + "name": "Treeflex" + }, + { + "id": 48, + "name": "Fintone" + }, + { + "id": 49, + "name": "Tin" + }, + { + "id": 50, + "name": "It" + }, + { + "id": 51, + "name": "Y-Solowarm" + }, + { + "id": 52, + "name": "Bigtax" + }, + { + "id": 53, + "name": "Flowdesk" + }, + { + "id": 54, + "name": "Vagram" + }, + { + "id": 55, + "name": "Keylex" + }, + { + "id": 56, + "name": "Zontrax" + }, + { + "id": 57, + "name": "Vagram" + }, + { + "id": 58, + "name": "Lotstring" + }, + { + "id": 59, + "name": "Cookley" + }, + { + "id": 60, + "name": "Zaam-Dox" + }, + { + "id": 61, + "name": "Zoolab" + }, + { + "id": 62, + "name": "Quo Lux" + }, + { + "id": 63, + "name": "Ventosanzap" + }, + { + "id": 64, + "name": "Ventosanzap" + }, + { + "id": 65, + "name": "Lotstring" + }, + { + "id": 66, + "name": "Keylex" + }, + { + "id": 67, + "name": "Stim" + }, + { + "id": 68, + "name": "Alphazap" + }, + { + "id": 69, + "name": "It" + }, + { + "id": 70, + "name": "Cardify" + }, + { + "id": 71, + "name": "Zamit" + }, + { + "id": 72, + "name": "Holdlamis" + }, + { + "id": 73, + "name": "Konklab" + }, + { + "id": 74, + "name": "Ventosanzap" + }, + { + "id": 75, + "name": "Wrapsafe" + }, + { + "id": 76, + "name": "Cookley" + }, + { + "id": 77, + "name": "Y-find" + }, + { + "id": 78, + "name": "Regrant" + }, + { + "id": 79, + "name": "Otcom" + }, + { + "id": 80, + "name": "Cookley" + }, + { + "id": 81, + "name": "Bigtax" + }, + { + "id": 82, + "name": "Holdlamis" + }, + { + "id": 83, + "name": "Bigtax" + }, + { + "id": 84, + "name": "Voltsillam" + }, + { + "id": 85, + "name": "Duobam" + }, + { + "id": 86, + "name": "Flowdesk" + }, + { + "id": 87, + "name": "It" + }, + { + "id": 88, + "name": "Hatity" + }, + { + "id": 89, + "name": "Zathin" + }, + { + "id": 90, + "name": "Hatity" + }, + { + "id": 91, + "name": "Cardify" + }, + { + "id": 92, + "name": "Konklab" + }, + { + "id": 93, + "name": "Treeflex" + }, + { + "id": 94, + "name": "Flexidy" + }, + { + "id": 95, + "name": "Latlux" + }, + { + "id": 96, + "name": "Bitwolf" + }, + { + "id": 97, + "name": "Zontrax" + }, + { + "id": 98, + "name": "Greenlam" + }, + { + "id": 99, + "name": "Asoka" + }, + { + "id": 100, + "name": "Ronstring" + } +] \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/groups_with_members.json b/persistence-modules/spring-boot-persistence-4/src/test/resources/groups_with_members.json new file mode 100644 index 0000000000..99e0da9891 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/groups_with_members.json @@ -0,0 +1,183 @@ +[ + { + "id": 1, + "name": "Pacocha-Hills", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + } + ] + }, + { + "id": 2, + "name": "Windler, Wiegand and Bernhard", + "members": [] + }, + { + "id": 3, + "name": "Flatley-Hilll", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + }, + { + "id": 5 + }, + { + "id": 6 + }, + { + "id": 7 + }, + { + "id": 8 + }, + { + "id": 9 + } + ] + }, + { + "id": 4, + "name": "Daugherty and Sons", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + }, + { + "id": 5 + }, + { + "id": 6 + }, + { + "id": 7 + } + ] + }, + { + "id": 5, + "name": "Leannon, Kub and Murazik", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + }, + { + "id": 5 + }, + { + "id": 6 + } + ] + }, + { + "id": 6, + "name": "Considine-Koss", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + }, + { + "id": 5 + }, + { + "id": 6 + }, + { + "id": 7 + } + ] + }, + { + "id": 7, + "name": "Russel LLC", + "members": [ + { + "id": 1 + }, + { + "id": 2 + }, + { + "id": 3 + }, + { + "id": 4 + } + ] + }, + { + "id": 8, + "name": "Wisoky-Grimes", + "members": [ + { + "id": 1 + }, + { + "id": 2 + } + ] + }, + { + "id": 9, + "name": "Larson Inc", + "members": [ + { + "id": 1 + }, + { + "id": 2 + } + ] + }, + { + "id": 10, + "name": "Franecki-Simonis", + "members": [] + } +] \ No newline at end of file diff --git a/persistence-modules/spring-boot-persistence-4/src/test/resources/users.json b/persistence-modules/spring-boot-persistence-4/src/test/resources/users.json new file mode 100644 index 0000000000..1fa6202dd3 --- /dev/null +++ b/persistence-modules/spring-boot-persistence-4/src/test/resources/users.json @@ -0,0 +1,899 @@ +[ + { + "id": 1, + "username": "ecrumbleholme0", + "email": "pmerritt0@wordpress.org", + "groups": [ + { + "id": 70 + }, + { + "id": 67 + }, + { + "id": 54 + }, + { + "id": 41 + }, + { + "id": 40 + }, + { + "id": 5 + }, + { + "id": 82 + } + ], + "profile": { + "id": 1, + "biography": "Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.", + "website": "https://engadget.com/adipiscing/lorem/vitae/mattis.jpg?non=in&mattis=porttitor&pulvinar=pede&nulla", + "profilePictureUrl": "http://dummyimage.com/207x100.png/dddddd/000000" + }, + "posts": [] + }, + { + "id": 2, + "username": "moldred1", + "email": "grichardes1@shareasale.com", + "groups": [ + { + "id": 39 + }, + { + "id": 53 + }, + { + "id": 26 + }, + { + "id": 70 + }, + { + "id": 49 + }, + { + "id": 33 + }, + { + "id": 64 + }, + { + "id": 65 + }, + { + "id": 14 + }, + { + "id": 10 + } + ], + "profile": { + "id": 2, + "biography": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.", + "website": "http://arizona.edu/urna/ut.html?erat=pretium&volutpat=iaculis&in=diam&congue", + "profilePictureUrl": "http://dummyimage.com/200x100.png/dddddd/000000" + }, + "posts": [ + { + "id": 1, + "content": "Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.", + "comments": [ + { + "id": 208446, + "test": { + "id": "Optional empowering attitude" + }, + "author": { + "id": 10 + } + }, + { + "id": 229466, + "test": { + "id": "Customizable multi-tasking encryption" + }, + "author": { + "id": 3 + } + }, + { + "id": 408703, + "test": { + "id": "Re-engineered contextually-based protocol" + }, + "author": { + "id": 9 + } + }, + { + "id": 837433, + "test": { + "id": "Centralized well-modulated pricing structure" + }, + "author": { + "id": 8 + } + } + ] + }, + { + "id": 2, + "content": "Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.", + "comments": [] + }, + { + "id": 3, + "content": "Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\n\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\n\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.", + "comments": [ + { + "id": 53333, + "test": { + "id": "Cross-platform bi-directional encoding" + }, + "author": { + "id": 6 + } + }, + { + "id": 806260, + "test": { + "id": "Expanded composite neural-net" + }, + "author": { + "id": 1 + } + }, + { + "id": 266068, + "test": { + "id": "Implemented attitude-oriented knowledge user" + }, + "author": { + "id": 3 + } + } + ] + } + ] + }, + { + "id": 3, + "username": "agoodsall2", + "email": "ofellman2@ibm.com", + "groups": [ + { + "id": 36 + }, + { + "id": 7 + }, + { + "id": 77 + }, + { + "id": 28 + }, + { + "id": 32 + } + ], + "profile": { + "id": 3, + "biography": "In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.", + "website": "http://cargocollective.com/consequat/in/consequat/ut/nulla/sed.png?dictumst=faucibus", + "profilePictureUrl": "http://dummyimage.com/224x100.png/cc0000/ffffff" + }, + "posts": [ + { + "id": 4, + "content": "Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\n\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.", + "comments": [ + { + "id": 349493, + "test": { + "id": "Operative actuating budgetary management" + }, + "author": { + "id": 2 + } + }, + { + "id": 909598, + "test": { + "id": "Profound next generation product" + }, + "author": { + "id": 4 + } + }, + { + "id": 247072, + "test": { + "id": "Team-oriented multimedia analyzer" + }, + "author": { + "id": 9 + } + } + ] + }, + { + "id": 5, + "content": "Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.", + "comments": [ + { + "id": 572489, + "test": { + "id": "Open-source optimizing focus group" + }, + "author": { + "id": 5 + } + }, + { + "id": 133947, + "test": { + "id": "Self-enabling attitude-oriented success" + }, + "author": { + "id": 1 + } + }, + { + "id": 496573, + "test": { + "id": "Profit-focused multimedia firmware" + }, + "author": { + "id": 8 + } + }, + { + "id": 137780, + "test": { + "id": "Self-enabling intermediate info-mediaries" + }, + "author": { + "id": 2 + } + } + ] + }, + { + "id": 6, + "content": "Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\n\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.", + "comments": [] + } + ] + }, + { + "id": 4, + "username": "cpourvoieur3", + "email": "mlisimore3@unicef.org", + "groups": [ + { + "id": 27 + }, + { + "id": 49 + }, + { + "id": 21 + }, + { + "id": 48 + }, + { + "id": 90 + }, + { + "id": 57 + }, + { + "id": 19 + }, + { + "id": 10 + } + ], + "profile": { + "id": 4, + "biography": "Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.", + "website": "http://slideshare.net/rutrum/nulla/tellus/in/sagittis/dui.png?duis=volutpat&at=erat", + "profilePictureUrl": "http://dummyimage.com/167x100.png/cc0000/ffffff" + }, + "posts": [ + { + "id": 7, + "content": "Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\n\nIn congue. Etiam justo. Etiam pretium iaculis justo.\n\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.", + "comments": [] + }, + { + "id": 8, + "content": "Sed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\n\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.", + "comments": [] + }, + { + "id": 9, + "content": "Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\n\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\n\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.", + "comments": [ + { + "id": 727396, + "test": { + "id": "Business-focused maximized framework" + }, + "author": { + "id": 6 + } + }, + { + "id": 529333, + "test": { + "id": "Fully-configurable client-driven Graphic Interface" + }, + "author": { + "id": 1 + } + }, + { + "id": 698528, + "test": { + "id": "Diverse methodical framework" + }, + "author": { + "id": 1 + } + }, + { + "id": 570927, + "test": { + "id": "Profound interactive infrastructure" + }, + "author": { + "id": 5 + } + } + ] + }, + { + "id": 10, + "content": "Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\n\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.", + "comments": [ + { + "id": 374234, + "test": { + "id": "Profit-focused fresh-thinking success" + }, + "author": { + "id": 6 + } + }, + { + "id": 266342, + "test": { + "id": "Advanced optimizing Graphic Interface" + }, + "author": { + "id": 6 + } + }, + { + "id": 970877, + "test": { + "id": "Multi-lateral web-enabled synergy" + }, + "author": { + "id": 8 + } + }, + { + "id": 635357, + "test": { + "id": "Centralized real-time time-frame" + }, + "author": { + "id": 5 + } + }, + { + "id": 818761, + "test": { + "id": "Profit-focused secondary middleware" + }, + "author": { + "id": 2 + } + } + ] + } + ] + }, + { + "id": 5, + "username": "driddoch4", + "email": "hfreake4@simplemachines.org", + "groups": [ + { + "id": 68 + }, + { + "id": 52 + }, + { + "id": 18 + }, + { + "id": 82 + }, + { + "id": 29 + }, + { + "id": 55 + }, + { + "id": 95 + } + ], + "profile": { + "id": 5, + "biography": "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.", + "website": "http://jiathis.com/turpis/enim/blandit/mi/in.html?duis=augue&mattis=vel&egestas", + "profilePictureUrl": "http://dummyimage.com/181x100.png/5fa2dd/ffffff" + }, + "posts": [ + { + "id": 11, + "content": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\n\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\n\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.", + "comments": [ + { + "id": 668167, + "test": { + "id": "Decentralized static application" + }, + "author": { + "id": 2 + } + }, + { + "id": 645021, + "test": { + "id": "Progressive cohesive complexity" + }, + "author": { + "id": 8 + } + }, + { + "id": 904836, + "test": { + "id": "Right-sized human-resource middleware" + }, + "author": { + "id": 2 + } + }, + { + "id": 193482, + "test": { + "id": "Organized bottom-line monitoring" + }, + "author": { + "id": 3 + } + } + ] + }, + { + "id": 12, + "content": "Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.", + "comments": [ + { + "id": 820497, + "test": { + "id": "Open-architected well-modulated structure" + }, + "author": { + "id": 10 + } + } + ] + }, + { + "id": 13, + "content": "Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\n\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\n\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.", + "comments": [ + { + "id": 214834, + "test": { + "id": "Face to face content-based contingency" + }, + "author": { + "id": 4 + } + }, + { + "id": 361052, + "test": { + "id": "Vision-oriented client-driven emulation" + }, + "author": { + "id": 10 + } + }, + { + "id": 819369, + "test": { + "id": "Decentralized clear-thinking projection" + }, + "author": { + "id": 9 + } + } + ] + }, + { + "id": 14, + "content": "Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\n\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.", + "comments": [] + } + ] + }, + { + "id": 6, + "username": "dvelte5", + "email": "shuke5@canalblog.com", + "groups": [ + { + "id": 86 + }, + { + "id": 28 + }, + { + "id": 8 + }, + { + "id": 22 + } + ], + "profile": { + "id": 6, + "biography": "Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.", + "website": "http://amazon.co.jp/eget/semper.aspx?faucibus=habitasse&cursus=platea&urna", + "profilePictureUrl": "http://dummyimage.com/179x100.png/dddddd/000000" + }, + "posts": [] + }, + { + "id": 7, + "username": "mhallor6", + "email": "gpetters6@tiny.cc", + "groups": [ + { + "id": 49 + }, + { + "id": 56 + }, + { + "id": 24 + } + ], + "profile": { + "id": 7, + "biography": "Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.", + "website": "https://globo.com/eget/nunc.js?justo=nulla&eu=neque&massa=libero&donec=convallis&dapibus", + "profilePictureUrl": "http://dummyimage.com/122x100.png/ff4444/ffffff" + }, + "posts": [ + { + "id": 15, + "content": "Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", + "comments": [ + { + "id": 380358, + "test": { + "id": "Optimized user-facing software" + }, + "author": { + "id": 1 + } + } + ] + } + ] + }, + { + "id": 8, + "username": "nbergeau7", + "email": "mkibbel7@tmall.com", + "groups": [ + { + "id": 24 + }, + { + "id": 54 + }, + { + "id": 95 + } + ], + "profile": { + "id": 8, + "biography": "Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.", + "website": "http://people.com.cn/odio/consequat/varius.jsp?eros=nullam&vestibulum=varius&ac=nulla", + "profilePictureUrl": "http://dummyimage.com/230x100.png/5fa2dd/ffffff" + }, + "posts": [ + { + "id": 16, + "content": "Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\n\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\n\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.", + "comments": [] + }, + { + "id": 17, + "content": "Sed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.", + "comments": [ + { + "id": 886087, + "test": { + "id": "Total demand-driven infrastructure" + }, + "author": { + "id": 9 + } + }, + { + "id": 235353, + "test": { + "id": "Versatile mobile monitoring" + }, + "author": { + "id": 4 + } + }, + { + "id": 264476, + "test": { + "id": "Future-proofed static initiative" + }, + "author": { + "id": 2 + } + } + ] + }, + { + "id": 18, + "content": "Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.", + "comments": [ + { + "id": 299869, + "test": { + "id": "Pre-emptive 3rd generation pricing structure" + }, + "author": { + "id": 5 + } + }, + { + "id": 82649, + "test": { + "id": "Public-key holistic pricing structure" + }, + "author": { + "id": 2 + } + }, + { + "id": 675757, + "test": { + "id": "Function-based homogeneous monitoring" + }, + "author": { + "id": 4 + } + }, + { + "id": 321146, + "test": { + "id": "Proactive intangible ability" + }, + "author": { + "id": 1 + } + }, + { + "id": 754321, + "test": { + "id": "Operative zero tolerance complexity" + }, + "author": { + "id": 3 + } + } + ] + } + ] + }, + { + "id": 9, + "username": "ddebruin8", + "email": "zdavidovits8@aol.com", + "groups": [ + { + "id": 40 + }, + { + "id": 67 + }, + { + "id": 67 + }, + { + "id": 13 + }, + { + "id": 50 + }, + { + "id": 34 + }, + { + "id": 53 + } + ], + "profile": { + "id": 9, + "biography": "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.", + "website": "http://shinystat.com/nec/sem/duis/aliquam/convallis/nunc.json?eget=facilisi&semper=cras", + "profilePictureUrl": "http://dummyimage.com/229x100.png/5fa2dd/ffffff" + }, + "posts": [ + { + "id": 19, + "content": "Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\n\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.", + "comments": [ + { + "id": 246330, + "test": { + "id": "Cross-platform needs-based firmware" + }, + "author": { + "id": 10 + } + }, + { + "id": 340531, + "test": { + "id": "Total content-based data-warehouse" + }, + "author": { + "id": 9 + } + }, + { + "id": 145457, + "test": { + "id": "Programmable transitional budgetary management" + }, + "author": { + "id": 3 + } + }, + { + "id": 13492, + "test": { + "id": "Switchable encompassing archive" + }, + "author": { + "id": 8 + } + }, + { + "id": 310239, + "test": { + "id": "Self-enabling attitude-oriented strategy" + }, + "author": { + "id": 4 + } + } + ] + }, + { + "id": 20, + "content": "Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\n\nFusce consequat. Nulla nisl. Nunc nisl.", + "comments": [] + }, + { + "id": 21, + "content": "Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\n\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\n\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.", + "comments": [ + { + "id": 909627, + "test": { + "id": "Expanded intangible forecast" + }, + "author": { + "id": 7 + } + } + ] + } + ] + }, + { + "id": 10, + "username": "lwoodrooffe9", + "email": "mhowatt9@kickstarter.com", + "groups": [ + { + "id": 31 + }, + { + "id": 44 + }, + { + "id": 38 + }, + { + "id": 14 + } + ], + "profile": { + "id": 10, + "biography": "Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", + "website": "https://blinklist.com/semper/est/quam/pharetra/magna/ac.png?sagittis=eu&nam=tincidunt", + "profilePictureUrl": "http://dummyimage.com/121x100.png/5fa2dd/ffffff" + }, + "posts": [ + { + "id": 22, + "content": "Sed ante. Vivamus tortor. Duis mattis egestas metus.\n\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\n\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.", + "comments": [ + { + "id": 831777, + "test": { + "id": "Open-source non-volatile help-desk" + }, + "author": { + "id": 7 + } + }, + { + "id": 883941, + "test": { + "id": "Fully-configurable mission-critical emulation" + }, + "author": { + "id": 6 + } + }, + { + "id": 759548, + "test": { + "id": "Right-sized bifurcated strategy" + }, + "author": { + "id": 10 + } + }, + { + "id": 294894, + "test": { + "id": "Balanced explicit paradigm" + }, + "author": { + "id": 3 + } + } + ] + } + ] + } +] \ No newline at end of file