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();
+ }
+
+}