Skip to content

Latest commit

 

History

History
2030 lines (1591 loc) · 55.7 KB

notes.md

File metadata and controls

2030 lines (1591 loc) · 55.7 KB

Introduction

  • 0001 - Master Hibernate and JPA with Spring Boot - Preview
  • 0002 - Master Hibernate and JPA with Spring Boot - Course Overview
  • 0003 - Master Hibernate and JPA with Spring Boot - Git Repository
  • 0004 - Master Hibernate and JPA with Spring Boot - Installing Basic Tools

JPA Introduction

  • 0005 - Quick introduction to JPA

Spring Boot in 10 Steps

  • 0000 - Section Introduction - Introduction to Spring Boot in 10 Steps
  • Step 1 : Introduction to Spring Boot - Goals and Important Features
  • Step 2 : Developing Spring Applications before Spring Boot
  • Step 3 : Using Spring Initializr to create a Spring Boot Application
  • Step 4 : Creating a Simple REST Controller
  • Step 5 : What is Spring Boot Auto Configuration?
  • Step 6 : Spring Boot vs Spring vs Spring MVC
  • Step 7 : Spring Boot Starter Projects - Starter Web and Starter JPA
  • Step 8 : Overview of different Spring Boot Starter Projects
  • Step 9 : Spring Boot Actuator
  • Step 10 : Spring Boot Developer Tools

Journey from JDBC To JPA

  • 0000 - Section Introduction - Introduction to Journey from JDBC To JPA
  • Step 01 - Setting up a project with JDBC, JPA, H2 and Web Dependencies
  • Step 02 - Launching up H2 Console
  • Step 03 - Creating a Database Table in H2
  • Step 04 - Populate data into Person Table
  • Step 05 - Implement findAll persons Spring JDBC Query Method
  • Step 06 - Execute the findAll method using CommandLineRunner
  • Step 07 - A Quick Review - JDBC vs Spring JDBC
  • Step 08 - Whats in the background? Understanding Spring Boot Autoconfiguration
  • Step 09 - Implementing findById Spring JDBC Query Method
  • Step 10 - Implementing deleteById Spring JDBC Update Method
  • Step 11 - Implementing insert and update Spring JDBC Update Methods
  • Step 12 - Creating a custom Spring JDBC RowMapper
  • Step 13 - Quick introduction to JPA
  • Step 14 - Defining Person Entity
  • Step 15 - Implementing findById JPA Repository Method
  • Step 16 - Implementing insert and update JPA Repository Methods
  • Step 17 - Implementing deleteById JPA Repository Method
  • Step 18 - Implementing findAll using JPQL Named Query

JUnit in 5 Steps

  • 0000 - Section Introduction - Introduction to JUnit in 5 Steps
  • Step 1 : What is JUnit and Unit Testing?
  • Step 2 : First JUnit Project and Green Bar
  • Step 3 : First Code and First Unit Test
  • Step 4 : Other assert methods
  • Step 5 : Important annotations

