├── .gitignore ├── spring-data-jpa-exercises-model ├── src │ └── main │ │ └── java │ │ └── com │ │ └── bobocode │ │ └── model │ │ ├── Gender.java │ │ ├── RoleType.java │ │ ├── Address.java │ │ ├── Role.java │ │ └── User.java └── pom.xml ├── user-service ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bobocode │ │ │ ├── exception │ │ │ └── EntityNotFoundException.java │ │ │ ├── config │ │ │ ├── RootConfig.java │ │ │ └── JpaConfig.java │ │ │ ├── dao │ │ │ ├── CustomUserRepository.java │ │ │ └── UserRepository.java │ │ │ └── service │ │ │ └── UserService.java │ └── test │ │ └── java │ │ └── com │ │ └── bobocode │ │ └── UserServiceAppTest.java ├── pom.xml └── README.MD ├── hello-jpa-repository ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bobocode │ │ │ ├── dao │ │ │ └── UserRepository.java │ │ │ └── config │ │ │ ├── RootConfig.java │ │ │ └── JpaConfig.java │ └── test │ │ └── java │ │ └── com │ │ └── bobocode │ │ └── SpringDataJpaConfigTest.java ├── pom.xml └── README.MD ├── README.md ├── spring-data-jpa-exercises-util ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── bobocode │ └── util │ └── TestDataGenerator.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | **/*.iml 3 | **/target 4 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/src/main/java/com/bobocode/model/Gender.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | public enum Gender { 4 | MALE, 5 | FEMALE 6 | } 7 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/src/main/java/com/bobocode/model/RoleType.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | public enum RoleType { 4 | USER, ADMIN, OPERATOR, CUSTOMER 5 | } 6 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/exception/EntityNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.exception; 2 | 3 | public class EntityNotFoundException extends RuntimeException{ 4 | public EntityNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /hello-jpa-repository/src/main/java/com/bobocode/dao/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.dao; 2 | 3 | import com.bobocode.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * This interface represents a data access object (DAO) for {@link User}. 8 | *

9 | * todo: 1. Configure {@link UserRepository} as {@link JpaRepository} for class User 10 | */ 11 | public interface UserRepository extends JpaRepository { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/config/RootConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | import org.springframework.transaction.PlatformTransactionManager; 4 | 5 | /** 6 | * This class is provides root Java config for Spring application. 7 | *

8 | * todo: 0. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 9 | *

10 | * todo: 1. Configure {@link PlatformTransactionManager} bean with name "transactionManager" 11 | * todo: 2. Enable transaction management 12 | */ 13 | public class RootConfig { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /hello-jpa-repository/src/main/java/com/bobocode/config/RootConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | import org.springframework.transaction.PlatformTransactionManager; 4 | 5 | /** 6 | * This class is provides root Java config for Spring application. 7 | *

8 | * todo: 1. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 9 | *

10 | * todo: 2. Configure {@link PlatformTransactionManager} bean with name "transactionManager" 11 | * todo: 3. Enable transaction management 12 | */ 13 | public class RootConfig { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-data-jpa-exercises 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-data-jpa-exercises-model 13 | 14 | 15 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/dao/CustomUserRepository.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.dao; 2 | 3 | import com.bobocode.model.RoleType; 4 | 5 | /** 6 | * This class declares custom {@link UserRepository} methods 7 | *

8 | * todo: 0. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 9 | *

10 | * todo: 1. Create class called "CustomUserRepositoryImpl" 11 | * todo: 2. Mark that class {@link org.springframework.stereotype.Repository} 12 | * todo: 3. Mark that class {@link org.springframework.transaction.annotation.Transactional} 13 | * todo: 4. Implement method {@link CustomUserRepository#addRoleToAllUsers(RoleType)} 14 | */ 15 | public interface CustomUserRepository { 16 | void addRoleToAllUsers(RoleType roleType); 17 | } 18 | -------------------------------------------------------------------------------- /user-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-data-jpa-exercises 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | user-service 13 | 14 | 15 | 16 | com.bobocode 17 | spring-data-jpa-exercises-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /hello-jpa-repository/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-data-jpa-exercises 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | hello-jpa-repository 13 | 14 | 15 | 16 | com.bobocode 17 | spring-data-jpa-exercises-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/dao/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.dao; 2 | 3 | import com.bobocode.model.User; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * This interface represents a data access object (DAO) for {@link User}. 8 | *

9 | * todo: 0. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 10 | *

11 | * todo: 1. Configure {@link UserRepository} as {@link JpaRepository} for class User 12 | * todo: 2. Create method that finds a list of Users by address city using Spring Data method name convention 13 | * todo: 3. Create method that finds optional user by email fetching its address and roles using {@link org.springframework.data.jpa.repository.Query} 14 | * todo: 4. Add custom User repository interface 15 | */ 16 | public interface UserRepository extends JpaRepository{ 17 | 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring Data JPA exercises 2 | The list of exercises dedicated to training your *Spring Data JPA* skills 3 | 4 | ### No pain, No gain :heavy_exclamation_mark: 5 | 6 | > Skill is only developed by hours and hours and hours of beating on your craft 7 | 8 | Working on real problems, you're focused on finding a solution. Learning new things, you're trying to understand how it works. 9 | It is important to have a different type of activities, which purpose is improving your skill 10 | 11 | ***An exercise** is a predefined task that you continuously implement to improve a certain skill* :muscle: 12 | ## 13 | * [Hello JPA Repository](https://github.com/bobocode-projects/spring-data-jpa-exercises/tree/master/hello-jpa-repository) 14 | * [User Service](https://github.com/bobocode-projects/spring-data-jpa-exercises/tree/master/user-service) -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/src/main/java/com/bobocode/model/Address.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | 4 | import lombok.*; 5 | 6 | import javax.persistence.*; 7 | import java.time.LocalDateTime; 8 | 9 | @NoArgsConstructor 10 | @Getter 11 | @Setter 12 | @EqualsAndHashCode(of = "id") 13 | @ToString(exclude = "user") 14 | @Entity 15 | @Table(name = "address") 16 | public class Address { 17 | @Id 18 | @GeneratedValue 19 | private Long id; 20 | 21 | @Column(name = "city") 22 | private String city; 23 | 24 | @Column(name = "street") 25 | private String street; 26 | 27 | @Column(name = "street_number") 28 | private String streetNumber; 29 | 30 | @Column(name = "apartment_number") 31 | private String apartmentNumber; 32 | 33 | @Column(name = "zip_code") 34 | private String zipCode; 35 | 36 | @Column(name = "creation_date") 37 | private LocalDateTime creationDate; 38 | 39 | @OneToOne 40 | @JoinColumn(name = "user_id") 41 | private User user; 42 | } 43 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-util/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-data-jpa-exercises 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-data-jpa-exercises-util 13 | 14 | 15 | 16 | com.bobocode 17 | spring-data-jpa-exercises-model 18 | 1.0-SNAPSHOT 19 | 20 | 21 | io.codearte.jfairy 22 | jfairy 23 | 0.5.7 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/config/JpaConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 4 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 5 | import org.springframework.orm.jpa.JpaVendorAdapter; 6 | 7 | import javax.sql.DataSource; 8 | 9 | /** 10 | * This class provides spring configuration for {@link javax.persistence.EntityManagerFactory} bean. 11 | *

12 | * todo: 0. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 13 | *

14 | * todo: 1. Configure {@link DataSource} bean 15 | * todo: 2. Configure {@link JpaVendorAdapter} bean 16 | * todo: 3. Configure {@link javax.persistence.EntityManagerFactory} bean with name "entityManagerFactory" 17 | * todo: 4. Enable JPA repository 18 | */ 19 | public class JpaConfig { 20 | 21 | public DataSource dataSource() { 22 | return new EmbeddedDatabaseBuilder() 23 | .setType(EmbeddedDatabaseType.H2) 24 | .build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /user-service/README.MD: -------------------------------------------------------------------------------- 1 | # User Service exercise :muscle: 2 | Improve your *Spring Data JPA* Java configuration skills 3 | ### Task 4 | The task is to **implement `UserService` using `UserRepository`**. In order to do that, you need to **provide all required 5 | configuration, implement methods, and create custom repository**. Please follow the instructions in the *todo* section. 6 | 7 | To verify your configuration, run `UserServiceAppTest.java` 8 | 9 | 10 | ### Pre-conditions :heavy_exclamation_mark: 11 | You're supposed to be familiar with *Spring IoC* and *Dependency injection* 12 | 13 | ### How to start :question: 14 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests 15 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source) 16 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation 17 | 18 | ### Related materials :information_source: 19 | * [Spring Data JPA basics tutorial](https://github.com/bobocode-projects/spring-data-jpa-tutorial/tree/master/jpa-repository-basics) 20 | 21 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/src/main/java/com/bobocode/model/Role.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | import javax.persistence.*; 9 | import java.time.LocalDateTime; 10 | import java.util.Objects; 11 | 12 | @NoArgsConstructor 13 | @Getter 14 | @Setter 15 | @ToString(exclude = "user") 16 | @Entity 17 | @Table(name = "role") 18 | public class Role { 19 | @Id 20 | @GeneratedValue 21 | private Long id; 22 | 23 | @Enumerated(EnumType.STRING) 24 | @Column(name = "role_type") 25 | private RoleType roleType; 26 | 27 | @Column(name = "creation_date") 28 | private LocalDateTime creationDate = LocalDateTime.now(); 29 | 30 | @ManyToOne 31 | @JoinColumn(name = "user_id") 32 | private User user; 33 | 34 | public static Role valueOf(RoleType roleType) { 35 | return new Role(roleType); 36 | } 37 | 38 | private Role(RoleType roleType) { 39 | this.roleType = roleType; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) return true; 45 | if (!(o instanceof Role)) return false; 46 | 47 | Role role = (Role) o; 48 | return id != null && id.equals(role.id); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return 31; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hello-jpa-repository/README.MD: -------------------------------------------------------------------------------- 1 | # Hello JpaRepository exercise :muscle: 2 | Improve your *Spring Data JPA* Java configuration skills 3 | ### Task 4 | The task is to **configure Spring Data JPA repository** for `User`. In order to do that you need to **configure 5 | `EntityManagerFactory`**, **enable transaction management** and **create a JPA repository**. Please follow 6 | the instruction in the *todo* sections. 7 | 8 | To verify your configuration, run `SpringDataJpaConfigTest.java` 9 | 10 | 11 | ### Pre-conditions :heavy_exclamation_mark: 12 | You're supposed to be familiar with *Spring IoC* and *Dependency injection* 13 | 14 | ### How to start :question: 15 | * Just clone the repository and start implementing the **todo** section, verify your changes by running tests 16 | * If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source) 17 | * Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation 18 | 19 | ### Related materials :information_source: 20 | * [Spring Data JPA basics tutorial](https://github.com/bobocode-projects/spring-data-jpa-tutorial/tree/master/jpa-repository-basics) 21 | 22 | -------------------------------------------------------------------------------- /user-service/src/main/java/com/bobocode/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.service; 2 | 3 | import com.bobocode.dao.UserRepository; 4 | import com.bobocode.exception.EntityNotFoundException; 5 | import com.bobocode.model.RoleType; 6 | import com.bobocode.model.User; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * This class provides service logic for {@link User}. 12 | *

13 | * todo: 0. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 14 | *

15 | * todo: 1. Implement {@link UserService#findByCity(String)} using {@link UserRepository}, make method read only 16 | * todo: 2. Implement {@link UserService#getByEmail(String)} using {@link UserRepository}, make method read only 17 | * todo: 3. In case user is not found by email, throw {@link EntityNotFoundException} with message "Cannot find user by email ${email}" 18 | * todo: 4. Implement {@link UserService#addRoleToAllUsers(RoleType)} using {@link UserRepository} 19 | */ 20 | public class UserService { 21 | public List findByCity(String city) { 22 | throw new UnsupportedOperationException("Do your best and implement this method!"); 23 | } 24 | 25 | public User getByEmail(String email) { 26 | throw new UnsupportedOperationException("Do your best and implement this method!"); 27 | } 28 | 29 | public void addRoleToAllUsers(RoleType roleType) { 30 | throw new UnsupportedOperationException("Do your best and implement this method!"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /hello-jpa-repository/src/main/java/com/bobocode/config/JpaConfig.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.config; 2 | 3 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 4 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 5 | import org.springframework.orm.jpa.JpaVendorAdapter; 6 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 7 | 8 | import javax.sql.DataSource; 9 | 10 | /** 11 | * This class provides spring configuration for {@link javax.persistence.EntityManagerFactory} bean. 12 | *

13 | * todo: 1. PLEASE NOTE, THAT SOME REQUIRED STEPS ARE OMITTED IN THE TODO LIST AND YOU HAVE TO DO IT ON YOUR OWN 14 | *

15 | * todo: 2. Configure {@link DataSource} bean 16 | * todo: 3. Configure {@link JpaVendorAdapter} bean 17 | * todo: 3. Configure {@link javax.persistence.EntityManagerFactory} bean with name "entityManagerFactory" 18 | * todo: 4. Enable JPA repository, set appropriate package using annotation property "basePackages" 19 | */ 20 | public class JpaConfig { 21 | 22 | public DataSource dataSource() { 23 | return new EmbeddedDatabaseBuilder() 24 | .setType(EmbeddedDatabaseType.H2) 25 | .build(); 26 | } 27 | 28 | public JpaVendorAdapter jpaVendorAdapter() { 29 | // todo: create HibernateJpaVendorAdapter 30 | // todo: set H2 database 31 | // todo: enable DDL generation 32 | throw new UnsupportedOperationException("Application won't start until you provide configs"); 33 | } 34 | 35 | public LocalContainerEntityManagerFactoryBean localContainerEMF() { 36 | // todo: create and configure required bean 37 | // todo: set package "com.bobocode.model" to scan for JPA entities 38 | throw new UnsupportedOperationException("Application won't start until you provide configs"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-model/src/main/java/com/bobocode/model/User.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.time.LocalDate; 7 | import java.util.HashSet; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | 12 | @NoArgsConstructor 13 | @Getter 14 | @Setter 15 | @ToString 16 | @EqualsAndHashCode(of = "id") 17 | @Entity 18 | @Table(name = "user") 19 | public class User { 20 | @Id 21 | @GeneratedValue 22 | private Long id; 23 | 24 | @Column(name = "first_name") 25 | private String firstName; 26 | 27 | @Column(name = "last_name") 28 | private String lastName; 29 | 30 | @Column(name = "email") 31 | private String email; 32 | 33 | @Column(name = "birthday") 34 | private LocalDate birthday; 35 | 36 | @Column(name = "creation_date") 37 | private LocalDate creationDate; 38 | 39 | @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) 40 | private Address address; 41 | 42 | @Setter(AccessLevel.PRIVATE) 43 | @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) 44 | private Set roles = new HashSet<>(); 45 | 46 | public void setAddress(Address address) { 47 | if (address != null) { 48 | address.setUser(this); 49 | } else if (this.address != null) { 50 | this.address.setUser(null); 51 | } 52 | this.address = address; 53 | } 54 | 55 | public void addRole(Role role) { 56 | roles.add(role); 57 | role.setUser(this); 58 | } 59 | 60 | public void addRoles(List roles) { 61 | this.roles.addAll(roles); 62 | roles.forEach(role -> role.setUser(this)); 63 | } 64 | 65 | public void removeRole(Role role) { 66 | this.roles.remove(role); 67 | role.setUser(null); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spring-data-jpa-exercises-util/src/main/java/com/bobocode/util/TestDataGenerator.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.util; 2 | 3 | 4 | import com.bobocode.model.Address; 5 | import com.bobocode.model.Role; 6 | import com.bobocode.model.RoleType; 7 | import com.bobocode.model.User; 8 | import io.codearte.jfairy.Fairy; 9 | import io.codearte.jfairy.producer.person.Person; 10 | 11 | import java.time.LocalDate; 12 | import java.time.LocalDateTime; 13 | import java.util.List; 14 | import java.util.Random; 15 | import java.util.function.Predicate; 16 | import java.util.stream.Stream; 17 | 18 | import static java.util.stream.Collectors.toList; 19 | 20 | public class TestDataGenerator { 21 | 22 | private List generateRoleList() { 23 | Random random = new Random(); 24 | Predicate randomPredicate = i -> random.nextBoolean(); 25 | 26 | return Stream.of(RoleType.values()) 27 | .filter(randomPredicate) 28 | .map(Role::valueOf) 29 | .collect(toList()); 30 | } 31 | 32 | public User generateUser(RoleType... roles) { 33 | User user = generateUserWithoutRoles(); 34 | Stream.of(roles) 35 | .map(Role::valueOf) 36 | .forEach(user::addRole); 37 | 38 | return user; 39 | } 40 | 41 | public User generateUserWithoutRoles() { 42 | Fairy fairy = Fairy.create(); 43 | Person person = fairy.person(); 44 | 45 | User user = new User(); 46 | user.setFirstName(person.getFirstName()); 47 | user.setLastName(person.getLastName()); 48 | user.setEmail(person.getEmail()); 49 | user.setBirthday(LocalDate.of( 50 | person.getDateOfBirth().getYear(), 51 | person.getDateOfBirth().getMonthOfYear(), 52 | person.getDateOfBirth().getDayOfMonth())); 53 | user.setCreationDate(LocalDate.now()); 54 | 55 | Address address = generateAddress(); 56 | user.setAddress(address); 57 | 58 | return user; 59 | } 60 | 61 | 62 | public User generateUser() { 63 | User user = generateUserWithoutRoles(); 64 | user.addRoles(generateRoleList()); 65 | 66 | return user; 67 | } 68 | 69 | private Address generateAddress() { 70 | Fairy fairy = Fairy.create(); 71 | Person person = fairy.person(); 72 | 73 | Address address = new Address(); 74 | address.setCity(person.getAddress().getCity()); 75 | address.setStreet(person.getAddress().getStreet()); 76 | address.setStreetNumber(person.getAddress().getStreetNumber()); 77 | address.setApartmentNumber(person.getAddress().getApartmentNumber()); 78 | address.setCreationDate(LocalDateTime.now()); 79 | address.setZipCode(person.getAddress().getPostalCode()); 80 | 81 | return address; 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.bobocode 8 | spring-data-jpa-exercises 9 | 1.0-SNAPSHOT 10 | 11 | spring-data-jpa-exercises-model 12 | spring-data-jpa-exercises-util 13 | hello-jpa-repository 14 | user-service 15 | 16 | 17 | pom 18 | 19 | 20 | 11 21 | 11 22 | 23 | 24 | 25 | 26 | org.springframework.data 27 | spring-data-jpa 28 | 2.1.0.RELEASE 29 | 30 | 31 | org.springframework 32 | spring-test 33 | 5.0.7.RELEASE 34 | 35 | 36 | com.h2database 37 | h2 38 | 1.4.197 39 | 40 | 41 | org.slf4j 42 | slf4j-simple 43 | 1.7.24 44 | 45 | 46 | org.hibernate.javax.persistence 47 | hibernate-jpa-2.1-api 48 | 1.0.2.Final 49 | 50 | 51 | org.hibernate 52 | hibernate-core 53 | 5.3.2.Final 54 | 55 | 56 | org.junit.jupiter 57 | junit-jupiter-engine 58 | 5.2.0 59 | test 60 | 61 | 62 | org.junit.platform 63 | junit-platform-launcher 64 | 1.3.1 65 | test 66 | 67 | 68 | org.hamcrest 69 | hamcrest-all 70 | 1.3 71 | test 72 | 73 | 74 | 75 | org.projectlombok 76 | lombok 77 | 1.18.2 78 | 79 | 80 | 81 | 82 | 83 | javax.xml.bind 84 | jaxb-api 85 | 2.2.11 86 | 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-surefire-plugin 94 | 2.22.0 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /hello-jpa-repository/src/test/java/com/bobocode/SpringDataJpaConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.config.JpaConfig; 4 | import com.bobocode.config.RootConfig; 5 | import com.bobocode.dao.UserRepository; 6 | import com.bobocode.model.User; 7 | import com.bobocode.util.TestDataGenerator; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.ComponentScan; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 15 | import org.springframework.stereotype.Repository; 16 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 17 | import org.springframework.transaction.PlatformTransactionManager; 18 | import org.springframework.transaction.annotation.Transactional; 19 | 20 | import javax.persistence.EntityManagerFactory; 21 | import java.util.List; 22 | import java.util.stream.Stream; 23 | 24 | import static org.hamcrest.MatcherAssert.assertThat; 25 | import static org.hamcrest.Matchers.array; 26 | import static org.hamcrest.Matchers.equalTo; 27 | import static org.hamcrest.Matchers.is; 28 | import static org.hamcrest.Matchers.nullValue; 29 | import static org.hamcrest.core.IsNull.notNullValue; 30 | 31 | @SpringJUnitConfig(RootConfig.class) 32 | @Transactional 33 | class SpringDataJpaConfigTest { 34 | @Configuration 35 | static class TestConfig { 36 | @Bean 37 | TestDataGenerator dataGenerator() { 38 | return new TestDataGenerator(); 39 | } 40 | } 41 | 42 | @Autowired 43 | private ApplicationContext applicationContext; 44 | 45 | @Autowired 46 | private UserRepository userRepository; 47 | 48 | @Autowired 49 | private TestDataGenerator dataGenerator; 50 | 51 | @Test 52 | void testTxManagerBeanName() { 53 | PlatformTransactionManager transactionManager = applicationContext.getBean(PlatformTransactionManager.class, "transactionManager"); 54 | 55 | assertThat(transactionManager, notNullValue()); 56 | } 57 | 58 | @Test 59 | void testUserRepositoryBeanName() { 60 | UserRepository userRepository = applicationContext.getBean(UserRepository.class, "userRepository"); 61 | 62 | assertThat(userRepository, notNullValue()); 63 | } 64 | 65 | @Test 66 | void testEntityManagerFactoryBeanName() { 67 | EntityManagerFactory entityManagerFactory = applicationContext.getBean(EntityManagerFactory.class, "entityManagerFactory"); 68 | 69 | assertThat(entityManagerFactory, notNullValue()); 70 | } 71 | 72 | @Test 73 | void testUserRepositoryIsNotMarkedAsRepository() { 74 | Repository repository = UserRepository.class.getAnnotation(Repository.class); 75 | 76 | assertThat(repository, nullValue()); 77 | } 78 | 79 | @Test 80 | void testRootConfigComponentScan() { 81 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class); 82 | 83 | String[] basePackages = componentScan.basePackages(); 84 | if (basePackages.length == 0) { 85 | basePackages = componentScan.value(); 86 | } 87 | 88 | assertThat(basePackages, array(equalTo("com.bobocode"))); 89 | } 90 | 91 | @Test 92 | void testJpaConfigRepositoriesPackage() { 93 | EnableJpaRepositories enableJpaRepositories = JpaConfig.class.getAnnotation(EnableJpaRepositories.class); 94 | 95 | assertThat(enableJpaRepositories.basePackages(), array(equalTo("com.bobocode.dao"))); 96 | } 97 | 98 | @Test 99 | void testSaveUser() { 100 | User user = dataGenerator.generateUser(); 101 | 102 | userRepository.save(user); 103 | 104 | assertThat(user.getId(), notNullValue()); 105 | } 106 | 107 | @Test 108 | void testFindAll() { 109 | Stream.generate(dataGenerator::generateUser).limit(10).forEach(userRepository::save); 110 | 111 | List users = userRepository.findAll(); 112 | 113 | assertThat(users.size(), is(10)); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /user-service/src/test/java/com/bobocode/UserServiceAppTest.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.config.JpaConfig; 4 | import com.bobocode.config.RootConfig; 5 | import com.bobocode.dao.UserRepository; 6 | import com.bobocode.exception.EntityNotFoundException; 7 | import com.bobocode.model.RoleType; 8 | import com.bobocode.model.User; 9 | import com.bobocode.service.UserService; 10 | import com.bobocode.util.TestDataGenerator; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.context.ApplicationContext; 14 | import org.springframework.context.annotation.Bean; 15 | import org.springframework.context.annotation.ComponentScan; 16 | import org.springframework.context.annotation.Configuration; 17 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 18 | import org.springframework.stereotype.Repository; 19 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 20 | import org.springframework.transaction.PlatformTransactionManager; 21 | import org.springframework.transaction.annotation.Transactional; 22 | 23 | import javax.persistence.EntityManager; 24 | import javax.persistence.EntityManagerFactory; 25 | import javax.persistence.Persistence; 26 | import javax.persistence.PersistenceContext; 27 | import java.util.List; 28 | import java.util.stream.Stream; 29 | 30 | import static java.util.stream.Collectors.toList; 31 | import static org.hamcrest.MatcherAssert.assertThat; 32 | import static org.hamcrest.Matchers.array; 33 | import static org.hamcrest.Matchers.equalTo; 34 | import static org.hamcrest.Matchers.everyItem; 35 | import static org.hamcrest.Matchers.greaterThan; 36 | import static org.hamcrest.Matchers.hasItem; 37 | import static org.hamcrest.Matchers.hasProperty; 38 | import static org.hamcrest.Matchers.hasSize; 39 | import static org.hamcrest.Matchers.is; 40 | import static org.hamcrest.Matchers.nullValue; 41 | import static org.hamcrest.core.IsNull.notNullValue; 42 | import static org.junit.jupiter.api.Assertions.assertTrue; 43 | import static org.junit.jupiter.api.Assertions.fail; 44 | 45 | @SpringJUnitConfig(RootConfig.class) 46 | @Transactional 47 | class UserServiceAppTest { 48 | @Configuration 49 | static class TestConfig { 50 | @Bean 51 | TestDataGenerator dataGenerator() { 52 | return new TestDataGenerator(); 53 | } 54 | } 55 | 56 | @Autowired 57 | private ApplicationContext applicationContext; 58 | 59 | @Autowired 60 | private UserRepository userRepository; 61 | 62 | @Autowired 63 | private TestDataGenerator dataGenerator; 64 | 65 | @Autowired 66 | private UserService userService; 67 | 68 | @PersistenceContext 69 | private EntityManager entityManager; 70 | 71 | @Test 72 | void testTxManagerBeanName() { 73 | PlatformTransactionManager transactionManager = applicationContext.getBean(PlatformTransactionManager.class, "transactionManager"); 74 | 75 | assertThat(transactionManager, notNullValue()); 76 | } 77 | 78 | @Test 79 | void testUserRepositoryBeanName() { 80 | UserRepository userRepository = applicationContext.getBean(UserRepository.class, "userRepository"); 81 | 82 | assertThat(userRepository, notNullValue()); 83 | } 84 | 85 | @Test 86 | void testEntityManagerFactoryBeanName() { 87 | EntityManagerFactory entityManagerFactory = applicationContext.getBean(EntityManagerFactory.class, "entityManagerFactory"); 88 | 89 | assertThat(entityManagerFactory, notNullValue()); 90 | } 91 | 92 | @Test 93 | void testUserRepositoryIsNotMarkedAsRepository() { 94 | Repository repository = UserRepository.class.getAnnotation(Repository.class); 95 | 96 | assertThat(repository, nullValue()); 97 | } 98 | 99 | @Test 100 | void testRootConfigComponentScan() { 101 | ComponentScan componentScan = RootConfig.class.getAnnotation(ComponentScan.class); 102 | 103 | String[] basePackages = componentScan.basePackages(); 104 | if (basePackages.length == 0) { 105 | basePackages = componentScan.value(); 106 | } 107 | 108 | assertThat(basePackages, array(equalTo("com.bobocode"))); 109 | } 110 | 111 | @Test 112 | void testJpaConfigRepositoriesPackage() { 113 | EnableJpaRepositories enableJpaRepositories = JpaConfig.class.getAnnotation(EnableJpaRepositories.class); 114 | 115 | String[] basePackages = enableJpaRepositories.basePackages(); 116 | if (basePackages.length == 0) { 117 | basePackages = enableJpaRepositories.value(); 118 | } 119 | 120 | assertThat(basePackages, array(equalTo("com.bobocode.dao"))); 121 | } 122 | 123 | 124 | @Test 125 | void testFindUsersByCity() { 126 | List userList = Stream.generate(dataGenerator::generateUser).limit(10).collect(toList()); 127 | userRepository.saveAll(userList); 128 | entityManager.flush(); 129 | userList.forEach(entityManager::detach); 130 | 131 | String city = userList.get(0).getAddress().getCity(); 132 | List cityUsers = userService.findByCity(city); 133 | 134 | assertThat(cityUsers, hasSize(greaterThan(0))); 135 | assertThat(cityUsers, everyItem(hasProperty("address", hasProperty("city", equalTo(city))))); 136 | } 137 | 138 | @Test 139 | void testFindUsersByCityIsReadOnly() throws NoSuchMethodException { 140 | Transactional transactional = UserService.class.getMethod("findByCity", String.class) 141 | .getAnnotation(Transactional.class); 142 | 143 | assertThat(transactional.readOnly(), is(true)); 144 | } 145 | 146 | @Test 147 | void testGetUserByEmail() { 148 | User generatedUser = dataGenerator.generateUser(); 149 | userRepository.save(generatedUser); 150 | entityManager.flush(); 151 | entityManager.detach(generatedUser); 152 | 153 | User foundUser = userService.getByEmail(generatedUser.getEmail()); 154 | 155 | assertThat(foundUser, equalTo(generatedUser)); 156 | } 157 | 158 | @Test 159 | void testGetUserByEmailFetchesRoles() { 160 | User generatedUser = dataGenerator.generateUser(); 161 | userRepository.save(generatedUser); 162 | entityManager.flush(); 163 | entityManager.detach(generatedUser); 164 | 165 | User foundUser = userService.getByEmail(generatedUser.getEmail()); 166 | 167 | assertTrue(Persistence.getPersistenceUtil().isLoaded(foundUser, "roles")); 168 | } 169 | 170 | @Test 171 | void testGetUserByEmailForUserWithoutRoles() { 172 | User generatedUserWithoutRoles = dataGenerator.generateUserWithoutRoles(); 173 | userRepository.save(generatedUserWithoutRoles); 174 | entityManager.flush(); 175 | entityManager.detach(generatedUserWithoutRoles); 176 | 177 | User foundUser = userService.getByEmail(generatedUserWithoutRoles.getEmail()); 178 | 179 | assertThat(foundUser, notNullValue()); 180 | } 181 | 182 | @Test 183 | void testGetUserByEmailFetchesAddress() { 184 | User generatedUser = dataGenerator.generateUser(); 185 | userRepository.save(generatedUser); 186 | entityManager.flush(); 187 | entityManager.detach(generatedUser); 188 | 189 | User foundUser = userService.getByEmail(generatedUser.getEmail()); 190 | 191 | assertTrue(Persistence.getPersistenceUtil().isLoaded(foundUser, "address")); 192 | } 193 | 194 | @Test 195 | void testGetUserByEmailForUserWithoutAddress() { 196 | User generatedUserWithoutAddress = dataGenerator.generateUser(); 197 | generatedUserWithoutAddress.setAddress(null); 198 | userRepository.save(generatedUserWithoutAddress); 199 | entityManager.flush(); 200 | entityManager.detach(generatedUserWithoutAddress); 201 | 202 | User foundUser = userService.getByEmail(generatedUserWithoutAddress.getEmail()); 203 | 204 | assertThat(foundUser, notNullValue()); 205 | } 206 | 207 | @Test 208 | void testGetByEmailIsReadOnly() throws NoSuchMethodException { 209 | Transactional transactional = UserService.class.getMethod("getByEmail", String.class) 210 | .getAnnotation(Transactional.class); 211 | 212 | assertThat(transactional.readOnly(), is(true)); 213 | } 214 | 215 | @Test 216 | void testGetUserByNotExistingEmail() { 217 | try { 218 | User foundUser = userService.getByEmail("xxx@gmail.com"); 219 | fail("Exception should be thrown"); 220 | } catch (Exception e) { 221 | assertTrue(e instanceof EntityNotFoundException); 222 | assertThat(e.getMessage(), equalTo(String.format("Cannot find user by email %s", "xxx@gmail.com"))); 223 | } 224 | } 225 | 226 | @Test 227 | void testAddRoleToAllUsers() { 228 | List userList = Stream.generate(dataGenerator::generateUser).limit(10).collect(toList()); 229 | userRepository.saveAll(userList); 230 | 231 | userService.addRoleToAllUsers(RoleType.OPERATOR); 232 | entityManager.flush(); 233 | userList.forEach(entityManager::detach); 234 | 235 | List users = userRepository.findAll(); 236 | 237 | assertThat(users, everyItem(hasProperty("roles", 238 | hasItem(hasProperty("roleType", is(RoleType.OPERATOR)))))); 239 | } 240 | 241 | @Test 242 | void testAddRoleToAllUsersIncludingUsersWithoutRoles() { 243 | List userList = Stream.generate(dataGenerator::generateUser).limit(10).collect(toList()); 244 | User userWithoutRoles = dataGenerator.generateUserWithoutRoles(); 245 | userList.add(userWithoutRoles); 246 | 247 | userRepository.saveAll(userList); 248 | 249 | userService.addRoleToAllUsers(RoleType.OPERATOR); 250 | entityManager.flush(); 251 | userList.forEach(entityManager::detach); 252 | 253 | List users = userRepository.findAll(); 254 | 255 | assertThat(users, everyItem(hasProperty("roles", 256 | hasItem(hasProperty("roleType", is(RoleType.OPERATOR)))))); 257 | } 258 | 259 | @Test 260 | void testAddRoleToAllUsersDoesntAddDuplicates() { 261 | User user = dataGenerator.generateUser(RoleType.USER, RoleType.OPERATOR); 262 | userRepository.save(user); 263 | 264 | userService.addRoleToAllUsers(RoleType.OPERATOR); 265 | 266 | entityManager.flush(); 267 | entityManager.detach(user); 268 | 269 | User foundUser = entityManager.find(User.class, user.getId()); 270 | 271 | assertThat(foundUser.getRoles(), hasSize(2)); 272 | } 273 | 274 | 275 | } 276 | --------------------------------------------------------------------------------