From f7bd3d50f5c265358afd738e2e7d440887955b0d Mon Sep 17 00:00:00 2001 From: Eugene Kovko <37694937+eukovko@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:50:38 +0100 Subject: [PATCH] BAEL-7509: An example with optimized referenceBy (#15988) * BAEL-7509: An example with optimized referenceBy * BAEL-7509: Added query counting tests * BAEL-7509: Formatting fix --- .../spring-data-jpa-repo-4/pom.xml | 12 +++ .../persistence/findvsget/entity/Group.java | 51 +++++++++++ .../findvsget/repository/GroupRepository.java | 8 ++ .../AdditionalLookupIntegrationTest.java | 87 +++++++++++++++++++ ...abaseConfigurationBaseIntegrationTest.java | 16 ++-- .../persistence/util/DataSourceWrapper.java | 36 ++++++++ .../data/persistence/util/TestConfig.java | 14 +++ 7 files changed, 219 insertions(+), 5 deletions(-) create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/entity/Group.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/repository/GroupRepository.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/AdditionalLookupIntegrationTest.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/DataSourceWrapper.java create mode 100644 persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/TestConfig.java diff --git a/persistence-modules/spring-data-jpa-repo-4/pom.xml b/persistence-modules/spring-data-jpa-repo-4/pom.xml index 9b03f7fdfb..2995949df3 100644 --- a/persistence-modules/spring-data-jpa-repo-4/pom.xml +++ b/persistence-modules/spring-data-jpa-repo-4/pom.xml @@ -76,6 +76,16 @@ logback-classic ${logback.version} + + io.hypersistence + hypersistence-utils-hibernate-62 + ${hypersistence-utils.version} + + + com.vladmihalcea + db-util + ${db.util.version} + @@ -130,6 +140,8 @@ 3.7.0 42.7.1 + 1.0.7 + 3.7.0 \ No newline at end of file diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/entity/Group.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/entity/Group.java new file mode 100644 index 0000000000..d059b1a0e8 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/entity/Group.java @@ -0,0 +1,51 @@ +package com.baeldung.spring.data.persistence.findvsget.entity; + +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Entity +@Table(name = "group") +public class Group { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + @OneToOne + private User administrator; + @OneToMany(mappedBy = "id") + private Set users = new HashSet<>(); + public void addUser(User user) { + users.add(user); + } + public Set getUsers() { + return users; + } + public void setUsers(Set users) { + this.users = users; + } + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public User getAdministrator() { + return administrator; + } + public void setAdministrator(User administrator) { + this.administrator = administrator; + } +} diff --git a/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/repository/GroupRepository.java b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/repository/GroupRepository.java new file mode 100644 index 0000000000..e846f1ee48 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/main/java/com/baeldung/spring/data/persistence/findvsget/repository/GroupRepository.java @@ -0,0 +1,8 @@ +package com.baeldung.spring.data.persistence.findvsget.repository; + +import com.baeldung.spring.data.persistence.findvsget.entity.Group; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface GroupRepository extends JpaRepository { + +} diff --git a/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/AdditionalLookupIntegrationTest.java b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/AdditionalLookupIntegrationTest.java new file mode 100644 index 0000000000..660210d990 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/AdditionalLookupIntegrationTest.java @@ -0,0 +1,87 @@ +package com.baeldung.spring.data.persistence.findvsget; + +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertInsertCount; +import static com.vladmihalcea.sql.SQLStatementCountValidator.assertSelectCount; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import com.baeldung.spring.data.persistence.findvsget.entity.Group; +import com.baeldung.spring.data.persistence.findvsget.entity.User; +import com.baeldung.spring.data.persistence.findvsget.repository.GroupRepository; +import com.baeldung.spring.data.persistence.findvsget.repository.SimpleUserRepository; +import io.hypersistence.utils.jdbc.validator.SQLStatementCountValidator; +import java.util.Optional; +import org.hibernate.LazyInitializationException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; + +class AdditionalLookupIntegrationTest extends DatabaseConfigurationBaseIntegrationTest { + + @Autowired + private SimpleUserRepository userRepository; + @Autowired + private GroupRepository groupRepository; + + @BeforeEach + void setup() { + SQLStatementCountValidator.reset(); + } + + @Test + void givenEmptyGroup_whenAssigningAdministratorWithGetByReference_thenNoAdditionalLookupHappens() { + User user = userRepository.getReferenceById(1L); + Group group = new Group(); + group.setAdministrator(user); + groupRepository.save(group); + assertSelectCount(0); + assertInsertCount(1); + } + + @Test + void givenEmptyGroup_whenAssigningIncorrectAdministratorWithGetByReference_thenErrorIsThrown() { + User user = userRepository.getReferenceById(-1L); + Group group = new Group(); + group.setAdministrator(user); + assertThatExceptionOfType(DataIntegrityViolationException.class) + .isThrownBy(() -> { + groupRepository.save(group); + }); + assertSelectCount(0); + } + + @Test + void givenEmptyGroup_whenAssigningAdministratorWithFindBy_thenAdditionalLookupHappens() { + Optional optionalUser = userRepository.findById(1L); + assertThat(optionalUser).isPresent(); + User user = optionalUser.get(); + Group group = new Group(); + group.setAdministrator(user); + groupRepository.save(group); + assertSelectCount(2); + assertInsertCount(1); + } + + @Test + void givenEmptyGroup_whenAddingUserWithGetByReference_thenTryToAccessInternalsAndThrowError() { + User user = userRepository.getReferenceById(1L); + Group group = new Group(); + assertThatExceptionOfType(LazyInitializationException.class) + .isThrownBy(() -> { + group.addUser(user); + }); + } + + @Test + void givenEmptyGroup_whenAddingUserWithFindBy_thenAdditionalLookupHappens() { + Optional optionalUser = userRepository.findById(1L); + assertThat(optionalUser).isPresent(); + User user = optionalUser.get(); + Group group = new Group(); + group.addUser(user); + groupRepository.save(group); + assertSelectCount(1); + assertInsertCount(1); + } +} diff --git a/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/DatabaseConfigurationBaseIntegrationTest.java b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/DatabaseConfigurationBaseIntegrationTest.java index 3ac46c3a39..853cbb5a0f 100644 --- a/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/DatabaseConfigurationBaseIntegrationTest.java +++ b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/findvsget/DatabaseConfigurationBaseIntegrationTest.java @@ -4,7 +4,9 @@ import static com.baeldung.spring.data.persistence.findvsget.UserProvider.userSo import static org.assertj.core.api.Assumptions.assumeThat; import com.baeldung.spring.data.persistence.findvsget.entity.User; +import com.baeldung.spring.data.persistence.findvsget.repository.GroupRepository; import com.baeldung.spring.data.persistence.findvsget.repository.SimpleUserRepository; +import com.baeldung.spring.data.persistence.util.TestConfig; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.AfterEach; @@ -13,7 +15,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -@SpringBootTest(classes = ApplicationConfig.class, properties = { +@SpringBootTest(classes = {ApplicationConfig.class, TestConfig.class}, properties = { "spring.jpa.generate-ddl=true", "spring.jpa.show-sql=false" }) @@ -22,7 +24,10 @@ abstract class DatabaseConfigurationBaseIntegrationTest { private static final int NUMBER_OF_USERS = 10; @Autowired - private SimpleUserRepository repository; + private SimpleUserRepository userRepository; + + @Autowired + private GroupRepository groupRepository; @BeforeEach void populateDatabase() { @@ -30,13 +35,14 @@ abstract class DatabaseConfigurationBaseIntegrationTest { .map(Arguments::get) .map(s -> new User(((Long) s[0]), s[1].toString(), s[2].toString())) .collect(Collectors.toList()); - repository.saveAll(users); - assumeThat(repository.findAll()).hasSize(NUMBER_OF_USERS); + userRepository.saveAll(users); + assumeThat(userRepository.findAll()).hasSize(NUMBER_OF_USERS); } @AfterEach void clearDatabase() { - repository.deleteAll(); + groupRepository.deleteAll(); + userRepository.deleteAll(); } } diff --git a/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/DataSourceWrapper.java b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/DataSourceWrapper.java new file mode 100644 index 0000000000..41a817cc28 --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/DataSourceWrapper.java @@ -0,0 +1,36 @@ +package com.baeldung.spring.data.persistence.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) { + DataSource originalDataSource = ((DataSource) bean); + 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-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/TestConfig.java b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/TestConfig.java new file mode 100644 index 0000000000..082a22464c --- /dev/null +++ b/persistence-modules/spring-data-jpa-repo-4/src/test/java/com/baeldung/spring/data/persistence/util/TestConfig.java @@ -0,0 +1,14 @@ +package com.baeldung.spring.data.persistence.util; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestConfig { + + @Bean + public DataSourceWrapper dataSourceWrapper() { + return new DataSourceWrapper(); + } + +}