JPA/Hibernate in Depth

  • 0000 - Section Introduction - Introduction to JPA and Hibernate in Depth
  • Step 01 - Create a JPA Project with H2 and Spring Boot
  • Step 02 - Create JPA Entity Course
  • Step 03 - Create findById using JPA Entity Manager
  • Step 04 - Configuring application.properties to enable H2 console and logging
  • Step 05 - Writing Unit Test for findById method
  • Step 06 - Writing a deleteByID method to delete an Entity
  • Step 07 - Writing Unit Test for deleteById method
  • Step 08 - Writing a save method to update and insert an Entity
  • Step 09 - Writing Unit Test for save method
  • Step 10 - Quick Review and Debugging Tips
  • Step 11 - Playing with Entity Manager
  • Step 12 - Entity Manager Methods - clear and detach
  • Step 13 - Entity Manager Methods - refresh
  • Step 14 - A Quick Review of Entity Manager
  • Step 15 - JPQL - Basics
  • Step 16 - JPA and Hibernate Annotations - @Table
  • Step 17 - JPA and Hibernate Annotations - @Column
  • Step 18 - JPA and Hibernate Annotations - @UpdateTimestamp and @CreationTimestamp
  • Step 19 - JPA and Hibernate Annotations - @NamedQuery and @NamedQueries
  • Step 20 - Native Queries - Basics
  • Step 21 - Entities and Relationships - An overview
  • Step 22 - Defining Entities - Student, Passport and Review
  • Step 23 - Introduction to One to One Relationship
  • Step 24 - OneToOne Mapping - Insert Student with Passport
  • Step 25 - OneToOne Mapping - Retrieving Student with Passport and Eager Fetch
  • Step 26 - OneToOne Mapping - Lazy Fetch
  • Step 27 - Transaction, Entity Manager and Persistence Context
  • Step 28 - OneToOne Mapping - Bidirectional Relationship - Part 1
  • Step 29 - OneToOne Mapping - Bidirectional Relationship - Part 2
  • Step 30 - ManyToOne Mapping - Designing the database
  • Step 30 - 02 - ManyToOne Mapping - Implementing the Mapping *****
  • Step 31 - ManyToOne Mapping - Retrieving and inserting Reviews for Course
  • Step 32 - ManyToOne Mapping - Generalizing Insert Reviews
  • Step 33 - ManyToOne Mapping - Wrapping up
  • Step 34 - ManyToMany Mapping - Table Design
  • Step 35 - ManyToMany Mapping - Adding Annotations on Entities
  • Step 36 - ManyToMany Mapping - Fixing two join tables problem
  • Step 37 - ManyToMany Mapping - Customizing the Join Table
  • Step 38 - ManyToMany Mapping - Insert Data and Write Join Query
  • Step 39 - ManyToMany Mapping - Retrieve Data using JPA Relationships
  • Step 40 - ManyToMany Mapping - Insert Student and Course
  • Step 41 - Relationships between JPA Entities - A summary
  • Step 42 - Introduction to Inheritance Hierarchies and Mappings
  • Step 43 - JPA Inheritance Hierarchies and Mappings - Setting up entities
  • Step 44 - JPA Inheritance Hierarchies and Mappings - Setting up a Repository
  • Step 45 - JPA Inheritance Hierarchies and Mappings - Single Table
  • Step 46 - JPA Inheritance Hierarchies and Mappings - Table Per Class
  • Step 47 - JPA Inheritance Hierarchies and Mappings - Joined
  • Step 48 - JPA Inheritance Hierarchies and Mappings - Mapped Super Class
  • Step 49 - JPA Inheritance Hierarchies and Mappings - How to Choose?
  • Step 50 - JPQL - Courses without Students
  • Step 51 - JPQL - Courses with atleast 2 Students and order by
  • Step 52 - JPQL - Courses like 100 Steps
  • Step 53 - JPQL - Using Joins
  • Step 54 - Criteria Query - Retrieving all courses
  • Step 55 - Criteria Query - Courses like 100 Steps
  • Step 56 - Criteria Query - Courses without Students
  • Step 57 - Criteria Query - Using Joins
  • Step 58 - Introduction to Transaction Management
  • Step 59 - Transaction Management - ACID Properties
  • Step 60 - Understanding Dirty, Phanthom and Non Repeatable Reads
  • Step 61 - Understand 4 Isolation Levels
  • Step 62 - Choosing between Isolation Levels
  • Step 63 - Implementing Transaction Management - 3 Things to Decide
  • Step 64 - Introduction to Spring Data JPA
  • Step 65 - Testing the Spring Data JPA Repository with findById.
  • Step 66 - Spring Data JPA Repository - CRUD Methods
  • Step 67 - Sorting using Spring Data JPA Repository
  • Step 68 - Pagination using Spring Data JPA Repository
  • Step 69 - Custom Queries using Spring Data JPA Repository
  • Step 70 - Spring Data REST
  • Step 71 - Introduction to Caching
  • Step 72 - Hibernate and JPA Caching - First Level Cache
  • Step 73 - Hibernate and JPA Caching - Basics of Second Level Cache with EhCache
  • Step 74 - Hibernate and JPA Caching - Second Level Cache Part 2
  • Step 75 - Hibernate Tips - Hibernate Soft Deletes - @SQLDelete and @Where
  • Step 76 - Hibernate Soft Deletes - Part 2
  • Step 77 - JPA Entity Life Cycle Methods
  • Step 78 - Using Embedded and Embeddable with JPA
  • Step 79 - Using Enums with JPA
  • Step 80 - JPA Tip - Be cautious with toString method implementations
  • Step 81 - JPA Tip - When do you use JPA?
  • Step 82 - Performance Tuning - Measure before Tuning
  • Step 83 - Performance Tuning - Indexes
  • Step 84 - Performance Tuning - Use Appropriate Caching
  • Step 85 - Performance Tuning - Eager vs Lazy Fetch
  • Step 86 - Performance Tuning - Avoid N+1 Problems

TO Decide

  • FAQ 1 - When does Hibernate send updates to the database?
  • FAQ 2 - When do we need @Transactional in an Unit Test?
  • FAQ 3 - Do read only methods need a transaction?
  • FAQ 4 - Why do we use @DirtiesContext in an Unit Test?
  • FAQ 5 - How to connect to a different database with Spring Boot?
  • FAQ 6 - Approach to design great applications with JPA?
  • FAQ 7 - Good Practices for developing JPA Applications

Conclusion

  • 9999 - Master Hibernate & JPA with Spring Boot - Congratulations on Completing the Course

Appendix - Spring Framework in 10 Steps

  • 0000 - Section Introduction - Introduction to Spring Framework in 10 Steps
  • Step 1 - Setting up a Spring Project using htttp://start.spring.io
  • Step 2 - Understanding Tight Coupling using the Binary Search Algorithm Example
  • Step 3 - Making the Binary Search Algorithm Example Loosely Coupled
  • Step 4 - Using Spring to Manage Dependencies - @Component, @Autowired
  • Step 5 - What is happening in the background?
  • Step 6 - Dynamic auto wiring and Troubleshooting - @Primary
  • Step 7 - Constructor and Setter Injection
  • Step 8 - Spring Modules
  • Step 9 - Spring Projects
  • Step 10 - Why is Spring Popular?

Notes

Notes while Recording

  • Do Read Only methods need a transaction?
    • Entities - User, Comment
@Transactional
List<Comment> someReadOnlyMethod() {
  
  User user = em.find(User.class, 1L);

  List<Comment> comments = user.getComments();//

  return comments;
}
  • Why do we need @Transactional in Unit Tests some times?
    • Unit Test -> Repository -> EntityManager
    • Unit Test -> EntityManager
  • When does Hibernate fire queries down to database?
