* BAEL-7281: When use getOne and findOne methods Spring Data JPA

* BAEL-7281: Cleanup

* BAEL-7281: Test rename

* BAEL-7281: Fix not updated name
This commit is contained in:
Eugene Kovko 2023-12-29 19:15:39 +01:00 committed by GitHub
parent cf80b7c3d4
commit e316c193a0
11 changed files with 459 additions and 0 deletions

View File

@ -0,0 +1,8 @@
package com.baeldung.spring.data.persistence.findvsget;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApplicationConfig {
}

View File

@ -0,0 +1,90 @@
package com.baeldung.spring.data.persistence.findvsget.entity;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "users")
public class User {
@Id
@Column(name = "id")
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "second_name")
private String secondName;
public User() {
}
public User(final Long id, final String firstName, final String secondName) {
this.id = id;
this.firstName = firstName;
this.secondName = secondName;
}
public void setId(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(final String firstName) {
this.firstName = firstName;
}
public String getSecondName() {
return secondName;
}
public void setSecondName(final String secondName) {
this.secondName = secondName;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final User user = (User) o;
if (!Objects.equals(id, user.id)) {
return false;
}
if (!Objects.equals(firstName, user.firstName)) {
return false;
}
return Objects.equals(secondName, user.secondName);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
result = 31 * result + (secondName != null ? secondName.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", secondName='" + secondName + '\'' +
'}';
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.spring.data.persistence.findvsget.repository;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface NewTransactionUserRepository extends JpaRepository<User, Long> {
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
User getReferenceById(Long id);
@Override
Optional<User> findById(Long id);
}

View File

@ -0,0 +1,10 @@
package com.baeldung.spring.data.persistence.findvsget.repository;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SimpleUserRepository extends JpaRepository<User, Long> {
}

View File

@ -0,0 +1,33 @@
package com.baeldung.spring.data.persistence.findvsget.service;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import com.baeldung.spring.data.persistence.findvsget.repository.SimpleUserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class NonTransactionalUserReferenceService {
private static final Logger log = LoggerFactory.getLogger(NonTransactionalUserReferenceService.class);
private SimpleUserRepository repository;
public User findUserReference(final long id) {
log.info("Before requesting a user");
final User user = repository.getReferenceById(id);
log.info("After requesting a user");
return user;
}
public User findAndUseUserReference(final long id) {
final User user = repository.getReferenceById(id);
log.info("Before accessing a username");
final String firstName = user.getFirstName();
log.info("This message shouldn't be displayed because of the thrown exception: {}", firstName);
return user;
}
public void setRepository(final SimpleUserRepository repository) {
this.repository = repository;
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.spring.data.persistence.findvsget.service;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import com.baeldung.spring.data.persistence.findvsget.repository.SimpleUserRepository;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class SimpleUserService {
private static final Logger log = LoggerFactory.getLogger(SimpleUserService.class);
private final SimpleUserRepository repository;
public SimpleUserService(final SimpleUserRepository repository) {
this.repository = repository;
}
public User findUser(final long id) {
log.info("Before requesting a user in a findUser method");
final Optional<User> optionalUser = repository.findById(id);
log.info("After requesting a user in a findUser method");
final User user = optionalUser.orElse(null);
log.info("After unwrapping an optional in a findUser method");
return user;
}
}

View File

@ -0,0 +1,36 @@
package com.baeldung.spring.data.persistence.findvsget.service;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransactionalUserReferenceService {
private static final Logger log = LoggerFactory.getLogger(TransactionalUserReferenceService.class);
private JpaRepository<User, Long> repository;
@Transactional
public User findUserReference(final long id) {
log.info("Before requesting a user");
final User user = repository.getReferenceById(id);
log.info("After requesting a user");
return user;
}
@Transactional
public User findAndUseUserReference(final long id) {
final User user = repository.getReferenceById(id);
log.info("Before accessing a username");
final String firstName = user.getFirstName();
log.info("After accessing a username: {}", firstName);
return user;
}
public void setRepository(final JpaRepository<User, Long> repository) {
this.repository = repository;
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.spring.data.persistence.findvsget;
import static com.baeldung.spring.data.persistence.findvsget.UserProvider.userSource;
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.SimpleUserRepository;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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 = {
"spring.jpa.generate-ddl=true",
"spring.jpa.show-sql=false"
})
abstract class DatabaseConfigurationBaseIntegrationTest {
private static final int NUMBER_OF_USERS = 10;
@Autowired
private SimpleUserRepository repository;
@BeforeEach
void populateDatabase() {
final List<User> users = userSource()
.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);
}
@AfterEach
void clearDatabase() {
repository.deleteAll();
}
}

View File

@ -0,0 +1,37 @@
package com.baeldung.spring.data.persistence.findvsget;
import static org.assertj.core.api.Assertions.assertThat;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import com.baeldung.spring.data.persistence.findvsget.service.SimpleUserService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
@DisplayName("findBy test:")
class FindUserIntegrationIntegrationTest extends DatabaseConfigurationBaseIntegrationTest {
@Autowired
private SimpleUserService service;
@ParameterizedTest
@ArgumentsSource(UserProvider.class)
@DisplayName("when looking for a user by an existing ID returns a user")
void whenGettingUserByCorrectIdThenReturnUser(Long id, String firstName, String lastName) {
final User expected = new User(id, firstName, lastName);
final User actual = service.findUser(id);
assertThat(actual).isEqualTo(expected);
}
@ParameterizedTest
@DisplayName("when looking for a user by a non-existing ID returns null")
@ValueSource(longs = {11, 12, 13})
void whenGettingUserByIncorrectIdThenReturnNull(Long id) {
assertThat(service.findUser(id)).isNull();
}
}

View File

@ -0,0 +1,125 @@
package com.baeldung.spring.data.persistence.findvsget;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import com.baeldung.spring.data.persistence.findvsget.entity.User;
import com.baeldung.spring.data.persistence.findvsget.repository.NewTransactionUserRepository;
import com.baeldung.spring.data.persistence.findvsget.repository.SimpleUserRepository;
import com.baeldung.spring.data.persistence.findvsget.service.NonTransactionalUserReferenceService;
import com.baeldung.spring.data.persistence.findvsget.service.TransactionalUserReferenceService;
import org.hibernate.LazyInitializationException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.springframework.beans.factory.annotation.Autowired;
@DisplayName("getReferenceBy test:")
class GetReferenceIntegrationIntegrationTest extends DatabaseConfigurationBaseIntegrationTest {
private static final long EXISTING_ID = 1L;
@Nested
@DisplayName("given non-transactional service, even if user exists")
class GivenNonTransactionalService {
@Autowired
private NonTransactionalUserReferenceService nonTransactionalService;
@BeforeEach
void configureService(@Autowired SimpleUserRepository repository) {
nonTransactionalService.setRepository(repository);
}
@Test
void whenFindUserReferenceUsingOutsideServiceThenThrowsException() {
final User user = nonTransactionalService.findUserReference(EXISTING_ID);
assertThatExceptionOfType(LazyInitializationException.class)
.isThrownBy(user::getFirstName);
}
@Test
void whenFindUserReferenceNotUsingOutsideServiceThenDontThrowException() {
final User user = nonTransactionalService.findUserReference(EXISTING_ID);
assertThat(user).isNotNull();
}
@Test
void whenFindUserReferenceUsingInsideServiceThenThrowsException() {
assertThatExceptionOfType(LazyInitializationException.class)
.isThrownBy(() -> nonTransactionalService.findAndUseUserReference(EXISTING_ID));
}
}
@Nested
@DisplayName("given transactional service with simple repository, even if user exists")
class GivenTransactionalService {
@Autowired
private TransactionalUserReferenceService transactionalService;
@BeforeEach
void configureService(@Autowired SimpleUserRepository repository) {
transactionalService.setRepository(repository);
}
@Test
void whenFindUserReferenceUsingOutsideServiceThenThrowsException() {
final User user = transactionalService.findUserReference(EXISTING_ID);
assertThatExceptionOfType(LazyInitializationException.class)
.isThrownBy(user::getFirstName);
}
@Test
void whenFindUserReferenceNotUsingOutsideServiceThenDontThrowException() {
final User user = transactionalService.findUserReference(EXISTING_ID);
assertThat(user).isNotNull();
}
@ParameterizedTest
@ArgumentsSource(UserProvider.class)
void whenFindUserReferenceUsingInsideServiceThenReturnsUser(Long id, String firstName, String lastName) {
final User expected = new User(id, firstName, lastName);
final User actual = transactionalService.findAndUseUserReference(id);
assertThat(actual).isEqualTo(expected);
}
}
@Nested
@DisplayName("given transactional service with new transaction repository, even if user exists")
class GivenTransactionalServiceWithNewTransactionRepository {
@Autowired
private TransactionalUserReferenceService transactionalServiceWithNewTransactionRepository;
@BeforeEach
void configureService(@Autowired NewTransactionUserRepository repository) {
transactionalServiceWithNewTransactionRepository.setRepository(repository);
}
@Test
void whenFindUserReferenceUsingOutsideServiceThenThrowsException() {
final User user = transactionalServiceWithNewTransactionRepository
.findUserReference(EXISTING_ID);
assertThatExceptionOfType(LazyInitializationException.class)
.isThrownBy(user::getFirstName);
}
@Test
void whenFindUserReferenceNotUsingOutsideServiceThenDontThrowException() {
final User user = transactionalServiceWithNewTransactionRepository.findUserReference(EXISTING_ID);
assertThat(user).isNotNull();
}
@Test
void whenFindUserReferenceUsingInsideServiceThenThrowsExceptionDueToSeparateTransactions() {
assertThatExceptionOfType(LazyInitializationException.class)
.isThrownBy(() -> transactionalServiceWithNewTransactionRepository
.findAndUseUserReference(EXISTING_ID));
}
}
}

View File

@ -0,0 +1,30 @@
package com.baeldung.spring.data.persistence.findvsget;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
public class UserProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(final ExtensionContext context) {
return userSource();
}
static Stream<Arguments> userSource() {
return Stream.of(
Arguments.of(1L, "Saundra", "Krystek"),
Arguments.of(2L, "Korey", "Venners"),
Arguments.of(3L, "Lory", "Daffey"),
Arguments.of(4L, "Michail", "Spinella"),
Arguments.of(5L, "Emanuel", "Geertje"),
Arguments.of(6L, "Jervis", "Waugh"),
Arguments.of(7L, "Chantal", "Soldan"),
Arguments.of(8L, "Darnall", "Fanner"),
Arguments.of(9L, "Cordelia", "Hindge"),
Arguments.of(10L, "Lem", "Pitcock")
);
}
}