├── jpa-hibernate-tutorial-model ├── src │ └── main │ │ └── java │ │ └── com │ │ └── bobocode │ │ └── model │ │ ├── Gender.java │ │ ├── advanced │ │ ├── RoleType.java │ │ ├── Comment.java │ │ ├── Customer.java │ │ ├── RequestComment.java │ │ ├── Vehicle.java │ │ ├── Driver.java │ │ ├── Ride.java │ │ ├── ExchangeRequest.java │ │ └── ExchangePoint.java │ │ ├── basic │ │ ├── Credentials.java │ │ ├── Address.java │ │ ├── Role.java │ │ └── User.java │ │ └── Account.java └── pom.xml ├── .gitignore ├── jpa-hibernate-tutorial-util ├── src │ └── main │ │ └── java │ │ └── com │ │ └── bobocode │ │ └── util │ │ ├── exception │ │ └── JpaUtilException.java │ │ ├── JpaUtil.java │ │ └── TestDataGenerator.java └── pom.xml ├── performance-optimization ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── bobocode │ │ │ ├── dto │ │ │ └── AccountProjection.java │ │ │ ├── Pagination.java │ │ │ └── FetchingDtoProjection.java │ │ └── resources │ │ └── META-INF │ │ └── persistence.xml └── pom.xml ├── jpa-hibernate-basics ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── bobocode │ │ ├── JpaTransaction.java │ │ ├── JpaEntryPoint.java │ │ ├── JpaEntityManagerCrudOperations.java │ │ └── JpaEntityStates.java │ └── resources │ └── META-INF │ └── persistence.xml ├── persistence-context ├── pom.xml └── src │ └── main │ ├── resources │ └── META-INF │ │ └── persistence.xml │ └── java │ └── com │ └── bobocode │ └── ActionQueue.java ├── entity-relationships-management ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── bobocode │ │ ├── OneToOneRelation.java │ │ ├── OneToManyRelation.java │ │ ├── HibernateProxies.java │ │ ├── StoringNewRelationship.java │ │ └── CascadeOperations.java │ └── resources │ └── META-INF │ └── persistence.xml ├── dirty-checking-mechanism ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── bobocode │ │ │ ├── ReadOnlySessionExample.java │ │ │ ├── ReadOnlyQueryExample.java │ │ │ └── AccountUpdateExample.java │ │ └── resources │ │ └── META-INF │ │ └── persistence.xml └── README.md ├── pom.xml └── README.md /jpa-hibernate-tutorial-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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | **/*.iml 4 | .gradle/ 5 | gradle/ 6 | gradlew 7 | gradlew.bat 8 | **/build 9 | /jpa-hibernate-advanced-model -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/RoleType.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | public enum RoleType { 4 | USER, ADMIN, OPERATOR, CUSTOMER 5 | } 6 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-util/src/main/java/com/bobocode/util/exception/JpaUtilException.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.util.exception; 2 | 3 | public class JpaUtilException extends RuntimeException{ 4 | public JpaUtilException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /performance-optimization/src/main/java/com/bobocode/dto/AccountProjection.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | @AllArgsConstructor 9 | @Getter 10 | @Setter 11 | @ToString 12 | public class AccountProjection { 13 | private Long id; 14 | private String email; 15 | } 16 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/Comment.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | 4 | import com.bobocode.model.basic.User; 5 | 6 | import javax.persistence.Embeddable; 7 | import java.time.LocalDateTime; 8 | 9 | @Embeddable 10 | public class Comment { 11 | private User author; 12 | private LocalDateTime createdOn; 13 | private String message; 14 | } 15 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/README.md: -------------------------------------------------------------------------------- 1 | # JPA and Hibernate basics tutorial 2 | 3 | This is the tutorial on JPA and Hibernate configurations and basics features 4 | ### Pre-conditions :heavy_exclamation_mark: 5 | You're supposed to have basic knowledge of ORM, and be able to write Java code. 6 | ## 7 | ### Best practices 8 | * avoid auto-commit mode 9 | * prefer *session-per-request* (*entity-manager-per-request*) transaction pattern 10 | 11 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/Customer.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | import com.bobocode.model.basic.User; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.persistence.Entity; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @NoArgsConstructor 14 | @Getter 15 | @Setter 16 | @ToString 17 | @Entity 18 | public class Customer extends User { 19 | private List exchangePoints = new ArrayList<>(); 20 | } 21 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/RequestComment.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | import javax.persistence.*; 9 | 10 | 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | @ToString 15 | @Entity 16 | public class RequestComment { 17 | @Id 18 | @GeneratedValue 19 | private Long id; 20 | @Embedded 21 | private Comment comment; 22 | @ManyToOne 23 | private ExchangeRequest exchangeRequest; 24 | } 25 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/Vehicle.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | import javax.persistence.*; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @NoArgsConstructor 12 | @Setter @Getter 13 | @Entity @Table(name = "vehicles") 14 | public class Vehicle { 15 | @Id 16 | @GeneratedValue 17 | private Long id; 18 | 19 | @OneToMany(mappedBy = "vehicle") 20 | List rides = new ArrayList<>(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.bobocode 9 | jpa-hibernate-tutorial 10 | 1.0-SNAPSHOT 11 | 12 | jpa-hibernate-tutorial-model 13 | 14 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/Driver.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | 4 | import com.bobocode.model.basic.User; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | 10 | import javax.persistence.Entity; 11 | import javax.persistence.OneToMany; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @NoArgsConstructor 16 | @Getter 17 | @Setter 18 | @ToString 19 | @Entity 20 | public class Driver extends User { 21 | private String driverLicenceNumber; 22 | @OneToMany(mappedBy = "driver") 23 | private List rides = new ArrayList<>(); 24 | } 25 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/basic/Credentials.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.basic; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.Column; 9 | import javax.persistence.Embeddable; 10 | import java.time.LocalDateTime; 11 | 12 | @NoArgsConstructor 13 | @Getter @Setter 14 | @EqualsAndHashCode(of = "email") 15 | @Embeddable 16 | public class Credentials { 17 | @Column(name = "email") 18 | private String email; 19 | 20 | @Column(name = "password") 21 | private String password; 22 | 23 | @Column(name = "last_modified_password") 24 | private LocalDateTime lastModifiedPassword; 25 | } 26 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/Ride.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.persistence.*; 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | 13 | @NoArgsConstructor 14 | @Getter 15 | @Setter 16 | @ToString 17 | @Entity 18 | public class Ride { 19 | @Id 20 | @GeneratedValue 21 | private Long id; 22 | @ManyToOne 23 | private Driver driver; 24 | @ManyToOne 25 | private Vehicle vehicle; 26 | 27 | @OneToMany(mappedBy = "ride") 28 | private List exchangeRequests; 29 | private LocalDateTime createdOn; 30 | private LocalDateTime startTime; 31 | private LocalDateTime finishTime; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.bobocode 9 | jpa-hibernate-tutorial 10 | 1.0-SNAPSHOT 11 | 12 | jpa-hibernate-basics 13 | 14 | 15 | 16 | com.bobocode 17 | jpa-hibernate-tutorial-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | -------------------------------------------------------------------------------- /persistence-context/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jpa-hibernate-tutorial 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | persistence-context 13 | 14 | 15 | 16 | com.bobocode 17 | jpa-hibernate-tutorial-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /performance-optimization/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jpa-hibernate-tutorial 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | performance-optimization 13 | 14 | 15 | 16 | com.bobocode 17 | jpa-hibernate-tutorial-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /entity-relationships-management/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jpa-hibernate-tutorial 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | entity-relationships-management 13 | 14 | 15 | 16 | 17 | com.bobocode 18 | jpa-hibernate-tutorial-util 19 | 1.0-SNAPSHOT 20 | 21 | 22 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/ExchangeRequest.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 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.ArrayList; 11 | import java.util.List; 12 | 13 | @NoArgsConstructor 14 | @Getter 15 | @Setter 16 | @ToString 17 | @Entity 18 | public class ExchangeRequest { 19 | @Id @GeneratedValue 20 | private Long id; 21 | @ManyToOne 22 | private ExchangePoint exchangePoint; 23 | private LocalDateTime createdOn; 24 | private LocalDateTime processedOn; 25 | private LocalDateTime finishedOn; 26 | @OneToMany(mappedBy = "exchangeRequest") 27 | private List comments = new ArrayList<>(); 28 | @ManyToOne 29 | private Ride ride; 30 | } 31 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/advanced/ExchangePoint.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.advanced; 2 | 3 | import com.bobocode.model.basic.Address; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | 9 | import javax.persistence.Entity; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.Id; 12 | import javax.persistence.OneToMany; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | @NoArgsConstructor 17 | @Getter 18 | @Setter 19 | @ToString 20 | @Entity 21 | public class ExchangePoint { 22 | @Id 23 | @GeneratedValue 24 | private Long id; 25 | private String description; 26 | private Customer customer; 27 | private Address address; 28 | @OneToMany(mappedBy = "exchangePoint") 29 | private List exchangeRequests = new ArrayList<>(); 30 | } 31 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/basic/Address.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.basic; 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 | -------------------------------------------------------------------------------- /persistence-context/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.bobocode.model.Account 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /performance-optimization/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.bobocode.model.Account 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-util/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.bobocode 9 | jpa-hibernate-tutorial 10 | 1.0-SNAPSHOT 11 | 12 | jpa-hibernate-tutorial-util 13 | 14 | 15 | 16 | com.bobocode 17 | jpa-hibernate-tutorial-model 18 | 1.0-SNAPSHOT 19 | 20 | 21 | io.codearte.jfairy 22 | jfairy 23 | 0.5.7 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | jpa-hibernate-tutorial 7 | com.bobocode 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | dirty-checking-mechanism 13 | 14 | 15 | 16 | com.bobocode 17 | jpa-hibernate-tutorial-util 18 | 1.0-SNAPSHOT 19 | 20 | 21 | com.bobocode 22 | jpa-hibernate-tutorial-model 23 | 1.0-SNAPSHOT 24 | 25 | 26 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/Account.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.math.BigDecimal; 7 | import java.time.LocalDate; 8 | import java.time.LocalDateTime; 9 | 10 | @NoArgsConstructor 11 | @Getter @Setter 12 | @ToString 13 | @EqualsAndHashCode(of = "id") 14 | @Entity 15 | @Table(name = "accounts") 16 | public class Account { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | 21 | @Column(name = "first_name") 22 | private String firstName; 23 | 24 | @Column(name = "last_name") 25 | private String lastName; 26 | 27 | @Column(name = "email", unique = true) 28 | private String email; 29 | 30 | @Column(name = "birthday") 31 | private LocalDate birthday; 32 | 33 | @Column(name = "gender") 34 | @Enumerated(EnumType.STRING) 35 | private Gender gender; 36 | 37 | @Column(name = "creation_time") 38 | private LocalDateTime creationTime; 39 | 40 | @Column(name = "balance") 41 | private BigDecimal balance = BigDecimal.ZERO; 42 | } 43 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/basic/Role.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.basic; 2 | 3 | import com.bobocode.model.advanced.RoleType; 4 | import lombok.*; 5 | 6 | import javax.persistence.*; 7 | import java.time.LocalDateTime; 8 | import java.util.Objects; 9 | 10 | @NoArgsConstructor 11 | @Getter 12 | @Setter 13 | @ToString(exclude = "user") 14 | @Entity 15 | @Table(name = "role") 16 | public class Role { 17 | @Id 18 | @GeneratedValue 19 | private Long id; 20 | 21 | @Enumerated(EnumType.STRING) 22 | @Column(name = "role_type") 23 | private RoleType roleType; 24 | 25 | @Column(name = "creation_date") 26 | private LocalDateTime creationDate = LocalDateTime.now(); 27 | 28 | @ManyToOne 29 | @JoinColumn(name = "user_id") 30 | private User user; 31 | 32 | public static Role valueOf(RoleType roleType) { 33 | return new Role(roleType); 34 | } 35 | 36 | private Role(RoleType roleType) { 37 | this.roleType = roleType; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (!(o instanceof Role)) return false; 44 | 45 | Role role = (Role) o; 46 | 47 | return Objects.equals(id, role.id); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return 31; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/java/com/bobocode/OneToOneRelation.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.basic.Address; 4 | import com.bobocode.model.basic.User; 5 | import com.bobocode.util.JpaUtil; 6 | import com.bobocode.util.TestDataGenerator; 7 | 8 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 9 | 10 | public class OneToOneRelation { 11 | 12 | public static void main(String[] args) { 13 | JpaUtil.init("BasicEntitiesH2"); 14 | 15 | User user = TestDataGenerator.generateUser(); 16 | System.out.println("Generated user: " + user); 17 | 18 | Address address = TestDataGenerator.generateAddress(); 19 | System.out.println("Generated address: " + address); 20 | 21 | saveUserWithAddress(user, address); 22 | 23 | printUserById(user.getId()); 24 | 25 | JpaUtil.close(); 26 | } 27 | 28 | private static void saveUserWithAddress(User user, Address address) { 29 | performWithinPersistenceContext(em -> { 30 | em.persist(user); 31 | address.setUser(user); 32 | em.persist(address); 33 | // the relation between User and Address is managed on the child side 34 | // so I can use address.setUser(user); to link user and it's address 35 | }); 36 | } 37 | 38 | private static void printUserById(Long userId) { 39 | performWithinPersistenceContext(em -> { 40 | User persistedUser = em.find(User.class, userId); 41 | System.out.println("Persisted user: " + persistedUser); 42 | }); 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/java/com/bobocode/OneToManyRelation.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.basic.Role; 4 | import com.bobocode.model.basic.User; 5 | import com.bobocode.util.JpaUtil; 6 | import com.bobocode.util.TestDataGenerator; 7 | 8 | import java.util.List; 9 | 10 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 11 | 12 | public class OneToManyRelation { 13 | 14 | public static void main(String[] args) { 15 | JpaUtil.init("BasicEntitiesH2"); 16 | 17 | User user = TestDataGenerator.generateUser(); 18 | List roles = TestDataGenerator.generateRoleList(); 19 | System.out.println(roles); 20 | 21 | System.out.println("Generated user: " + user); 22 | saveUserWithRoles(user, roles); 23 | 24 | printUserById(user.getId()); 25 | 26 | JpaUtil.close(); 27 | } 28 | 29 | private static void saveUserWithRoles(User user, List roleList) { 30 | performWithinPersistenceContext(em -> { 31 | em.persist(user); 32 | user.addRoles(roleList); 33 | roleList.forEach(em::persist); 34 | // the relation between User and Role is managed on the child side (Role side) 35 | // we use method addRoles() that setup relation on the child side (it sets user for each role) 36 | }); 37 | } 38 | 39 | private static void printUserById(Long userId) { 40 | performWithinPersistenceContext(em -> { 41 | User persistedUser = em.find(User.class, userId); 42 | System.out.println("Persisted user: " + persistedUser); 43 | }); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-model/src/main/java/com/bobocode/model/basic/User.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.model.basic; 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 = "users") 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 = "birthday") 31 | private LocalDate birthday; 32 | 33 | @Column(name = "creation_date") 34 | private LocalDate creationDate; 35 | 36 | @Embedded 37 | private Credentials credentials; 38 | 39 | @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) 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 | address.setUser(this); 48 | this.address = address; 49 | } 50 | 51 | public void addRole(Role role) { 52 | roles.add(role); 53 | role.setUser(this); 54 | } 55 | 56 | public void addRoles(List roles) { 57 | this.roles.addAll(roles); 58 | roles.forEach(role -> role.setUser(this)); 59 | } 60 | 61 | public void removeRole(Role role) { 62 | this.roles.remove(role); 63 | role.setUser(null); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/src/main/java/com/bobocode/ReadOnlySessionExample.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.TestDataGenerator; 5 | import org.hibernate.Session; 6 | 7 | import static com.bobocode.util.JpaUtil.*; 8 | 9 | /** 10 | * This code example shows how to turn of dirty checking mechanism for a particular entity 11 | */ 12 | public class ReadOnlySessionExample { 13 | public static void main(String[] args) { 14 | init("BasicEntitiesH2"); 15 | 16 | long accountId = saveRandomAccount(); 17 | 18 | performWithinPersistenceContext(entityManager -> { 19 | Session session = entityManager.unwrap(Session.class); 20 | session.setDefaultReadOnly(true); // turns off dirty checking for this session (for this entityManager) 21 | //todo: try to comment the previous line and run it again 22 | 23 | Account managedAccount = entityManager.find(Account.class, accountId); 24 | managedAccount.setFirstName("XXX"); // won't cause SQL UPDATE statement since dirty checking is disabled 25 | }); 26 | 27 | printAccountById(accountId); 28 | 29 | close(); 30 | } 31 | 32 | private static long saveRandomAccount() { 33 | Account account = TestDataGenerator.generateAccount(); 34 | performWithinPersistenceContext(entityManager -> entityManager.persist(account)); 35 | return account.getId(); 36 | } 37 | 38 | private static void printAccountById(long accountId) { 39 | performWithinPersistenceContext(entityManager -> { 40 | Account storedAccount = entityManager.find(Account.class, accountId); 41 | System.out.println(storedAccount); 42 | }); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/src/main/java/com/bobocode/ReadOnlyQueryExample.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.TestDataGenerator; 5 | import org.hibernate.jpa.QueryHints; 6 | 7 | import static com.bobocode.util.JpaUtil.*; 8 | 9 | /** 10 | * This code example shows how to turn of dirty checking mechanism using read-only Session 11 | */ 12 | public class ReadOnlyQueryExample { 13 | public static void main(String[] args) { 14 | init("BasicEntitiesH2"); 15 | 16 | long accountId = saveRandomAccount(); 17 | 18 | performWithinPersistenceContext(entityManager -> { 19 | Account managedAccount = entityManager.createQuery("select a from Account a where a.id = :id", Account.class) 20 | .setParameter("id", accountId) 21 | .setHint(QueryHints.HINT_READONLY, true)// turns off dirty checking for this particular entity 22 | .getSingleResult(); 23 | managedAccount.setFirstName("XXX"); // won't cause SQL UPDATE statement since dirty checking is disabled for this entity 24 | }); 25 | 26 | printAccountById(accountId); 27 | 28 | close(); 29 | } 30 | 31 | private static long saveRandomAccount() { 32 | Account account = TestDataGenerator.generateAccount(); 33 | performWithinPersistenceContext(entityManager -> entityManager.persist(account)); 34 | return account.getId(); 35 | } 36 | 37 | private static void printAccountById(long accountId) { 38 | performWithinPersistenceContext(entityManager -> { 39 | Account storedAccount = entityManager.find(Account.class, accountId); 40 | System.out.println(storedAccount); 41 | }); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /performance-optimization/src/main/java/com/bobocode/Pagination.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.JpaUtil; 5 | import com.bobocode.util.TestDataGenerator; 6 | 7 | import java.util.List; 8 | import java.util.stream.Stream; 9 | 10 | import static com.bobocode.util.JpaUtil.performReturningWithinPersistenceContext; 11 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 12 | import static java.util.stream.Collectors.toList; 13 | 14 | /** 15 | * This code examples show how to use pagination in JPA queries. 16 | */ 17 | public class Pagination { 18 | public static void main(String[] args) { 19 | JpaUtil.init("BasicEntitiesH2"); 20 | List accounts = Stream.generate(TestDataGenerator::generateAccount).limit(100).collect(toList()); 21 | performWithinPersistenceContext(entityManager -> accounts.forEach(entityManager::persist)); 22 | 23 | List pageAccounts = loadAccounts(20, 10); 24 | pageAccounts.forEach(System.out::println); 25 | JpaUtil.close(); 26 | } 27 | 28 | /** 29 | * Loads all account starting from {@code offset} position. The number of loaded accounts is limited by 30 | * {@code limit} number 31 | * 32 | * @param offset starting position 33 | * @param limit number of account to load 34 | * @return list of accounts 35 | */ 36 | private static List loadAccounts(int offset, int limit) { 37 | return performReturningWithinPersistenceContext(entityManager -> entityManager 38 | .createQuery("select a from Account a", Account.class) 39 | .setFirstResult(offset) 40 | .setMaxResults(limit) 41 | .getResultList()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /performance-optimization/src/main/java/com/bobocode/FetchingDtoProjection.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.dto.AccountProjection; 4 | import com.bobocode.model.Account; 5 | import com.bobocode.util.JpaUtil; 6 | import com.bobocode.util.TestDataGenerator; 7 | 8 | import static com.bobocode.util.JpaUtil.performReturningWithinPersistenceContext; 9 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 10 | 11 | /** 12 | * This code example shows how to fetch an entity projection instead of full entity. 13 | */ 14 | public class FetchingDtoProjection { 15 | public static void main(String[] args) { 16 | JpaUtil.init("BasicEntitiesH2"); 17 | Account account = TestDataGenerator.generateAccount(); 18 | performWithinPersistenceContext(entityManager -> entityManager.persist(account)); 19 | 20 | AccountProjection accountProjection = fetchAccountProjectionById(account.getId()); 21 | System.out.println(accountProjection); 22 | JpaUtil.close(); 23 | } 24 | 25 | /** 26 | * Fetches {@link AccountProjection} by account id. An {@link AccountProjection} is a data transfer object (DTO) for 27 | * {@link Account}. (It's a short version that stores some account data, but is not manged by Hibernate) 28 | * 29 | * @param id account id 30 | * @return account projection 31 | */ 32 | private static AccountProjection fetchAccountProjectionById(Long id) { 33 | return performReturningWithinPersistenceContext(entityManager -> 34 | entityManager.createQuery("select new com.bobocode.dto.AccountProjection(a.id, a.email) " + 35 | "from Account a where a.id = :id", AccountProjection.class) 36 | .setParameter("id", id) 37 | .getSingleResult()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.bobocode.model.Account 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | com.bobocode.model.Account 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/src/main/java/com/bobocode/JpaTransaction.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.TestDataGenerator; 5 | 6 | import javax.persistence.EntityManager; 7 | import javax.persistence.EntityManagerFactory; 8 | import javax.persistence.Persistence; 9 | 10 | /** 11 | * This examples shows how to handle database transactions using JPA {@link EntityManager} 12 | */ 13 | public class JpaTransaction { 14 | public static void main(String[] args) { 15 | EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("BasicEntitiesH2"); 16 | EntityManager entityManager = entityManagerFactory.createEntityManager(); 17 | Account account = TestDataGenerator.generateAccount(); 18 | 19 | saveAccountWithinTx(account, entityManager); 20 | 21 | entityManagerFactory.close(); 22 | } 23 | 24 | /** 25 | * In order to handle database transactions using JPA you need to get {@link javax.persistence.EntityTransaction} 26 | * instance that allow you to start, commit and rollback the transaction 27 | * 28 | * @param account 29 | * @param entityManager 30 | */ 31 | private static void saveAccountWithinTx(Account account, EntityManager entityManager) { 32 | entityManager.getTransaction().begin(); // calls Connection#setAutoCommit(false) 33 | try { 34 | entityManager.persist(account); 35 | entityManager.getTransaction().commit();// calls Connection#commit() 36 | System.out.printf("Account %s has been saved.%n", account); 37 | } catch (Exception e) { 38 | entityManager.getTransaction().rollback(); // calls Connection#rollback() 39 | System.err.printf("Error saving account %s. Transaction has been rolled back.%n", account); 40 | } finally { 41 | entityManager.close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/src/main/java/com/bobocode/JpaEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.JpaUtil; 5 | import com.bobocode.util.TestDataGenerator; 6 | 7 | import javax.persistence.EntityManager; 8 | import javax.persistence.EntityManagerFactory; 9 | import javax.persistence.Persistence; 10 | 11 | /** 12 | * {@link JpaEntryPoint} shows an example of using basic JPA classes {@link EntityManagerFactory} and {@link EntityManager}. 13 | *

14 | * {@link EntityManager} allows to perform database operations with JPA entities. It represents a db session. E.g. each 15 | * user should get a new instance of {@link EntityManager} each time to perform db operations on JPA entity. 16 | *

17 | * {@link EntityManagerFactory} is a thread-safe factory that allow to create {@link EntityManager} instances. 18 | */ 19 | public class JpaEntryPoint { 20 | public static void main(String[] args) { 21 | EntityManagerFactory entityManagerFactory = createEntityManagerFactory(); 22 | EntityManager entityManager = entityManagerFactory.createEntityManager(); 23 | 24 | Account account = TestDataGenerator.generateAccount(); 25 | 26 | entityManager.getTransaction().begin(); 27 | entityManager.persist(account); 28 | entityManager.getTransaction().commit(); 29 | 30 | System.out.println(account); 31 | 32 | entityManager.close(); 33 | entityManagerFactory.close(); 34 | } 35 | 36 | /** 37 | * Creates an instance of {@link EntityManagerFactory}. It uses JPA util class {@link Persistence} that allows 38 | * to create an instance by its name. All JPA configuration required to create {@link EntityManagerFactory} are 39 | * located in resources /META-INF/persistence.xml which is a default location for JPA configs. 40 | * 41 | * @return instance of entity manager factory 42 | */ 43 | private static EntityManagerFactory createEntityManagerFactory() { 44 | return Persistence.createEntityManagerFactory("BasicEntitiesH2"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-util/src/main/java/com/bobocode/util/JpaUtil.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.util; 2 | 3 | import com.bobocode.util.exception.JpaUtilException; 4 | 5 | import javax.persistence.EntityManager; 6 | import javax.persistence.EntityManagerFactory; 7 | import javax.persistence.Persistence; 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | 11 | public class JpaUtil { 12 | 13 | private static EntityManagerFactory emf; 14 | 15 | public static void init(String persistenceUnitName) { 16 | emf = Persistence.createEntityManagerFactory(persistenceUnitName); 17 | } 18 | 19 | public static EntityManagerFactory getEntityManagerFactory() { 20 | return emf; 21 | } 22 | 23 | public static void close() { 24 | emf.close(); 25 | } 26 | 27 | public static void performWithinPersistenceContext(Consumer operation) { 28 | EntityManager entityManager = emf.createEntityManager(); 29 | entityManager.getTransaction().begin(); 30 | try { 31 | operation.accept(entityManager); 32 | entityManager.getTransaction().commit(); 33 | } catch (Exception e) { 34 | entityManager.getTransaction().rollback(); 35 | throw new JpaUtilException("Error performing JPA operation. Transaction is rolled back", e); 36 | } finally { 37 | entityManager.close(); 38 | } 39 | } 40 | 41 | public static T performReturningWithinPersistenceContext(Function entityManagerFunction) { 42 | EntityManager entityManager = emf.createEntityManager(); 43 | entityManager.getTransaction().begin(); 44 | try { 45 | T result = entityManagerFunction.apply(entityManager); 46 | entityManager.getTransaction().commit(); 47 | return result; 48 | } catch (Exception e) { 49 | entityManager.getTransaction().rollback(); 50 | throw new JpaUtilException("Error performing JPA operation. Transaction is rolled back", e); 51 | } finally { 52 | entityManager.close(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/java/com/bobocode/HibernateProxies.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | 4 | import com.bobocode.model.basic.Address; 5 | import com.bobocode.model.basic.Role; 6 | import com.bobocode.model.basic.User; 7 | import com.bobocode.util.JpaUtil; 8 | import com.bobocode.util.TestDataGenerator; 9 | 10 | import java.util.List; 11 | 12 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 13 | 14 | /** 15 | * This example demonstrates how Hibernate creates proxies around entities. 16 | */ 17 | public class HibernateProxies { 18 | 19 | public static void main(String[] args) { 20 | JpaUtil.init("BasicEntitiesH2"); 21 | 22 | 23 | User user = TestDataGenerator.generateUser(); 24 | List roles = TestDataGenerator.generateRoleList(); 25 | Address address = TestDataGenerator.generateAddress(); 26 | 27 | saveUser(user, address, roles); 28 | printUserProxyClassByUserId(user.getId()); 29 | printRoleSetProxyClassByUserId(user.getId()); 30 | 31 | JpaUtil.close(); 32 | } 33 | 34 | private static void saveUser(User user, Address address, List roles) { 35 | performWithinPersistenceContext(em -> { 36 | user.setAddress(address); 37 | user.addRoles(roles); 38 | em.persist(user); 39 | }); 40 | } 41 | 42 | /** 43 | * This examples uses {@link javax.persistence.EntityManager#getReference(Class, Object)} to create a user proxy 44 | * 45 | * @param userId 46 | */ 47 | private static void printUserProxyClassByUserId(Long userId) { 48 | performWithinPersistenceContext(em -> { 49 | User userProxy = em.getReference(User.class, userId); 50 | System.out.println("\nUser proxy class is " + userProxy.getClass()); 51 | }); 52 | } 53 | 54 | 55 | private static void printRoleSetProxyClassByUserId(Long userId) { 56 | performWithinPersistenceContext(em -> { 57 | User user = em.find(User.class, userId); 58 | System.out.println("\nUser class is " + user.getClass()); 59 | System.out.println("\nUser roles proxy class is " + user.getRoles().getClass()); 60 | }); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.bobocode.model.basic.Address 6 | com.bobocode.model.basic.Credentials 7 | com.bobocode.model.basic.Role 8 | com.bobocode.model.basic.User 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | com.bobocode.model.basic.Address 25 | com.bobocode.model.basic.Credentials 26 | com.bobocode.model.basic.Role 27 | com.bobocode.model.basic.User 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.bobocode.model.Account 6 | com.bobocode.model.basic.Address 7 | com.bobocode.model.basic.Credentials 8 | com.bobocode.model.basic.Role 9 | com.bobocode.model.basic.User 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | com.bobocode.model.Account 26 | com.bobocode.model.basic.Address 27 | com.bobocode.model.basic.Credentials 28 | com.bobocode.model.basic.Role 29 | com.bobocode.model.basic.User 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/src/main/java/com/bobocode/AccountUpdateExample.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.TestDataGenerator; 5 | 6 | import static com.bobocode.util.JpaUtil.*; 7 | 8 | /** 9 | * This code examples shows the usage of Hibernate mechanism called Dirty Checking. 10 | */ 11 | public class AccountUpdateExample { 12 | public static void main(String[] args) { 13 | init("BasicEntitiesH2"); 14 | 15 | long accountId = saveRandomAccount(); 16 | printAccountById(accountId); 17 | updateAccountLastNameById(accountId, "NEW LAST NAME"); 18 | printAccountById(accountId); 19 | 20 | close(); 21 | } 22 | 23 | /** 24 | * Saves a random account into the database and returns it generated id. 25 | * 26 | * @return generated id 27 | */ 28 | private static long saveRandomAccount() { 29 | Account account = TestDataGenerator.generateAccount(); 30 | performWithinPersistenceContext(entityManager -> entityManager.persist(account)); 31 | return account.getId(); 32 | } 33 | 34 | /** 35 | * Loads an {@link Account} instance form database and prints it 36 | * 37 | * @param accountId stored account id 38 | */ 39 | private static void printAccountById(long accountId) { 40 | performWithinPersistenceContext(entityManager -> { 41 | Account account = entityManager.find(Account.class, accountId); 42 | System.out.println(account); 43 | }); 44 | } 45 | 46 | /** 47 | * Loads {@link Account} from the db by its id and updates it last name. Since Hibernate mechanism which is called 48 | * Dirty checking is enabled by default, it check all managed entities state changes and generates appropriate 49 | * UPDATE statements for the database. 50 | * 51 | * @param accountId stored account id 52 | * @param newLastName new account last name 53 | */ 54 | private static void updateAccountLastNameById(long accountId, String newLastName) { 55 | performWithinPersistenceContext(entityManager -> { // Persistence Context begins 56 | Account account = entityManager.find(Account.class, accountId); // account is managed by Hibernate 57 | account.setLastName(newLastName); // this changes will be detected by Dirty Checking mechanism 58 | // on flush dirty checking will generate UPDATE statement and will send it to the database 59 | });// Persistence Context ends 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.bobocode 8 | jpa-hibernate-tutorial 9 | 1.0-SNAPSHOT 10 | pom 11 | 12 | 13 | 1.10 14 | 1.10 15 | 16 | 17 | 18 | jpa-hibernate-basics 19 | jpa-hibernate-tutorial-model 20 | jpa-hibernate-tutorial-util 21 | dirty-checking-mechanism 22 | entity-relationships-management 23 | persistence-context 24 | performance-optimization 25 | 26 | 27 | 28 | 29 | org.projectlombok 30 | lombok 31 | 1.18.0 32 | 33 | 34 | org.postgresql 35 | postgresql 36 | 42.2.3 37 | 38 | 39 | com.h2database 40 | h2 41 | 1.4.197 42 | 43 | 44 | org.slf4j 45 | slf4j-simple 46 | 1.7.24 47 | 48 | 49 | org.hibernate.javax.persistence 50 | hibernate-jpa-2.1-api 51 | 1.0.2.Final 52 | 53 | 54 | org.hibernate 55 | hibernate-core 56 | 5.3.2.Final 57 | 58 | 59 | 60 | 61 | javax.xml.bind 62 | jaxb-api 63 | 2.2.11 64 | 65 | 66 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/src/main/java/com/bobocode/JpaEntityManagerCrudOperations.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | 4 | import com.bobocode.model.Account; 5 | import com.bobocode.util.TestDataGenerator; 6 | 7 | import java.util.List; 8 | 9 | import static com.bobocode.util.JpaUtil.*; 10 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 11 | 12 | /** 13 | * This code example shows a typical usage of {@link javax.persistence.EntityManager} and its Create, Read, Update, 14 | * Delete (CRUD) operations 15 | */ 16 | public class JpaEntityManagerCrudOperations { 17 | 18 | public static void main(String[] args) { 19 | init("BasicEntitiesH2"); 20 | 21 | Account account = TestDataGenerator.generateAccount(); 22 | System.out.printf("Account before save: %s%n", account); 23 | 24 | saveAccount(account); 25 | System.out.printf("Stored account: %s%n", account); 26 | 27 | printAllAccounts(); 28 | 29 | Account foundAccount = findAccountByEmail(account.getEmail()); 30 | System.out.printf("Account found by email (%s): %s%n", account.getEmail(), foundAccount); 31 | 32 | account.setFirstName("UPDATED"); 33 | account = updateAccount(account); 34 | System.out.printf("Updated account: %s%n", account); 35 | 36 | printAllAccounts(); 37 | 38 | removeAccount(account); 39 | printAllAccounts(); 40 | 41 | close(); 42 | } 43 | 44 | private static void saveAccount(Account account) { 45 | performWithinPersistenceContext(em -> em.persist(account)); 46 | } 47 | 48 | private static void printAllAccounts() { 49 | System.out.printf("All accounts: %s%n", findAllAccounts()); 50 | } 51 | 52 | private static List findAllAccounts() { 53 | return performReturningWithinPersistenceContext(em -> 54 | em.createQuery("select a from Account a", Account.class).getResultList() 55 | ); 56 | 57 | } 58 | 59 | private static Account findAccountByEmail(String email) { 60 | return performReturningWithinPersistenceContext(em -> 61 | em.createQuery("select a from Account a where a.email = :email", Account.class) 62 | .setParameter("email", email) 63 | .getSingleResult() 64 | ); 65 | } 66 | 67 | private static Account updateAccount(Account account) { 68 | return performReturningWithinPersistenceContext(entityManager -> entityManager.merge(account)); 69 | } 70 | 71 | private static void removeAccount(Account account) { 72 | performWithinPersistenceContext(em -> { 73 | Account managedAccount = em.merge(account); // only managed entities can be removed 74 | em.remove(managedAccount); 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /dirty-checking-mechanism/README.md: -------------------------------------------------------------------------------- 1 | # Hibernate "Dirty checking" tutorial 2 | This is the tutorial well-known Hibernate feature called "Dirty checking" 3 | 4 | ### Pre-conditions :heavy_exclamation_mark: 5 | You're supposed to have basic knowledge of ORM, JPA and Hibernate. 6 | ## 7 | **Dirty checking mechanism** is a *Hibernate* feature that **transforms entity field changes into SQL `UPDATE`** statements in the 8 | scope of *Persistence Context (PC)*. 9 | 10 | **By default, dirty checking is enabled.** For each managed(persistent) entity it Hibernate creates a snapshot of persisted 11 | data (entity copy). That data is stored in the scope of *PC* as an array of `Object` elements. That array basically holds 12 | the same data as corresponding database row. At flush time, every entity is compared with its snapshot copy, if some changes detected, Hibernate generates appropriate 13 | `UPDATE` statement. 14 | 15 | ```java 16 | performWithinPersistenceContext(entityManager -> { // Persistence Context begins 17 | Account account = entityManager.find(Account.class, accountId); // account is managed by Hibernate 18 | account.setLastName(newLastName); // this changes will be detected by Dirty Checking mechanism 19 | // on flush dirty checking will generate UPDATE statement and will send it to the database 20 | });// Persistence Context ends 21 | ``` 22 | 23 | Please note, that this mechanism has **huge performance impact**, because it **doubles the size of the *PC***, requires 24 | **additional CPU resources** to check each attribute of each manges entity, and causes **additional Garbage Collection.** 25 | In order to **turn of dirty checking**, you should set default read-only for the `Session`, or specify a hint when creating 26 | new select query. 27 | 28 | ```java 29 | performWithinPersistenceContext(entityManager -> { 30 | Session session = entityManager.unwrap(Session.class); 31 | session.setDefaultReadOnly(true); // turns off dirty checking for this session (for this entityManager) 32 | Account managedAccount = entityManager.find(Account.class, accountId); 33 | managedAccount.setFirstName("XXX"); // won't cause SQL UPDATE statement since dirty checking is disabled 34 | }); 35 | ``` 36 | 37 | In order to disable dirty checking for a particular entity on select use query hints: 38 | 39 | ```java 40 | performWithinPersistenceContext(entityManager -> { 41 | Account managedAccount = entityManager.createQuery("select a from Account a where a.email = :email", Account.class) 42 | .setParameter("email", email) 43 | .setHint(QueryHints.HINT_READONLY, true)// turns off dirty checking for this particular entity 44 | .getSingleResult(); 45 | managedAccount.setFirstName("XXX"); // won't cause SQL UPDATE statement since dirty checking is disabled for this entity 46 | }); 47 | ``` 48 | 49 | ### Best practices 50 | * always use **read-only** queries for all `SELECT` statements 51 | * keep *Persistence Context* size as small as possible 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JPA and Hibernate tutorial 2 | The list of tutorials on ORM, JPA and Hibernate features and best practices 3 | 4 | ### Pre-conditions :heavy_exclamation_mark: 5 | You're supposed to be familiar with OOP, have basic knowledge of JDK, and be able to write Java code. 6 | ### Related information :information_source: 7 | #### Overview 8 | * [Java Persistence with Hibernate](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1617290459/ref=sr_1_1?s=books&ie=UTF8&qid=1538386848&sr=1-1&keywords=java+persistence+with+hibernate%2C+second+edition) :green_book: 9 | * **1.1.3** Using SQL in Java. *(JDBC API disadvantages)* 10 | * **1.2** The paradigm mismatch. *(Object-oriented vs Relational model)* 11 | * **1.3** ORM and JPA. *(What are ORM advantages?)* 12 | * **3.1.1** A layered architecture. *(What is a persistence (DAO) layer?)* 13 | * **3.1.2** Analyzing the business domain. *(What is a domain model?)* 14 | ### JPA and Hibernate basics 15 | * [JPA and Hibernate ORM basics tutorial](https://github.com/bobocode-projects/jpa-hibernate-tutorial/tree/master/jpa-hibernate-basics) 16 | * [A beginner’s guide to entity state transitions with JPA and Hibernate](https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/) 17 | * [Java Persistence with Hibernate](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1617290459/ref=sr_1_1?s=books&ie=UTF8&qid=1538386848&sr=1-1&keywords=java+persistence+with+hibernate%2C+second+edition) :green_book: 18 | * **10.1.1** Entity instance states 19 | * **10.1.2** The persistence context 20 | * **10.2.1 - 10.2.6** The EntityManager interface. *(Entity Create Read Update Remove (CRUD) operations)* 21 | * **10.3.4** Merging entity instances. *(What's happening on merge?)* 22 | ### Dirty checking 23 | * ["Dirty checking" tutorial](https://github.com/bobocode-projects/jpa-hibernate-tutorial/tree/master/dirty-checking-mechanism) 24 | * [The anatomy of Hibernate dirty checking mechanism](https://vladmihalcea.com/the-anatomy-of-hibernate-dirty-checking/) 25 | * [Java Persistence with Hibernate](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1617290459/ref=sr_1_1?s=books&ie=UTF8&qid=1538386848&sr=1-1&keywords=java+persistence+with+hibernate%2C+second+edition) :green_book: 26 | * **10.2.3** Retrieving and modifying persistent data. *(Entity update)* 27 | ### Lazy loading 28 | * [Java Persistence with Hibernate](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/1617290459/ref=sr_1_1?s=books&ie=UTF8&qid=1538386848&sr=1-1&keywords=java+persistence+with+hibernate%2C+second+edition) :green_book: 29 | * **12.1.1** Understanding entity proxies. *(One entity lazy loading)* 30 | * **12.1.2** Lazy persistent collections. *(Collection lazy loading)* 31 | * **12.2.1** The n+1 selects problem 32 | 33 | -------------------------------------------------------------------------------- /jpa-hibernate-basics/src/main/java/com/bobocode/JpaEntityStates.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | 4 | import com.bobocode.model.Account; 5 | import com.bobocode.util.TestDataGenerator; 6 | 7 | import static com.bobocode.util.JpaUtil.*; 8 | 9 | /** 10 | * This tutorial provides examples of different JPA entity states. {@link Account} instance, which is a JPA entity 11 | * is moved from state TRANSIENT to state PERSISTENT, then it becomes DETACHED. After it again becomes PERSISTENT 12 | * and soon REMOVED. 13 | */ 14 | public class JpaEntityStates { 15 | public static void main(String[] args) { 16 | init("BasicEntitiesH2"); 17 | 18 | Account account = TestDataGenerator.generateAccount(); 19 | 20 | printAccountInTransientState(account); 21 | printAccountInPersistentState(account); 22 | printAccountInDetachedState(account); 23 | printAccountInRemovedState(account); 24 | 25 | close(); 26 | } 27 | 28 | /** 29 | * Receives a new account instance. It doesn't have associated id. It is not stored in the database. 30 | * 31 | * @param account account in transient state 32 | */ 33 | private static void printAccountInTransientState(Account account) { 34 | System.out.printf("Account in TRANSIENT state: %s%n", account); 35 | } 36 | 37 | /** 38 | * Receives an account in transient state. Opens a persistence context. Stores account into database, and prints 39 | * an account in persistent state. Persistent account that has associated id, is stored in the database and 40 | * has is associated with a persistence context. 41 | * 42 | * @param account account in transient state 43 | */ 44 | private static void printAccountInPersistentState(Account account) { 45 | // performs logic in scope of persistence context 46 | performWithinPersistenceContext(entityManager -> { 47 | entityManager.persist(account); // stores an account in the database (makes it persistent) 48 | System.out.printf("Account in PERSISTENT state: %s%n", account); 49 | }); 50 | } 51 | 52 | /** 53 | * Receives an account in DETACHED state and prints it. Detached account is an account that has associated id, has 54 | * corresponding database record, but does not associated with a persistent context. 55 | * 56 | * @param account account in DETACHED state 57 | */ 58 | private static void printAccountInDetachedState(Account account) { 59 | System.out.printf("Account in DETACHED state: %s%n", account); 60 | } 61 | 62 | /** 63 | * Receives an account in detached state. Opens a persistence context, merges an account to make it maneged (persistent) 64 | * and then removes an account. Prints account in REMOVED state 65 | * 66 | * @param account account in DETACHED state 67 | */ 68 | private static void printAccountInRemovedState(Account account) { 69 | performWithinPersistenceContext(entityManager -> { 70 | Account mergedAccount = entityManager.merge(account); 71 | entityManager.remove(mergedAccount); 72 | System.out.printf("Account in REMOVED state: %s%n", mergedAccount); 73 | 74 | }); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /persistence-context/src/main/java/com/bobocode/ActionQueue.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.Account; 4 | import com.bobocode.util.JpaUtil; 5 | import com.bobocode.util.TestDataGenerator; 6 | 7 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 8 | 9 | /** 10 | * This code example shows how Hibernate's {@link org.hibernate.engine.spi.ActionQueue} work 11 | */ 12 | public class ActionQueue { 13 | public static void main(String[] args) { 14 | JpaUtil.init("BasicEntitiesH2"); 15 | 16 | tryToSaveAccountWithSameEmailAfterRemove(); 17 | tryToSaveAccountWithSameEmailAfterEmailUpdate(); 18 | 19 | JpaUtil.close(); 20 | } 21 | 22 | private static void tryToSaveAccountWithSameEmailAfterRemove() { 23 | try { 24 | saveAccountWithSameEmailAfterRemove(); 25 | } catch (Exception e) { 26 | System.out.println("Second account wasn't stored, transaction is rolled back"); 27 | } 28 | 29 | } 30 | 31 | /** 32 | * Stores and account, then removes it and tries to store the other account with the save email in the scope of the 33 | * same transaction. It does not work because Hibernate operations are sorted according to its priorities. 34 | * E.g. INSERT is done before UPDATE 35 | */ 36 | private static void saveAccountWithSameEmailAfterRemove() { 37 | Account account = TestDataGenerator.generateAccount(); 38 | performWithinPersistenceContext(entityManager -> entityManager.persist(account)); 39 | Account secondAccount = TestDataGenerator.generateAccount(); 40 | secondAccount.setEmail(account.getEmail()); 41 | 42 | performWithinPersistenceContext(entityManager -> { 43 | Account managedAccount = entityManager.merge(account); 44 | entityManager.remove(managedAccount); // remove first account from the database 45 | // won't work because insert will be performed before remove 46 | entityManager.persist(secondAccount); // store second account with the same email 47 | }); 48 | } 49 | 50 | 51 | private static void tryToSaveAccountWithSameEmailAfterEmailUpdate() { 52 | try { 53 | saveAccountWithSameEmailAfterEmailUpdate(); 54 | } catch (Exception e) { 55 | System.out.println("Second account wasn't stored, transaction is rolled back"); 56 | } 57 | 58 | } 59 | 60 | /** 61 | * Stores and account, then updates its email and tries to store the other account with the previous email value 62 | * in the scope of the same transaction. It does not work because Hibernate operations are sorted according to 63 | * its priorities. 64 | * E.g. INSERT is done before DELETE 65 | */ 66 | private static void saveAccountWithSameEmailAfterEmailUpdate() { 67 | Account account = TestDataGenerator.generateAccount(); 68 | Account secondAccount = TestDataGenerator.generateAccount(); 69 | secondAccount.setEmail(account.getEmail()); 70 | 71 | performWithinPersistenceContext(entityManager -> { 72 | entityManager.persist(account); 73 | account.setEmail("UPDATED"); // change the email of the first account 74 | // won't work because insert will be performed before update 75 | entityManager.persist(secondAccount); // store second account with the same email 76 | }); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/java/com/bobocode/StoringNewRelationship.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.advanced.RoleType; 4 | import com.bobocode.model.basic.Role; 5 | import com.bobocode.model.basic.User; 6 | import com.bobocode.util.JpaUtil; 7 | import com.bobocode.util.TestDataGenerator; 8 | 9 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 10 | 11 | /** 12 | * This example shows different ways of storing new relationship between {@link User} and {@link Role} 13 | */ 14 | public class StoringNewRelationship { 15 | public static void main(String[] args) { 16 | JpaUtil.init("BasicEntitiesH2"); 17 | User user = TestDataGenerator.generateUser(); 18 | 19 | storeNewUserWithRole(user, RoleType.USER); 20 | printUserById(user.getId()); 21 | 22 | addNewRole(user, RoleType.OPERATOR); 23 | printUserById(user.getId()); 24 | 25 | addNewRole(user.getId(), RoleType.ADMIN); 26 | printUserById(user.getId()); 27 | 28 | JpaUtil.close(); 29 | } 30 | 31 | /** 32 | * Stores new {@link User} with a specific role. This method uses cascade persist and a helper method 33 | * {@link User#addRole(Role)}, which links {@link User} object with new {@link Role} object 34 | * 35 | * @param user NEW user 36 | * @param roleType a role type of NEW role 37 | */ 38 | private static void storeNewUserWithRole(User user, RoleType roleType) { 39 | performWithinPersistenceContext(entityManager -> { 40 | Role role = Role.valueOf(roleType); 41 | user.addRole(role); 42 | entityManager.persist(user); // cascade operation will store also new role records 43 | }); 44 | } 45 | 46 | /** 47 | * Loads and prints a {@link User} by id 48 | * 49 | * @param userId 50 | */ 51 | private static void printUserById(long userId) { 52 | performWithinPersistenceContext(entityManager -> { 53 | User mangedUser = entityManager.find(User.class, userId); 54 | System.out.println(mangedUser); 55 | }); 56 | } 57 | 58 | /** 59 | * Adds a new role to an existing user. This method receives existing user object and uses it helper method 60 | * {@link User#addRole(Role)} to add a new role 61 | * 62 | * @param user DETACHED user 63 | * @param roleType role type of new role 64 | */ 65 | private static void addNewRole(User user, RoleType roleType) { 66 | Role role = Role.valueOf(roleType); 67 | user.addRole(role); 68 | performWithinPersistenceContext(entityManager -> entityManager.merge(user)); 69 | } 70 | 71 | /** 72 | * Adds a new role to an existing user by its id. This method does not require a full {@link User} object to add 73 | * a new role. It uses a special method {@link javax.persistence.EntityManager#getReference(Class, Object)} that 74 | * allow to get an entity proxy by its id. This proxy is used to store a new relation between {@link Role} and 75 | * {@link User}. User is is enough to store the relation because role store only user id as a foreign key. 76 | * 77 | * @param userId stored user id 78 | * @param roleType role type of new role 79 | */ 80 | private static void addNewRole(long userId, RoleType roleType) { 81 | performWithinPersistenceContext(entityManager -> { 82 | Role role = Role.valueOf(roleType); 83 | User userProxy = entityManager.getReference(User.class, userId); // does not call db 84 | role.setUser(userProxy); 85 | entityManager.persist(role); 86 | }); 87 | } 88 | 89 | 90 | } 91 | -------------------------------------------------------------------------------- /entity-relationships-management/src/main/java/com/bobocode/CascadeOperations.java: -------------------------------------------------------------------------------- 1 | package com.bobocode; 2 | 3 | import com.bobocode.model.basic.Address; 4 | import com.bobocode.model.basic.Role; 5 | import com.bobocode.model.basic.User; 6 | import com.bobocode.util.JpaUtil; 7 | import com.bobocode.util.TestDataGenerator; 8 | 9 | import javax.persistence.OneToMany; 10 | import java.util.List; 11 | 12 | import static com.bobocode.util.JpaUtil.performWithinPersistenceContext; 13 | 14 | public class CascadeOperations { 15 | 16 | public static void main(String[] args) { 17 | JpaUtil.init("BasicEntitiesH2"); 18 | 19 | 20 | User user = TestDataGenerator.generateUser(); 21 | List roles = TestDataGenerator.generateRoleList(); 22 | Address address = TestDataGenerator.generateAddress(); 23 | 24 | cascadePersist(user, address, roles); 25 | orphanRemoveRoleByUserId(user.getId()); 26 | cascadeRemoveRoleByUserId(user.getId()); 27 | 28 | JpaUtil.close(); 29 | } 30 | 31 | /** 32 | * This methods show an example of cascade persist. Entities address, and roles will be persisted by the cascade. 33 | * Cascade type {@link javax.persistence.CascadeType#ALL} is specified for {@link User#address} and {@link User#roles} 34 | * 35 | * @param user in a transient (NEW) state 36 | * @param address in a transient (NEW) state 37 | * @param roles in a transient (NEW) state 38 | */ 39 | private static void cascadePersist(User user, Address address, List roles) { 40 | System.out.println("\n-------------------------------------"); 41 | performWithinPersistenceContext(em -> { 42 | System.out.println("Persisting user: " + user + ", with address: " + address + ", with roles: " + roles); 43 | // without cascading you would write persist statement for each entity 44 | // em.persist(address); 45 | // em.persist(roles); 46 | user.setAddress(address); 47 | user.addRoles(roles); 48 | em.persist(user); 49 | System.out.println("Persisted user: " + user); 50 | }); 51 | } 52 | 53 | /** 54 | * This method shows an example of orphan removal. Since {@link User#roles} is marked with @{@link OneToMany#orphanRemoval()} 55 | * value true, roles which are removed from a {@link User#roles} should be deleted from the database. 56 | * 57 | * @param userId 58 | */ 59 | private static void orphanRemoveRoleByUserId(Long userId) { 60 | System.out.println("\n-------------------------------------"); 61 | performWithinPersistenceContext(em -> { 62 | User user = em.find(User.class, userId); 63 | System.out.println("User before orphan role remove: " + user); 64 | Role firstRole = user.getRoles().iterator().next(); 65 | System.out.println("Role to remove: " + firstRole); 66 | // without orphan removal you would create a remove statement for role entity 67 | // em.remove(firstRole); 68 | user.removeRole(firstRole); 69 | System.out.println("User after orphan role remove: " + user); 70 | }); 71 | } 72 | 73 | /** 74 | * This method shows an example of cascade remove 75 | */ 76 | private static void cascadeRemoveRoleByUserId(Long userId) { 77 | System.out.println("\n-------------------------------------"); 78 | performWithinPersistenceContext(em -> { 79 | User user = em.find(User.class, userId); 80 | System.out.println("Removing user: " + user); 81 | // without cascading you would write remove statement for each entity. 82 | // em.remove(address); 83 | // em.remove(roles); 84 | em.remove(user); 85 | }); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /jpa-hibernate-tutorial-util/src/main/java/com/bobocode/util/TestDataGenerator.java: -------------------------------------------------------------------------------- 1 | package com.bobocode.util; 2 | 3 | 4 | import com.bobocode.model.Account; 5 | import com.bobocode.model.Gender; 6 | import com.bobocode.model.advanced.RoleType; 7 | import com.bobocode.model.basic.Address; 8 | import com.bobocode.model.basic.Credentials; 9 | import com.bobocode.model.basic.Role; 10 | import com.bobocode.model.basic.User; 11 | import io.codearte.jfairy.Fairy; 12 | import io.codearte.jfairy.producer.person.Person; 13 | 14 | import java.math.BigDecimal; 15 | import java.time.LocalDate; 16 | import java.time.LocalDateTime; 17 | import java.util.List; 18 | import java.util.Random; 19 | import java.util.function.Predicate; 20 | import java.util.stream.Stream; 21 | 22 | import static java.util.stream.Collectors.toList; 23 | 24 | public class TestDataGenerator { 25 | 26 | public static List generateRoleList() { 27 | Random random = new Random(); 28 | Predicate randomPredicate = i -> random.nextBoolean(); 29 | 30 | return Stream.of(RoleType.values()) 31 | .filter(randomPredicate) 32 | .map(Role::valueOf) 33 | .collect(toList()); 34 | } 35 | 36 | public static User generateUser(RoleType... roles) { 37 | User user = generateUser(); 38 | Stream.of(roles) 39 | .map(Role::valueOf) 40 | .forEach(user::addRole); 41 | 42 | return user; 43 | } 44 | 45 | 46 | public static User generateUser() { 47 | Fairy fairy = Fairy.create(); 48 | Person person = fairy.person(); 49 | 50 | User user = new User(); 51 | user.setFirstName(person.getFirstName()); 52 | user.setLastName(person.getLastName()); 53 | user.setBirthday(LocalDate.of( 54 | person.getDateOfBirth().getYear(), 55 | person.getDateOfBirth().getMonthOfYear(), 56 | person.getDateOfBirth().getDayOfMonth())); 57 | user.setCreationDate(LocalDate.now()); 58 | 59 | Credentials credentials = new Credentials(); 60 | credentials.setEmail(person.getEmail()); 61 | credentials.setPassword(person.getPassword()); 62 | credentials.setLastModifiedPassword(LocalDateTime.now()); 63 | 64 | user.setCredentials(credentials); 65 | 66 | return user; 67 | } 68 | 69 | public static 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 | public static Account generateAccount(){ 85 | Fairy fairy = Fairy.create(); 86 | Person person = fairy.person(); 87 | Random random = new Random(); 88 | 89 | 90 | Account fakeAccount = new Account(); 91 | fakeAccount.setFirstName(person.getFirstName()); 92 | fakeAccount.setLastName(person.getLastName()); 93 | fakeAccount.setEmail(person.getEmail()); 94 | fakeAccount.setBirthday(LocalDate.of( 95 | person.getDateOfBirth().getYear(), 96 | person.getDateOfBirth().getMonthOfYear(), 97 | person.getDateOfBirth().getDayOfMonth())); 98 | fakeAccount.setGender(Gender.valueOf(person.getSex().name())); 99 | fakeAccount.setBalance(BigDecimal.valueOf(random.nextInt(200_000))); 100 | fakeAccount.setCreationTime(LocalDateTime.now()); 101 | 102 | return fakeAccount; 103 | } 104 | 105 | } --------------------------------------------------------------------------------