@Transactional
void someMethodWithChange() {
   
    //Create Objects
  em.persist(user1);
  em.persist(user2);
  
  em.flush();
  
  //Change user1
  //Change user2
}
//all changes are saved down to the database!

Hibernate Mapping

<hibernate-mapping>
   <class name="Person" table="person">
      <id name="id" type="int" column="id">
         <generator class="native"/>
      </id>
      <property name="firstName" column="first_name" type="string"/>
      <property name="lastName" column="last_name" type="string"/>
      <property name="role" column="role " type="integer"/>
   </class>
</hibernate-mapping>

Entity Manager

  • detach method
  • clear method
  • flush method

Play with Annotations

  • @Table : Indicates table name.
  • @Column : Defining Constraints on Columns.
  • @NamedQueries Indicates list of named queries.
  • @NamedQuery Indicates a Query using static name.
  • @CreationTimestamp
  • @UpdateTimestamp
  • @Transient : Column will not be persisted.
  • @PostLoad

Basics of JPQL

  • Discuss the basics here
@NamedQueries({
    @NamedQuery(name="name1",
                query="Query1"),
    @NamedQuery(name="name2",
                query="Query2"),
})
  • We can discuss the stuff needing Join after discussing relationships

Native Queries

  • Database specific feature

Relationships

  • Three types of relationships

Inheritance

  • best performance - single table strategy

  • best data integrity - joined

  • JPQL queries with Joins

    • basic_empty_courses
    • basic_courses_with_min_three_students

Criteria API

    // 1. Use Criteria Builder to create a Criteria Query returning the
    // expected result object
    // 2. Define roots for tables which are involved in the query
    // 3. Define Predicates etc using Criteria Builder
    // 4. Add Predicates etc to the Criteria Query
    // 5. Build the TypedQuery using the entity manager and criteria query

Transaction Management

  • Spring vs JPA @Transactional
  • Isolation Levels
  • Transactions have importance even within read-only context - like specifying a database isolation-level. We strongly recommend that all database operations occur within the scope of some transaction.

Advanced JPQL

  • basic_courses_order_by
  • join
  • left_outer_join
  • cross_join

Advanced Criterial Query

  • join
  • left_outer_join

Spring Data JPA

  • Spring Data REST

Caching

  • First Level Cache Demo
  • Second Level Cache Demo

Hibernate Tips

  • Implementing Soft Deletes - @Where and @SQLDelete
  • Embedded Entities
  • Using Enumerations With JPA
  • Be cautious about toString
  • Be cautious about Eager fetching on both sides of a relationship
  • When do you use JPA?
    • SQL Database + Static Domain Model + Mostly CRUD + Mostly Simple Queries/Mappings and Updates with few Stored Procedures

Performance Tuning

  • Zero Performance Tuning without Statistics. Check Stats in atleast one environment.
  • Do not use JPA/Hibernate for Database intensive Batch Operations - Use Stored Procedures
  • Add the right indexes on the database - Execution Plan
  • Use Appropriate Caching
    • Be careful about the size of First Level Cache
  • Eager vs Lazy Fetch - Use Lazy fetching mostly
    • Remember that all mapping *ToOne (@ManyToOne and @OneToOne) are EAGER by default.
  • Avoid N+1
    • Entity Graph & Named Entity Graphs & Dynamic Entity Graphs
    • Join Fetch Clause
  • Use Pagination & Batch Updates
query.setFirstResult(0);
query.setMaxResults(10);
  • @Immutable - zero dirty checks!
  • Read only transactions

Complete Code Example

/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.in28minutes.jpa</groupId>
  <artifactId>jpa-demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>jpa-demo</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-jpamodelgen</artifactId>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-ehcache</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          <compilerArguments>
            <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
          </compilerArguments>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
          <execution>
            <phase>process-sources</phase>
            <configuration>
              <sources>
                <source>${project.build.directory}/generated-sources/annotations</source>
              </sources>
            </configuration>
            <goals>
              <goal>add-source</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>maven-annotation</id>
      <url>http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo</url>
    </pluginRepository>
  </pluginRepositories>


</project>

/src/main/java/com/in28minutes/jpa/jpademo/controller/CourseController.java

package com.in28minutes.jpa.jpademo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.repository.CourseRepository;

@RestController
public class CourseController {
  
  @Autowired
  CourseRepository repository;
  
  @GetMapping("/courses/{id}")
  public  Course retrieveCourse(@PathVariable long id){
    return repository.retrieveCourse(id);
  }
}

/src/main/java/com/in28minutes/jpa/jpademo/embedded/entity/Name.java

package com.in28minutes.jpa.jpademo.embedded.entity;

import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
//@Embeddable
public class Name {
  
  @Id
  @GeneratedValue
  protected Long id;
  protected String firstName;
  protected String middleName;
  protected String lastName;
}

/src/main/java/com/in28minutes/jpa/jpademo/embedded/entity/Person.java

package com.in28minutes.jpa.jpademo.embedded.entity;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;

@Entity
public class Person {
  @Id
  @GeneratedValue
  protected Long id;
  
  @OneToOne
  //@Embedded
  protected Name name;

}

/src/main/java/com/in28minutes/jpa/jpademo/inheritence/entity/Employee.java

package com.in28minutes.jpa.jpademo.inheritence.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.MappedSuperclass;

//@MappedSuperclass
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
// @DiscriminatorColumn(name = "disc_type")
public abstract class Employee {

