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
This commit is contained in:
Eugene Kovko 2024-02-29 21:50:38 +01:00 committed by GitHub
parent c1fe1fd285
commit f7bd3d50f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 219 additions and 5 deletions

View File

@ -76,6 +76,16 @@
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>io.hypersistence</groupId>
<artifactId>hypersistence-utils-hibernate-62</artifactId>
<version>${hypersistence-utils.version}</version>
</dependency>
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>db-util</artifactId>
<version>${db.util.version}</version>
</dependency>
</dependencies>
<build>
@ -130,6 +140,8 @@
<properties>
<hypersistance-utils-hibernate-55.version>3.7.0</hypersistance-utils-hibernate-55.version>
<postgresql.version>42.7.1</postgresql.version>
<db.util.version>1.0.7</db.util.version>
<hypersistence-utils.version>3.7.0</hypersistence-utils.version>
</properties>
</project>

View File

@ -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<User> users = new HashSet<>();
public void addUser(User user) {
users.add(user);
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> 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;
}
}

View File

@ -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<Group, Long> {
}

View File

@ -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<User> 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<User> 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);
}
}

View File

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

View File

@ -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;
}
}

View File

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