  public Employee() {
  }

  public Employee(String name) {
    this.name = name;
  }

  @GeneratedValue
  @Id
  protected Integer id;

  private String name;

}

/src/main/java/com/in28minutes/jpa/jpademo/inheritence/entity/FullTimeEmployee.java

package com.in28minutes.jpa.jpademo.inheritence.entity;

import java.math.BigDecimal;

import javax.persistence.Entity;

@Entity
public class FullTimeEmployee extends Employee {
  public FullTimeEmployee(){}
  
  public FullTimeEmployee(String name, BigDecimal salary) {
    super(name);
    this.salary = salary;
  }

  protected BigDecimal salary;
}

/src/main/java/com/in28minutes/jpa/jpademo/inheritence/entity/PartTimeEmployee.java

package com.in28minutes.jpa.jpademo.inheritence.entity;

import java.math.BigDecimal;

import javax.persistence.Entity;

@Entity
public class PartTimeEmployee extends Employee {
  
  public PartTimeEmployee(){}
  
  public PartTimeEmployee(String name, BigDecimal hourlyWage) {
    super(name);
    this.hourlyWage = hourlyWage;
  }

  protected BigDecimal hourlyWage;
}

/src/main/java/com/in28minutes/jpa/jpademo/inheritence/repository/EmployeeRepository.java

package com.in28minutes.jpa.jpademo.inheritence.repository;

import java.util.List;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.in28minutes.jpa.jpademo.inheritence.entity.Employee;

@Transactional
@Repository
public class EmployeeRepository {
  
  @Autowired
  EntityManager entityManager;
  
  public void insertEmployee(Employee employee) {
    entityManager.persist(employee);
  }
  
  public List<Employee> allEmployees() {
    return entityManager.createQuery("Select e from Employee e", Employee.class).getResultList();
  }
  
  

}

/src/main/java/com/in28minutes/jpa/jpademo/JpaDemoApplication.java

package com.in28minutes.jpa.jpademo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.in28minutes.jpa.jpademo.inheritence.repository.EmployeeRepository;
import com.in28minutes.jpa.jpademo.relationships.repository.StudentRepository;

@SpringBootApplication
public class JpaDemoApplication implements CommandLineRunner {
  
  @Autowired
  StudentRepository studentRepository;
  
  @Autowired 
  EmployeeRepository employeeRepository;

  public static void main(String[] args) {
    SpringApplication.run(JpaDemoApplication.class, args);
  }

  @Override
  public void run(String... args) throws Exception {
  }

}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/Course.java

package com.in28minutes.jpa.jpademo.relationships.entity;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

@Entity
@Table(name = "Course")
@NamedQuery(query = "select c from Course c", name = "QUERY_ALL_COURSES")
@NamedEntityGraph(name = "graph.CourseAndStudents", 
attributeNodes = @NamedAttributeNode(value = "students"/*, subgraph = "students"),*/)
/*subgraphs = @NamedSubgraph(name = "students", attributeNodes = @NamedAttributeNode("passport"))*/)
@Cacheable
public class Course {

  public Course() {
  }

  public Course(String name) {
    super();
    this.name = name;
  }

  @Id
  @GeneratedValue
  protected Long id;

  protected String name;

  // @OneToMany
  @OneToMany(mappedBy = "course")
  protected List<Review> reviews = new ArrayList<>();

  @ManyToMany
  // @JoinTable(name = "COURSE_STUDENT",
  // joinColumns = @JoinColumn(name = "COURSE_ID"),
  // inverseJoinColumns = @JoinColumn(name = "STUDENT_ID"))
  protected List<Student> students = new ArrayList<>();

  @CreationTimestamp
  private LocalDateTime createDateTime;

  private LocalDate activeFrom;

  @UpdateTimestamp
  private LocalDateTime updateDateTime;

  public Long getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List<Review> getReviews() {
    return reviews;
  }

  public void addReview(Review review) {
    this.reviews.add(review);
  }

  public List<Student> getStudents() {
    return students;
  }

  public void addStudent(Student student) {
    this.students.add(student);
  }

  public LocalDate getActiveFrom() {
    return activeFrom;
  }

  public void setActiveFrom(LocalDate activeFrom) {
    this.activeFrom = activeFrom;
  }

  @Override
  public String toString() {
    return String.format("Course[%s]", name);
  }

}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/Passport.java

package com.in28minutes.jpa.jpademo.relationships.entity;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.Where;

@Entity
@Where(clause = "1=1")
public class Passport {

  protected Passport() {
  }

  public Passport(String number) {
    super();
    this.number = number;
  }

  @Id
  @GeneratedValue
  protected Long id;

  @NotNull
  protected String number;

  // Inverse Relationship
  // bi-directional OneToOne relationship
  // Column will not be created in the table
  // Try removing mappedBy = "passport" => You will see a student_id column
  // will be created in passport
  // @OneToOne
  @OneToOne(fetch = FetchType.LAZY, mappedBy = "passport")
  protected Student student;

  public Long getId() {
    return id;
  }

  public String getNumber() {
    return number;
  }

  public void setNumber(String number) {
    this.number = number;
  }

  public Student getStudent() {
    return student;
  }

  public void setStudent(Student student) {
    this.student = student;
  }
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/Review.java

package com.in28minutes.jpa.jpademo.relationships.entity;

import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class Review {
  
  private Review() {}

  public Review(ReviewRating rating, String description) {
    super();
    this.rating = rating;
    this.description = description;
  }

  @Id
  @GeneratedValue
  protected Long id;
  
  @Enumerated
  protected ReviewRating rating;
    
  protected String description;

  @ManyToOne
  // @JoinColumn(name="COURSE_ID")
  protected Course course;

  public Long getId() {
    return id;
  }

  public ReviewRating getRating() {
    return rating;
  }

  public void setRating(ReviewRating rating) {
    this.rating = rating;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public Course getCourse() {
    return course;
  }

  public void setCourse(Course course) {
    this.course = course;
  }
  
  
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/ReviewRating.java

package com.in28minutes.jpa.jpademo.relationships.entity;
public enum ReviewRating {
  ONE,TWO,THREE,FOUR,FIVE
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/Student.java

package com.in28minutes.jpa.jpademo.relationships.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.MapKeyColumn;
import javax.persistence.MapKeyEnumerated;
import javax.persistence.OneToOne;

import org.hibernate.annotations.SQLDelete;

@Entity
@SQLDelete(sql = "UPDATE student SET state = 'DELETED' WHERE id = ?")
public class Student {

  private Student() {
  }

  public Student(String name, StudentType studentType) {
    super();
    this.name = name;
    this.studentType = studentType;
  }

  @Id
  @GeneratedValue
  protected Long id;

  protected String name;

  @OneToOne(fetch = FetchType.LAZY)
  protected Passport passport;

  // @ManyToMany
  @ManyToMany(mappedBy = "students")
  protected List<Course> courses = new ArrayList<>();

  // @Enumerated
  @Enumerated(EnumType.STRING)
  private StudentType studentType;

  @ElementCollection
  @CollectionTable(name = "STUDENT_PHONE")
  @MapKeyEnumerated(EnumType.STRING)
  @MapKeyColumn(name = "PHONE_TYPE")
  @Column(name = "PHONE_NUM")
  private Map<PhoneType, String> phoneNumbers;

  enum PhoneType {
    Home, Mobile, Work
  }

  public Long getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Passport getPassport() {
    return passport;
  }

  public void setPassport(Passport passport) {
    this.passport = passport;
  }

  public List<Course> getCourses() {
    return courses;
  }

  public void addCourse(Course course) {
    courses.add(course);
  }

  public StudentType getStudentType() {
    return studentType;
  }

  public void setStudentType(StudentType studentType) {
    this.studentType = studentType;
  }

  public Map<PhoneType, String> getPhoneNumbers() {
    return phoneNumbers;
  }

  public void addPhoneNumber(PhoneType phoneType, String number) {
    phoneNumbers.put(phoneType, number);
  }

  @Override
  public String toString() {
    return String.format("Student[%s]", name);
  }

}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/entity/StudentType.java

package com.in28minutes.jpa.jpademo.relationships.entity;
public enum StudentType {
  FullTime, PartTime
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/repository/CourseRepository.java

package com.in28minutes.jpa.jpademo.relationships.repository;

import java.util.List;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.Subgraph;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Course_;
import com.in28minutes.jpa.jpademo.relationships.entity.Review;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;

@Repository
@Transactional
public class CourseRepository {

  @Autowired
  EntityManager entityManager;

  public void createCourse(Course course) {
    entityManager.persist(course);
  }

  public Course retrieveCourse(Long id) {
    return entityManager.find(Course.class, id);
  }

  public void printAllCourseAndStudents() {
    EntityGraph graph = entityManager.getEntityGraph("graph.CourseAndStudents");
    List<Course> courses = entityManager.createQuery("Select c from Course c", Course.class)
        .setHint("javax.persistence.loadgraph", graph).getResultList();
    for (Course course : courses) {
      System.out.println(course + " " + course.getStudents());
    }
  }

  public void printAllCourseAndStudentsDynamicSubgraph() {
    EntityGraph<Course> graph = entityManager.createEntityGraph(Course.class);
    Subgraph<List<Student>> bookSubGraph = graph.addSubgraph(Course_.students);

    List<Course> courses = entityManager.createQuery("Select c from Course c", Course.class)
        .setHint("javax.persistence.loadgraph", graph).getResultList();

    for (Course course : courses) {
      System.out.println(course + " " + course.getStudents());
    }
  }

  public void printAllCourseAndStudentsJoinFetch() {
    List<Course> courses = entityManager.createQuery("Select c from Course c JOIN FETCH c.students s", Course.class)
        .getResultList();
    for (Course course : courses) {
      System.out.println(course + " " + course.getStudents());
    }
  }

  public void updateCourse(Course course) {
    entityManager.merge(course);
  }

  public void createCourseWithStudents(Course course, Student... students) {

    for (Student student : students) {
      course.addStudent(student);
      student.addCourse(course);
      if (student.getId() == null) {
        entityManager.persist(student);
      }
    }

    createCourse(course);

  }

  public void createReviewsForCourse(Course course, Review... reviews) {
    for (Review review : reviews) {
      course.addReview(review);
      review.setCourse(course);
      if (review.getId() == null) {
        entityManager.persist(review);
      }
    }
  }

}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/repository/EntityManagerRepository.java

package com.in28minutes.jpa.jpademo.relationships.repository;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.in28minutes.jpa.jpademo.relationships.entity.Passport;

@Repository
@Transactional
public class EntityManagerRepository {

  @Autowired
  EntityManager entityManager;

  public void doSomething() {
    Passport passport = new Passport("E123456");
    entityManager.persist(passport);
    entityManager.flush();
    passport.setNumber("E123457");
    // entityManager.clear();
    // entityManager.detach(passport);
    // entityManager.refresh(passport);
    // entityManager.remove(passport);
    // entityManager.merge(passport);
    // Queries
    // Entity Graphs
  }
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/repository/StudentRepository.java

package com.in28minutes.jpa.jpademo.relationships.repository;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Passport;
import com.in28minutes.jpa.jpademo.relationships.entity.Review;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;

@Repository
@Transactional
public class StudentRepository {

  @Autowired
  EntityManager entityManager;

  public void createStudentWithPassport(Student student, Passport passport) {
    student.setPassport(passport);
    //passport.setStudent(student);
    entityManager.persist(passport);
    entityManager.persist(student);
        
  }
}

/src/main/java/com/in28minutes/jpa/jpademo/relationships/repository/TransactionManagementRepository.java

package com.in28minutes.jpa.jpademo.relationships.repository;

import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.in28minutes.jpa.jpademo.relationships.entity.Passport;

@Repository
@Transactional
public class TransactionManagementRepository {

  @Autowired
  EntityManager entityManager;

  public void doSomething() {
    Passport passport1 = new Passport("E123456");
    entityManager.persist(passport1);
    Passport passport2 = new Passport(null);
    entityManager.persist(passport2);
  }
}

/src/main/resources/application.properties

spring.h2.console.enabled=true
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.stat=debug
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
#logging.level.org.hibernate.type=TRACE
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
logging.level.net.sf.ehcache=debug

/src/main/resources/data.sql

insert into course(id, name)
values(10101,'Caching in 100 Steps');

/src/test/java/com/in28minutes/jpa/jpademo/CriteriaQueryDemoApplicationTest.java

package com.in28minutes.jpa.jpademo;

import static org.junit.Assert.assertEquals;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Course_;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class CriteriaQueryDemoApplicationTest {

  // @LocalServerPort
  // String port;

  @Autowired
  EntityManager entityManager;

  @Test
  public void basic() {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Course> cq = cb.createQuery(Course.class);
    Root<Course> root = cq.from(Course.class);

    TypedQuery<Course> query = entityManager.createQuery(cq.select(root));

    List<Course> courses = query.getResultList();
    System.out.println(courses);
  }

  @Test
  public void basic2() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Course> cq = cb.createQuery(Course.class);

    Root<Course> course = cq.from(Course.class);
    Predicate condition = cb.like(course.get(Course_.name), "%100 Steps");
    cq.where(condition);

    TypedQuery<Course> query = entityManager.createQuery(cq.select(course));

    List<Course> courses = query.getResultList();
    System.out.println(courses);

    assertEquals(2, courses.size());
    System.out.println(courses);
  }

  @Test
  public void basic_empty_courses() {

    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Course> cq = cb.createQuery(Course.class);

    Root<Course> course = cq.from(Course.class);
    Predicate condition = cb.isEmpty(course.get(Course_.students));
    cq.where(condition);

    TypedQuery<Course> query = entityManager.createQuery(cq.select(course));

    List<Course> courses = query.getResultList();
    System.out.println(courses);

    assertEquals(1, courses.size());
    System.out.println(courses);
  }

  @Test
  public void basic_courses_order_by() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Course> cq = cb.createQuery(Course.class);
    Root<Course> course = cq.from(Course.class);
    cq.orderBy(cb.desc(course.get(Course_.name)));
    TypedQuery<Course> query = entityManager.createQuery(cq.select(course));

    List<Course> courses = query.getResultList();
    System.out.println(courses);

  }

  @Test
  public void join() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Course> cq = cb.createQuery(Course.class);
    Root<Course> course = cq.from(Course.class);
    Join<Course, Student> student = course.join(Course_.students);

    TypedQuery<Course> query = entityManager.createQuery(cq.select(course));

    List<Course> courses = query.getResultList();
    System.out.println(courses);
    assertEquals(5, courses.size());
  }

  @Test
  public void left_outer_join() {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();

    CriteriaQuery<Course> cq = cb.createQuery(Course.class);
    Root<Course> course = cq.from(Course.class);
    Join<Course, Student> student = course.join(Course_.students, JoinType.LEFT);

    TypedQuery<Course> query = entityManager.createQuery(cq.select(course));

    List<Course> courses = query.getResultList();
    System.out.println(courses);
    assertEquals(6, courses.size());
  }
}

/src/test/java/com/in28minutes/jpa/jpademo/EntityManagerDemoApplicationTests.java

package com.in28minutes.jpa.jpademo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.repository.EntityManagerRepository;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class EntityManagerDemoApplicationTests {

  @Autowired
  EntityManagerRepository entityManagerRepository;

  @Test
  public void someTest() {
    entityManagerRepository.doSomething();
  }
}

/src/test/java/com/in28minutes/jpa/jpademo/InheritanceDemoApplicationTest.java

package com.in28minutes.jpa.jpademo;

import java.math.BigDecimal;

import javax.persistence.EntityManager;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.inheritence.entity.FullTimeEmployee;
import com.in28minutes.jpa.jpademo.inheritence.entity.PartTimeEmployee;
import com.in28minutes.jpa.jpademo.inheritence.repository.EmployeeRepository;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class InheritanceDemoApplicationTest {

  // @LocalServerPort
  // String port;

  @Autowired
  EntityManager entityManager;

  @Autowired
  EmployeeRepository employeeRepository;

  @Test
  public void basic() {
    employeeRepository.insertEmployee(new PartTimeEmployee("PartTimeEE", new BigDecimal(100)));
    employeeRepository.insertEmployee(new FullTimeEmployee("FullTimeEE", new BigDecimal(10)));
    System.out.println(employeeRepository.allEmployees());
  }
}

/src/test/java/com/in28minutes/jpa/jpademo/JpaDemoApplicationTests.java

package com.in28minutes.jpa.jpademo;

import java.time.LocalDate;
import java.time.Month;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Passport;
import com.in28minutes.jpa.jpademo.relationships.entity.Review;
import com.in28minutes.jpa.jpademo.relationships.entity.ReviewRating;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;
import com.in28minutes.jpa.jpademo.relationships.entity.StudentType;
import com.in28minutes.jpa.jpademo.relationships.repository.CourseRepository;
import com.in28minutes.jpa.jpademo.relationships.repository.StudentRepository;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class JpaDemoApplicationTests {

  // @LocalServerPort
  // String port;

  @Autowired
  EntityManager entityManager;

  @Autowired
  CourseRepository courseRepository;

  @Autowired
  StudentRepository studentRepository;

  @Test
  public void createCourseWithStudents() {
    Student student = new Student("Ranga", StudentType.FullTime);
    courseRepository.createCourseWithStudents(new Course("Spring in 100 Steps"), student);
    courseRepository.createCourseWithStudents(new Course("Spring Boot in 100 Steps"), student);
  }

  @Test
  public void createReviewsForCourse() {
    Course course = new Course("JPA in 100 Steps");
    courseRepository.createCourse(course);
    courseRepository.createReviewsForCourse(course, new Review(ReviewRating.FIVE, "Awesome Course"),
        new Review(ReviewRating.FIVE, "Wow!"));
    
  }

  @Test
  public void createCourse() {
    courseRepository.createCourse(new Course("JPA in 100 Steps"));
  }

  @Test
  @Transactional
  public void createStudentWithPassport() {
    Student student = new Student("Ranga", StudentType.FullTime);
    Passport passport = new Passport("A12345678");
    studentRepository.createStudentWithPassport(student, passport);
  }

  @Test
  public void updateCourse() {
    Course course = courseRepository.retrieveCourse(10001L);
    course.setName("JPA in 100 Steps - updated");
    course.setActiveFrom(LocalDate.of(2018, Month.APRIL, 10));
    courseRepository.updateCourse(course);
  }

  @After
  public void printAllData() {
    System.out.println("Dummy");
  }
}

/src/test/java/com/in28minutes/jpa/jpademo/JPQLDemoApplicationTest.java

package com.in28minutes.jpa.jpademo;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class JPQLDemoApplicationTest {

  // @LocalServerPort
  // String port;

  @Autowired
  EntityManager entityManager;

  @Test
  public void basic() {
    Query query = entityManager.createQuery("SELECT c FROM Course c");
    System.out.println(query.getResultList());
  }

  @Test
  public void basic_typed() {
    TypedQuery<Course> query = entityManager.createQuery("SELECT c FROM Course c", Course.class);
    List<Course> resultList = query.getResultList();
    System.out.println(resultList);
  }

  @Test
  public void basic2() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE c.name like '%100 Steps'");
    List resultList = query.getResultList();
    assertEquals(2, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_empty_courses() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE c.students IS EMPTY");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_courses_with_min_three_students() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE size(c.students) >= 3");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_courses_order_by() {
    Query query = entityManager.createQuery("SELECT c FROM Course c ORDER BY size(c.students) DESC");
    List resultList = query.getResultList();
    assertEquals(3, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic3() {
    Query query = entityManager.createQuery("SELECT s FROM Student s WHERE s.passport.number like 'N%'");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic4() {
    Query query = entityManager.createQuery("SELECT s FROM Student s WHERE s.passport.number like 'N%'");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  // BETWEEN 100 and 1000
  // IS NULL
  // upper, lower, trim, length
  // Group by, having
  
  
  @Test
  public void join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c JOIN c.students s");
    List resultList = query.getResultList();
    System.out.println(resultList.get(1).getClass());
    assertEquals(5, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void left_outer_join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c LEFT JOIN c.students s");
    List<Object[]> resultList = query.getResultList();
    assertEquals(6, resultList.size());
    for (Object[] result : resultList) {
      Course course = (Course) result[0];
      Student student = (Student) result[1];
      System.out.println(course + " " + student);
    }
  }

  @Test
  public void cross_join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c, Student s");
    List resultList = query.getResultList();
    assertEquals(12, resultList.size());
    System.out.println(resultList);
  }

}

/src/test/java/com/in28minutes/jpa/jpademo/NativeQueriesDemoApplicationTest.java

package com.in28minutes.jpa.jpademo;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.entity.Course;
import com.in28minutes.jpa.jpademo.relationships.entity.Student;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class NativeQueriesDemoApplicationTest {

  // @LocalServerPort
  // String port;

  @Autowired
  EntityManager entityManager;

  @Test
  public void basic() {
    Query query = entityManager.createNativeQuery("SELECT * FROM Course c");
    System.out.println(query.getResultList());
  }

  @Test
  public void basic_with_parameter() {
    Query query = entityManager.createNativeQuery("SELECT * FROM Course c where c.id = ?");
    query.setParameter(1  , 10001L);
    
    List resultList = query.getResultList();
    assertEquals(1,resultList.size());

    System.out.println(resultList);
  }

  @Test
  public void basic_with_named_parameter() {
    Query query = entityManager.createNativeQuery("SELECT * FROM Course c where c.id = :id");
    query.setParameter("id", 10001L);
    
    List resultList = query.getResultList();
    assertEquals(1,resultList.size());

    System.out.println(resultList);
  }
  
  @Test
  public void basic_with_named_native_query() {
  }

  @Test
  @Transactional
  public void updating_a_number_of_rows() {
    Query query = entityManager.createNativeQuery("Update Course Set create_date_time=sysdate()");
    int executeUpdate = query.executeUpdate();
    System.out.println(executeUpdate);
  }


  @Test
  public void basic_typed() {
    TypedQuery<Course> query = entityManager.createQuery("SELECT c FROM Course c", Course.class);
    List<Course> resultList = query.getResultList();
    System.out.println(resultList);
  }

  @Test
  public void basic2() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE c.name like '%100 Steps'");
    List resultList = query.getResultList();
    assertEquals(2, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_empty_courses() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE c.students IS EMPTY");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_courses_with_min_three_students() {
    Query query = entityManager.createQuery("SELECT c FROM Course c WHERE size(c.students) >= 3");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic_courses_order_by() {
    Query query = entityManager.createQuery("SELECT c FROM Course c ORDER BY size(c.students) DESC");
    List resultList = query.getResultList();
    assertEquals(3, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic3() {
    Query query = entityManager.createQuery("SELECT s FROM Student s WHERE s.passport.number like 'N%'");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void basic4() {
    Query query = entityManager.createQuery("SELECT s FROM Student s WHERE s.passport.number like 'N%'");
    List resultList = query.getResultList();
    assertEquals(1, resultList.size());
    System.out.println(resultList);
  }

  // BETWEEN 100 and 1000
  // IS NULL
  // upper, lower, trim, length
  // Group by, having
  
  
  @Test
  public void join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c JOIN c.students s");
    List resultList = query.getResultList();
    System.out.println(resultList.get(1).getClass());
    assertEquals(5, resultList.size());
    System.out.println(resultList);
  }

  @Test
  public void left_outer_join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c LEFT JOIN c.students s");
    List<Object[]> resultList = query.getResultList();
    assertEquals(6, resultList.size());
    for (Object[] result : resultList) {
      Course course = (Course) result[0];
      Student student = (Student) result[1];
      System.out.println(course + " " + student);
    }
  }

  @Test
  public void cross_join() {
    Query query = entityManager.createQuery("SELECT c, s FROM Course c, Student s");
    List resultList = query.getResultList();
    assertEquals(12, resultList.size());
    System.out.println(resultList);
  }

}

/src/test/java/com/in28minutes/jpa/jpademo/PerformanceDemoApplicationTest.java

package com.in28minutes.jpa.jpademo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.repository.CourseRepository;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PerformanceDemoApplicationTest {

  @Autowired
  CourseRepository courseRepository;
  
  @Test
  public void testNplus1(){
    courseRepository.printAllCourseAndStudents();
    //courseRepository.printAllCourseAndStudentsDynamicSubgraph();
    //courseRepository.printAllCourseAndStudentsJoinFetch();
  }


}

/src/test/java/com/in28minutes/jpa/jpademo/TransactionManagementDemoApplicationTests.java

package com.in28minutes.jpa.jpademo;

import static org.junit.Assert.assertNull;

import javax.persistence.EntityManager;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.in28minutes.jpa.jpademo.relationships.entity.Passport;
import com.in28minutes.jpa.jpademo.relationships.repository.TransactionManagementRepository;

@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = WebEnvironment.RANDOM_PORT)
public class TransactionManagementDemoApplicationTests {

  @Autowired
  TransactionManagementRepository transactionManagementRepository;

  @Autowired
  EntityManager entityManager;

  @Test
  public void someTest() {
    
    try {
      transactionManagementRepository.doSomething();
    } catch (Exception e) { }
    
    assertNull(entityManager.find(Passport.class, 1L));
  }
}

/src/test/resources/data.sql

insert into passport(id, number)
values(40001, 'L123456');

insert into passport(id, number)
values(40002, 'M123456');

insert into passport(id, number)
values(40003, 'N123456');

insert into passport(id, number)
values(40004, 'O123456');


insert into course(id, name)
values(10001,'Spring in 100 Steps');

insert into course(id, name)
values(10002,'Spring Boot in 100 Steps');

insert into course(id, name)
values(10003,'JPA in 50 Steps');

insert into student(id, name,passport_id)
values(20001, 'Adam',40001);

insert into student(id, name,passport_id)
values(20002, 'Buck',40002);

insert into student(id, name,passport_id)
values(20003, 'Chris',40003);

insert into student(id, name,passport_id)
values(20004, 'Dennis',40004);


insert into course_students(courses_id,students_id)
values(10001,20001);

insert into course_students(courses_id,students_id)
values(10001,20002);

insert into course_students(courses_id,students_id)
values(10001,20003);

insert into course_students(courses_id,students_id)
values(10002,20001);

insert into course_students(courses_id,students_id)
values(10002,20002);