0% found this document useful (0 votes)
104 views

09 Spring Boot 3 Jpa Advanced Mappings

Here are the key steps to create the InstructorDetail class: 1. Add the @Entity annotation to mark it as a JPA entity 2. Add the @Table annotation to specify the table name in the database 3. Add fields for the columns in the table 4. Add the @Id annotation for the primary key field 5. Add @GeneratedValue and @Column annotations as needed This maps the Java class to the instructor_detail database table.

Uploaded by

def46917
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
104 views

09 Spring Boot 3 Jpa Advanced Mappings

Here are the key steps to create the InstructorDetail class: 1. Add the @Entity annotation to mark it as a JPA entity 2. Add the @Table annotation to specify the table name in the database 3. Add fields for the columns in the table 4. Add the @Id annotation for the primary key field 5. Add @GeneratedValue and @Column annotations as needed This maps the Java class to the instructor_detail database table.

Uploaded by

def46917
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 165

JPA / Hibernate

Advanced Mappings

© luv2code LLC
Basic Mapping

Java Class Database Table

Hibernate

www.luv2code.com © luv2code LLC


Advanced Mappings
• In the database, you most likely will have

• Multiple Tables

• Relationships between Tables

• Need to model this with Hibernate

www.luv2code.com © luv2code LLC


Advanced Mappings
• One-to-One

• One-to-Many, Many-to-One

• Many-to-Many

www.luv2code.com © luv2code LLC


One-to-One Mapping
• An instructor can have an “instructor detail” entity

• Similar to an “instructor profile”

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


One-to-Many Mapping
• An instructor can have many courses

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Many-to-Many Mapping
• A course can have many students

• A student can have many courses

Course Student

Course Student

Course Student

Course Student

www.luv2code.com © luv2code LLC


Important Database Concepts
• Primary key and foreign key

• Cascade

www.luv2code.com © luv2code LLC


Primary Key and Foreign Key
• Primary key: identify a unique row in a table

• Foreign key:

• Link tables together

• a field in one table that refers to primary key in another table

www.luv2code.com © luv2code LLC


Foreign Key Example Foreign key
column

Table: instructor

Table: instructor_detail

www.luv2code.com © luv2code LLC


Cascade
• You can cascade operations

• Apply the same operation to related entities

Instructor
Instructor
Detail
save

www.luv2code.com © luv2code LLC


Cascade
• If we delete an instructor, we should also delete their instructor_detail

• This is known as “CASCADE DELETE”

Instructor
Instructor
Detail
delete

www.luv2code.com © luv2code LLC


Cascade Delete Foreign key
column

Table: instructor

Table: instructor_detail

www.luv2code.com © luv2code LLC


Cascade Delete
• Cascade delete depends on the use case No way!!!
• Should we do cascade delete here???

Course Student

Course Student

Course Student

Course Student

www.luv2code.com © luv2code LLC


Cascade Delete Developer can
configure cascading
• Cascade delete depends on the use case

• Should we do cascade delete here???

Course Student

Course Student

Course Student

Course Student

www.luv2code.com © luv2code LLC


Fetch Types: Eager vs Lazy Loading
• When we fetch / retrieve data, should we retrieve EVERYTHING?

• Eager will retrieve everything

• Lazy will retrieve on request

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Uni-Directional

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


Bi-Directional

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


JPA / Hibernate
One-to-One

© luv2code LLC
One-to-One Mapping
• An instructor can have an “instructor detail” entity

• Similar to an “instructor profile”

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


Uni-Directional

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


Development Process: One-to-One
1. Prep Work - Define database tables
Step-
By-S
tep
2. Create InstructorDetail class

3. Create Instructor class

4. Create Main App

www.luv2code.com © luv2code LLC


table: instructor_detail
File: create-db.sql

CREATE TABLE `instructor_detail` (

`id` int(11) NOT NULL AUTO_INCREMENT,


`youtube_channel` varchar(128) DEFAULT NULL,
`hobby` varchar(45) DEFAULT NULL,

);

...

www.luv2code.com © luv2code LLC


table: instructor_detail
File: create-db.sql

CREATE TABLE `instructor_detail` (

`id` int(11) NOT NULL AUTO_INCREMENT,


`youtube_channel` varchar(128) DEFAULT NULL,
`hobby` varchar(45) DEFAULT NULL,

PRIMARY KEY (`id`)


);

...

www.luv2code.com © luv2code LLC


table: instructor
File: create-db.sql

CREATE TABLE `instructor` (

`id` int(11) NOT NULL AUTO_INCREMENT,


`first_name` varchar(45) DEFAULT NULL,
`last_name` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`instructor_detail_id` int(11) DEFAULT NULL,

PRIMARY KEY (`id`)


...
);

www.luv2code.com © luv2code LLC


Foreign Key
• Link tables together

• A field in one table that refers to primary key in another table

www.luv2code.com © luv2code LLC


Foreign Key Example Foreign key
column

Table: instructor

Table: instructor_detail

www.luv2code.com © luv2code LLC


Defining Foreign Key
File: create-db.sql


CREATE TABLE `instructor` (

CONSTRAINT `FK_DETAIL` FOREIGN KEY (`instructor_detail_id`)


REFERENCES `instructor_detail` (`id`)

);

www.luv2code.com © luv2code LLC


More on Foreign Key
• Main purpose is to preserve relationship between tables

• Referential Integrity

• Prevents operations that would destroy relationship

• Ensures only valid data is inserted into the foreign key column

• Can only contain valid reference to primary key in other table

www.luv2code.com © luv2code LLC


Development Process: One-to-One
Step-
By-S
tep
1. Prep Work - Define database tables

2. Create InstructorDetail class

3. Create Instructor class

4. Create Main App

www.luv2code.com
Step 2: Create InstructorDetail class
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

www.luv2code.com
Step 2: Create InstructorDetail class
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

www.luv2code.com
Step 2: Create InstructorDetail class
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="youtube_channel")
private String youtubeChannel;

www.luv2code.com
Step 2: Create InstructorDetail class
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="youtube_channel")
private String youtubeChannel;

@Column(name="hobby")
private String hobby;

www.luv2code.com
Step 2: Create InstructorDetail class
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="youtube_channel")
private String youtubeChannel;

@Column(name="hobby")
private String hobby;

// constructors

// getters / setters
}

www.luv2code.com
Step 3: Create Instructor class
@Entity
@Table(name="instructor")
public class Instructor {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="first_name")
private String firstName;

@Column(name="last_name")
private String lastName;

@Column(name="email")
private String email;


// constructors, getters / setters
}

www.luv2code.com
Step 3: Create Instructor class - @OneToOne

@Entity
@Table(name="instructor")
public class Instructor {

@OneToOne
@JoinColumn(name=“instructor_detail_id")
private InstructorDetail instructorDetail;

// constructors, getters / setters
}

www.luv2code.com
Entity Lifecycle
Operations Description

Detach If entity is detached, it is not associated with a Hibernate session

If instance is detached from session, then merge will reattach to


Merge
session
Transitions new instances to managed state. Next flush / commit
Persist
will save in db.
Transitions managed entity to be removed. Next flush / commit
Remove
will delete from db.

Refresh Reload / synch object with data from db. Prevents stale data

www.luv2code.com
Entity Lifecycle - session method calls

New / Transient

commit
save / persist rollback / new

delete / remove
Persistent /
refresh Removed
Managed
persist/rollback

commit/rollback/close merge rollback

Detached

www.luv2code.com
Cascade
• Recall: You can cascade operations

• Apply the same operation to related entities

Instructor
Instructor
Detail
save

www.luv2code.com
Cascade Delete Foreign key
column

Table: instructor

Table: instructor_detail

www.luv2code.com
@OneToOne - Cascade Types

Cascade Type Description

PERSIST If entity is persisted / saved, related entity will also be persisted

REMOVE If entity is removed / deleted, related entity will also be deleted

REFRESH If entity is refreshed, related entity will also be refreshed


If entity is detached (not associated w/ session),
DETACH
then related entity will also be detached
MERGE If entity is merged, then related entity will also be merged

ALL All of above cascade types

www.luv2code.com
Configure Cascade Type
@Entity
@Table(name="instructor")
public class Instructor {

@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="instructor_detail_id")
private InstructorDetail instructorDetail;


// constructors, getters / setters
} By default, no operations are cascaded.

www.luv2code.com
Configure Multiple Cascade Types

@OneToOne(cascade={CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.PERSIST,
CascadeType.REFRESH,
CascadeType.REMOVE})

www.luv2code.com
Step 4 - Creating Spring Boot - Command Line App
• We will create a Spring Boot - Command Line App

• This will allow us to focus on JPA / Hibernate

• Leverage our DAO pattern as in previous videos

Data Access Object

MainApp AppDAO

www.luv2code.com © luv2code LLC


Define DAO interface
import com.luv2code.cruddemo.entity.Instructor;

public interface AppDAO {

void save(Instructor theInstructor);

Instructor
Instructor
Detail

www.luv2code.com © luv2code LLC


Define DAO implementation
import com.luv2code.cruddemo.entity.Instructor;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class AppDAOImpl implements AppDAO {

// define field for entity manager


private EntityManager entityManager;
Inject the Entity Manager
// inject entity manager using constructor injection
@Autowired
public AppDAOImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}

@Override
@Transactional
public void save(Instructor theInstructor) {
entityManager.persist(theInstructor);
Save the
} Java object
}

www.luv2code.com © luv2code LLC


Define DAO implementation
import com.luv2code.cruddemo.entity.Instructor;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class AppDAOImpl implements AppDAO {

// define field for entity manager


private EntityManager entityManager;

// inject entity manager using constructor injection


@Autowired This will ALSO save the details object
public AppDAOImpl(EntityManager entityManager) {
this.entityManager = entityManager;
} Because of CascadeType.ALL
@Override
@Transactional
public void save(Instructor theInstructor) {
entityManager.persist(theInstructor);
}

www.luv2code.com © luv2code LLC


Update main app
@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {


SpringApplication.run(MainApplication.class, args);
Inject the AppDAO
}

@Bean
public CommandLineRunner commandLineRunner(AppDAO appDAO) {
return runner -> { private void createInstructor(AppDAO appDAO) {

createInstructor(appDAO); // create the instructor


} Instructor tempInstructor =
new Instructor("Chad", "Darby", "darby@luv2code.com");

// create the instructor detail
InstructorDetail tempInstructorDetail =
new InstructorDetail(
Remember: "http://www.luv2code.com/youtube",
"Luv 2 code!!!");

// associate the objects


This will ALSO save the details object tempInstructor.setInstructorDetail(tempInstructorDetail);

// save the instructor


System.out.println("Saving instructor: " + tempInstructor);
Because of CascadeType.ALL appDAO.save(tempInstructor);

System.out.println("Done!");

In AppDAO, delegated to
}
}

entityManager.persist(…)

www.luv2code.com © luv2code LLC


JPA / Hibernate
One-to-One: Find an entity

© luv2code LLC
Define DAO implementation
@Repository
public class AppDAOImpl implements AppDAO { We’ll add supporting code in the video:
interface, main app

@Override
public Instructor findInstructorById(int theId) {
return entityManager.find(Instructor.class, theId);
}

}
This will ALSO retrieve the instructor details object

Because of default behavior of @OneToOne


fetch type is eager … more on fetch types later

www.luv2code.com © luv2code LLC


JPA / Hibernate
One-to-One: Delete an entity

© luv2code LLC
Define DAO implementation
@Repository
public class AppDAOImpl implements AppDAO {

We’ll add supporting code in the video:
@Override interface, main app
@Transactional
public void deleteInstructorById(int theId) {

// retrieve the instructor


Instructor tempInstructor = entityManager.find(Instructor.class, theId);

// delete the instructor


entityManager.remove(tempInstructor);
}

} This will ALSO delete the instructor details object

Because of CascadeType.ALL

www.luv2code.com © luv2code LLC


JPA / Hibernate
One-to-One: Bi-Directional

© luv2code LLC
One-to-One Mapping
• We currently have a uni-directional mapping

Instructor
Instructor
Detail

www.luv2code.com
New Use Case
• If we load an InstructorDetail

• Then we’d like to get the associated Instructor

• Can’t do this with current uni-directional relationship :-(

Instructor
Instructor
Detail

www.luv2code.com
Bi-Directional
• Bi-Directional relationship is the solution

• We can start with InstructorDetail and make it back to the Instructor

Instructor
Instructor
Detail

www.luv2code.com
Bi-Directional - The Good News

• To use Bi-Directional, we can keep the existing database schema

• No changes required to database

• Simply update the Java code

www.luv2code.com
Development Process: One-to-One (Bi-Directional)
Step-
By-S
1. Make updates to InstructorDetail class: tep

1. Add new field to reference Instructor

2. Add getter/setter methods for Instructor

3. Add @OneToOne annotation

2. Create Main App

www.luv2code.com
Step 1.1: Add new field to reference Instructor
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

private Instructor instructor;

www.luv2code.com
Step 1.2: Add getter/setter methods Instructor
@Entity
@Table(name="instructor_detail")
public class InstructorDetail {

private Instructor instructor;

public Instructor getInstructor() {


return instructor;
}

public void setInstructor(Instructor instructor) {


this.instructor = instructor;
}

www.luv2code.com
Step 1.3: Add @OneToOne annotation
@Entity
@Table(name="instructor_detail")
public class InstructorDetail { Refers to “instructorDetail” property
… in “Instructor” class

@OneToOne(mappedBy="instructorDetail")
private Instructor instructor;

public Instructor getInstructor() {


return instructor;
}

public void setInstructor(Instructor instructor) {


this.instructor = instructor;
}

www.luv2code.com
More on mappedBy public class InstructorDetail {

@OneToOne(mappedBy="instructorDetail")
private Instructor instructor;

• mappedBy tells Hibernate

• Look at the instructorDetail property in the Instructor class

• Use information from the Instructor class @JoinColumn

• To help find associated instructor


public class Instructor {

@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="instructor_detail_id")
private InstructorDetail instructorDetail;

www.luv2code.com
Add support for Cascading Cascade all operations
@Entity to the associated Instructor
@Table(name="instructor_detail")
public class InstructorDetail {

@OneToOne(mappedBy="instructorDetail", cascade=CascadeType.ALL)
private Instructor instructor;

public Instructor getInstructor() {


return instructor;
}

public void setInstructor(Instructor instructor) {


this.instructor = instructor;
}

www.luv2code.com
Define DAO interface
import com.luv2code.cruddemo.entity.Instructor;

public interface AppDAO {

InstructorDetail findInstructorDetailById(int theId);

www.luv2code.com © luv2code LLC


Define DAO implementation
import com.luv2code.cruddemo.entity.InstructorDetail;

@Repository
public class AppDAOImpl implements AppDAO {

// define field for entity manager


private EntityManager entityManager; This will ALSO retrieve the instructor object

Because
// inject entity manager using constructor injection of default behavior of @OneToOne

@Override
public InstructorDetail findInstructorDetailById(int theId) {

return entityManager.find(InstructorDetail.class, theId);


Retrieve the
} InstructorDetail

www.luv2code.com © luv2code LLC


Update main app
@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {


SpringApplication.run(MainApplication.class, args);
Inject the AppDAO
}

@Bean
public CommandLineRunner commandLineRunner(AppDAO appDAO) {
return runner -> {

findInstructorDetail(appDAO);
} private void findInstructorDetail(AppDAO appDAO) {

… int theId = 1;
System.out.println("Finding instructor detail id: " + theId);

InstructorDetail tempInstructorDetail = appDAO.findInstructorDetailById(theId);

System.out.println("tempInstructorDetail: " + tempInstructorDetail);


System.out.println("the associated instructor: " + tempInstructorDetail.getInstructor());

}
}

www.luv2code.com © luv2code LLC


JPA / Hibernate
One-to-Many

© luv2code LLC
One-to-Many Mapping
• An instructor can have many courses

• Bi-directional

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Many-to-One Mapping
• Many courses can have one instructor

• Inverse / opposite of One-to-Many

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Real-World Project Requirement
• If you delete an instructor, DO NOT delete the courses
Do not apply
• If you delete a course, DO NOT delete the instructor cascading deletes!

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Development Process: One-to-Many
1. Prep Work - Define database tables
Step-
By-S
tep
2. Create Course class

3. Update Instructor class

4. Create Main App

www.luv2code.com © luv2code LLC


table: course
File: create-db.sql

CREATE TABLE `course` (

`id` int(11) NOT NULL AUTO_INCREMENT,


`title` varchar(128) DEFAULT NULL,
`instructor_id` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `TITLE_UNIQUE` (`title`),


... Prevent duplicate course titles
);

www.luv2code.com © luv2code LLC


table: course - foreign key
File: create-db.sql

CREATE TABLE `course` (



KEY `FK_INSTRUCTOR_idx` (`instructor_id`),
CONSTRAINT `FK_INSTRUCTOR`
FOREIGN KEY (`instructor_id`)
REFERENCES `instructor` (`id`)

);

www.luv2code.com © luv2code LLC


table: instructor - no changes

www.luv2code.com © luv2code LLC


Step 2: Create Course class
@Entity
@Table(name="course")
public class Course {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="title")
private String title;


// constructors, getters / setters
}

www.luv2code.com © luv2code LLC


Step 2: Create Course class - @ManyToOne

@Entity
@Table(name="course")
public class Course {

@ManyToOne
@JoinColumn(name="instructor_id")
private Instructor instructor;

// constructors, getters / setters
}

www.luv2code.com © luv2code LLC


Step 3: Update Instructor - reference courses
@Entity
@Table(name="instructor")
public class Instructor {

private List<Course> courses;

www.luv2code.com © luv2code LLC


Step 3: Update Instructor - reference courses
@Entity
@Table(name="instructor")
public class Instructor {

private List<Course> courses;

public List<Course> getCourses() {


return courses;
}

public void setCourses(List<Course> courses) {


this.courses = courses;
}

www.luv2code.com © luv2code LLC


Add @OneToMany annotation
@Entity
@Table(name="instructor")
public class Instructor { Refers to “instructor” property

in “Course” class
@OneToMany(mappedBy="instructor")
private List<Course> courses;

public List<Course> getCourses() {


return courses;
}

public void setCourses(List<Course> courses) {


this.courses = courses;
}

www.luv2code.com © luv2code LLC


More: mappedBy public class Instructor {

@OneToMany(mappedBy="instructor")
private List<Course> courses;

• mappedBy tells Hibernate

• Look at the instructor property in the Course class

• Use information from the Course class @JoinColumn

• To help find associated courses


for instructor public class Course {

@ManyToOne
@JoinColumn(name="instructor_id")
private Instructor instructor;

www.luv2code.com © luv2code LLC


Add support for Cascading
Do not apply
cascading deletes!
@Entity
@Table(name="instructor")
public class Instructor {

@OneToMany(mappedBy="instructor",
cascade={CascadeType.PERSIST, CascadeType.MERGE
CascadeType.DETACH, CascadeType.REFRESH})
private List<Course> courses;

www.luv2code.com © luv2code LLC


Add support for Cascading
@Entity
@Table(name="course")
public class Course {

@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE
CascadeType.DETACH, CascadeType.REFRESH})
@JoinColumn(name="instructor_id")
private Instructor instructor;


// constructors, getters / setters
}
Do not apply
cascading deletes!

www.luv2code.com © luv2code LLC


Add convenience methods for bi-directional
@Entity
@Table(name="instructor")
public class Instructor {

// add convenience methods for bi-directional relationship

public void add(Course tempCourse) {

if (courses == null) {
courses = new ArrayList<>();
}

courses.add(tempCourse);

tempCourse.setInstructor(this);
}

}

www.luv2code.com © luv2code LLC


Fetch Types: Eager vs Lazy

© luv2code LLC
Fetch Types: Eager vs Lazy Loading
• When we fetch / retrieve data, should we retrieve EVERYTHING?

• Eager will retrieve everything

• Lazy will retrieve on request

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Eager Loading
• Eager loading will load all dependent entities

• Load instructor and all of their courses at once

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Eager Loading
• What about course and students?

• Could easily turn into a performance nightmare …

Course

www.luv2code.com © luv2code LLC


Eager Loading
• In our app, if we are searching for a course by keyword

• Only want a list of matching courses

• Eager loading would still load all students for each course …. not good!

www.luv2code.com © luv2code LLC


Best Practice
Only load data when absolutely needed

Prefer Lazy loading


instead of
Eager loading

www.luv2code.com © luv2code LLC


Lazy Loading Lazy Loading is
preferred
• Lazy loading will load the main entity first

• Load dependent entities on demand (lazy)


Load on
demand (lazy)
Load course
first

www.luv2code.com © luv2code LLC


Real-World Use Case
• Search for instructors

www.luv2code.com © luv2code LLC


Real-World Use Case
• In Master view, use lazy loading

• In Detail view, retrieve the entity and necessary dependent entities

www.luv2code.com © luv2code LLC


Real-World Use Case - Master View
• In Master view, use lazy loading for search results

• Only load instructors … not their courses

www.luv2code.com © luv2code LLC


Real-World Use Case - Detail View
• In Detail view, retrieve the entity and necessary dependent entities

• Load instructor AND their courses

List of
courses

www.luv2code.com © luv2code LLC


Fetch Type
• When you define the mapping relationship

• You can specify the fetch type: EAGER or LAZY

@Entity
@Table(name="instructor")
public class Instructor {

@OneToMany(fetch=FetchType.LAZY, mappedBy=“instructor”)
private List<Course> courses;

www.luv2code.com © luv2code LLC


Default Fetch Types

Mapping Default Fetch Type

@OneToOne FetchType.EAGER

@OneToMany FetchType.LAZY

@ManyToOne FetchType.EAGER

@ManyToMany FetchType.LAZY

www.luv2code.com © luv2code LLC


Overriding Default Fetch Type
• Specifying the fetch type, overrides the defaults

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="instructor_id")
private Instructor instructor;

www.luv2code.com © luv2code LLC


More about Lazy Loading
• When you lazy load, the data is only retrieved on demand

• However, this requires an open Hibernate session

• need an connection to database to retrieve data

www.luv2code.com © luv2code LLC


More about Lazy Loading
• If the Hibernate session is closed

• And you attempt to retrieve lazy data

• Hibernate will throw an exception Watch out for this!

www.luv2code.com © luv2code LLC


Lazy: Find Courses

© luv2code LLC
Previous Solution: Eager
• Eager will retrieve everything … all of the courses for an instructor

• But we may not want this ALL of the time

• We’d like the option to load courses as needed …

Course

Course
Instructor
Course

Course

www.luv2code.com © luv2code LLC


Fetch Type
• Change the fetch type back to LAZY

@Entity
@Table(name="instructor")
public class Instructor {

@OneToMany(fetch=FetchType.LAZY, mappedBy=“instructor”)
private List<Course> courses;


FetchType for @OneToMany defaults to lazy …
} But I will explicitly list it for readability

www.luv2code.com © luv2code LLC


Add new method to find courses for instructor
File: AppDAOImpl.java

@Override
public List<Course> findCoursesByInstructorId(int theId) {

// create query
TypedQuery<Course> query = entityManager.createQuery("from Course where instructor.id = :data", Course.class);
query.setParameter("data", theId);

// execute query
List<Course> courses = query.getResultList(); Since fetch type for courses is lazy
return courses; File: CruddemoApplication.java
This will retrieve the instructor
}
private void findCoursesForInstructor(AppDAO appDAO) { WITHOUT courses
int theId = 1;

// find the instructor


Instructor tempInstructor = appDAO.findInstructorById(theId);
System.out.println("tempInstructor: " + tempInstructor);

// find courses for instructor


List<Course> courses = appDAO.findCoursesByInstructorId(theId);

// associate the objects


tempInstructor.setCourses(courses);

System.out.println("the associated courses: " + tempInstructor.getCourses());


}

www.luv2code.com © luv2code LLC


Lazy: Find Instructor with Courses

© luv2code LLC
Previous Solution: Find Courses for Instructor
• Previous solution was OK … but …

• Required an extra query

• I wish we could have a new method that would

• Get instructor AND courses … in a single query

• Also keep the LAZY option available … don’t change fetch type

www.luv2code.com © luv2code LLC


Add new method to find instructor with courses
File: AppDAOImpl.java Even with Instructor
@OneToMany(fetchType=LAZY)
@Override
public Instructor findInstructorByIdJoinFetch(int theId) {
This code will still retrieve Instructor AND Courses
// create query
TypedQuery<Instructor> query = entityManager.createQuery( The JOIN FETCH is similar to EAGER loading
"select i from Instructor i "
+ "JOIN FETCH i.courses "
+ "where i.id = :data", Instructor.class);

query.setParameter("data", theId);
File: CruddemoApplication.java
// execute query
Instructor instructor = query.getSingleResult(); private void findInstructorWithCoursesJoinFetch(AppDAO appDAO) {
return instructor; int theId = 1;
}
// find the instructor
System.out.println("Finding instructor id: " + theId);
Instructor tempInstructor = appDAO.findInstructorByIdJoinFetch(theId);
This code will still retrieve Instructor AND Courses
System.out.println("tempInstructor: " + tempInstructor);
System.out.println("the associated courses: " + tempInstructor.getCourses());

System.out.println("Done!");
}

www.luv2code.com © luv2code LLC


We have options now
• If you only need Instructor … and no courses, then call

• appDAO.findInstructorById(…)

• If you need Instructor AND courses, then call

• appDAO.findInstructorByIdJoinFetch(…)

• This gives us flexibility instead of having EAGER fetch hard-coded

www.luv2code.com © luv2code LLC


@OneToMany: Update Instructor

© luv2code LLC
Update Instructor
• Find an instructor by ID

• Change the instructor’s data by calling setter method(s)

• Update the instructor using the DAO

www.luv2code.com © luv2code LLC


Add new DAO method to update instructor
File: AppDAOImpl.java

@Override
@Transactional
public void update(Instructor tempInstructor) {
entityManager.merge(tempInstructor);
}

merge(…) will update an existing entity

www.luv2code.com © luv2code LLC


Main app
File: CruddemoApplication.java

private void updateInstructor(AppDAO appDAO) {

int theId = 1;

System.out.println("Finding instructor id: " + theId);


Instructor tempInstructor = appDAO.findInstructorById(theId);

System.out.println("Updating instructor id: " + theId);


tempInstructor.setLastName("TESTER");
Change instructor’s data
appDAO.update(tempInstructor);

System.out.println("Done");
} Call DAO method
to update database

www.luv2code.com © luv2code LLC


@OneToMany: Update Course

© luv2code LLC
Update Course
• Find a course by ID

• Change the course’s data by calling setter method(s)

• Update the course using the DAO

www.luv2code.com © luv2code LLC


Add new DAO method to update course
File: AppDAOImpl.java

@Override
@Transactional
public void update(Course tempCourse) {
entityManager.merge(tempCourse);
}

merge(…) will update an existing entity

www.luv2code.com © luv2code LLC


Main app
File: CruddemoApplication.java

private void updateCourse(AppDAO appDAO) {

int theId = 10;

System.out.println("Finding course id: " + theId);


Course tempCourse = appDAO.findCourseById(theId);

System.out.println("Updating course id: " + theId);


tempCourse.setTitle("Enjoy the Simple Things"); Change course’s data

appDAO.update(tempCourse);

System.out.println("Done");
Call DAO method
}
to update database

www.luv2code.com © luv2code LLC


@OneToMany: Delete Instructor

© luv2code LLC
Delete instructor
• Find an instructor by ID

• Break association of all instructor’s courses

• Delete the instructor

www.luv2code.com © luv2code LLC


Add new DAO method to delete instructor
File: AppDAOImpl.java

@Override
@Transactional
public void deleteInstructorById(int theId) {

// retrieve the instructor


Instructor tempInstructor = entityManager.find(Instructor.class, theId);

List<Course> courses = tempInstructor.getCourses();

// break associations of all courses for instructor


for (Course tempCourse : courses) {
tempCourse.setInstructor(null);
} Remove the instructor from the courses
// delete the instructor

}
entityManager.remove(tempInstructor);
We only delete the instructor …
not the associated course
based on our cascade types

www.luv2code.com © luv2code LLC


Error message
• If you don’t remove instructor from courses … constraint violation

Caused by: java.sql.SQLIntegrityConstraintViolationException:

Cannot delete or update a parent row: a foreign key constraint fails

(`hb-03-one-to-many`.`course`,
CONSTRAINT `FK_INSTRUCTOR` FOREIGN KEY (`instructor_id`) REFERENCES `instructor` (`id`))

• An instructor can not be deleted if it is referenced by a course

• You must remove the instructor from the course first

www.luv2code.com © luv2code LLC


Main app
File: CruddemoApplication.java

private void deleteInstructor(AppDAO appDAO) {

int theId = 1;
System.out.println("Deleting instructor id: " + theId);

appDAO.deleteInstructorById(theId);

System.out.println("Done!");
}

www.luv2code.com © luv2code LLC


@OneToMany: Delete Course

© luv2code LLC
Delete course
• Delete the course by ID

www.luv2code.com © luv2code LLC


Add new DAO method to delete course
File: AppDAOImpl.java

@Override
@Transactional
public void deleteCourseById(int theId) {

// retrieve the course


Course tempCourse = entityManager.find(Course.class, theId);

// delete the course


entityManager.remove(tempCourse);
}

www.luv2code.com © luv2code LLC


Main app
File: CruddemoApplication.java

private void deleteCourseById(AppDAO appDAO) {

int theId = 10;


System.out.println("Deleting course id: " + theId);

appDAO.deleteCourseById(theId);

System.out.println("Done!");

www.luv2code.com © luv2code LLC


@OneToMany: Uni-Directional

© luv2code LLC
One-to-Many Mapping
• A course can have many reviews

• Uni-directional

Review

Review
Course
Review

Review

www.luv2code.com © luv2code LLC


Real-World Project Requirement
• If you delete a course, also delete the reviews

• Reviews without a course … have no meaning Apply cascading


deletes!

Review

Review
Course
Review

Review

www.luv2code.com © luv2code LLC


@OneToMany

@OneToMany (uni)

www.luv2code.com © luv2code LLC


Look Mom … our project is growing!

@OneToOne

This section
is new!

@OneToMany (bi)

@OneToMany (uni)

@ManyToOne

www.luv2code.com © luv2code LLC


Development Process: One-to-Many
Step-
By-S
tep
1. Prep Work - Define database tables

2. Create Review class

3. Update Course class

www.luv2code.com © luv2code LLC


table: review
File: create-db.sql comment:
“Wow … this course is awesome!”

CREATE TABLE `review` (

`id` int(11) NOT NULL AUTO_INCREMENT,


`comment` varchar(256) DEFAULT NULL,
`course_id` int(11) DEFAULT NULL,

...
);

www.luv2code.com © luv2code LLC


table: review - foreign key
File: create-db.sql

CREATE TABLE `review` (



KEY `FK_COURSE_ID_idx` (`course_id`),
CONSTRAINT `FK_COURSE`
FOREIGN KEY (`course_id`)
REFERENCES `course` (`id`)

);
Table Column

www.luv2code.com © luv2code LLC


table: course - no changes

www.luv2code.com © luv2code LLC


Step 2: Create Review class
@Entity
@Table(name="review")
public class Review {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;

@Column(name="comment")
private String comment;


// constructors, getters / setters
}

www.luv2code.com © luv2code LLC


Step 3: Update Course - reference reviews

@Entity
@Table(name="course")
public class Course {

private List<Review> reviews;

// getter / setters

www.luv2code.com © luv2code LLC


Add @OneToMany annotation

@Entity
@Table(name="course") Refers to “course_id” column
public class Course {
in “review” table

@OneToMany
@JoinColumn(name="course_id")
private List<Review> reviews;

// getter / setters

www.luv2code.com © luv2code LLC


More: @JoinColumn public class Course {

• In this scenario, @JoinColumn tells Hibernate @OneToMany


@JoinColumn(name="course_id")
private List<Review> reviews;
• Look at the course_id column in the review table

• Use this information to help find associated reviews for a course

www.luv2code.com © luv2code LLC


Add support for Cascading
Cascade all operations
@Entity including deletes!
@Table(name="course")
public class Course {

@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="course_id")
private List<Review> reviews;

www.luv2code.com © luv2code LLC


Add support for Lazy loading
Lazy load the
@Entity
reviews
@Table(name="course")
public class Course {

@OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
@JoinColumn(name="course_id")
private List<Review> reviews;

www.luv2code.com © luv2code LLC


Add convenience method for adding review
@Entity
@Table(name="course")
public class Course {

// add convenience methods for adding reviews

public void add(Review tempReview) {

if (reviews == null) {
reviews = new ArrayList<>();
}

reviews.add(tempReview);

}

}

www.luv2code.com © luv2code LLC


@ManyToMany

© luv2code LLC
Many-to-Many Mapping
• A course can have many students

• A student can have many courses

Course Student

Course Student

Course Student

Course Student

www.luv2code.com © luv2code LLC


Keep track of relationships
• Need to track which student is in which course and vice-versa

Course Student

Course Student
Join
Course Table Student

Course Student

www.luv2code.com © luv2code LLC


Join Table

A table that provides a mapping between two tables.

It has foreign keys for each table


to define the mapping relationship.

www.luv2code.com © luv2code LLC


@ManyToMany

Join Table

www.luv2code.com © luv2code LLC


Join Table Example Find John’s Courses

- Pacman

Join Table

www.luv2code.com © luv2code LLC


Join Table Example Find Mary’s Courses

- Pacman
- Rubik’s Cube
- Atari 2600

www.luv2code.com © luv2code LLC


Development Process: Many-to-Many
1. Prep Work - Define database tables
Step-
By-S
tep
2. Update Course class

3. Update Student class

www.luv2code.com © luv2code LLC


join table: course_student
File: create-db.sql

CREATE TABLE `course_student` (

`course_id` int(11) NOT NULL,


`student_id` int(11) NOT NULL,

PRIMARY KEY (`course_id`, `student_id`),

...
);

www.luv2code.com © luv2code LLC


join table: course_student - foreign keys

CREATE TABLE `course_student` (



CONSTRAINT `FK_COURSE_05`
FOREIGN KEY (`course_id`)
REFERENCES `course` (`id`),

CONSTRAINT `FK_STUDENT`
FOREIGN KEY (`student_id`)
REFERENCES `student` (`id`)

);
Table Column

www.luv2code.com © luv2code LLC


join table: course_student - foreign keys

CREATE TABLE `course_student` (



CONSTRAINT `FK_COURSE_05`
FOREIGN KEY (`course_id`)
REFERENCES `course` (`id`),

CONSTRAINT `FK_STUDENT`
Table
FOREIGN KEY (`student_id`)
Column
REFERENCES `student` (`id`)

);

www.luv2code.com © luv2code LLC


Step 2: Update Course - reference students
@Entity
@Table(name="course")
public class Course {

private List<Student> students;

// getter / setters

www.luv2code.com © luv2code LLC


Add @ManyToMany annotation
@Entity
@Table(name="course") Refers to “course_id” column
public class Course {

in “course_student” join table

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="course_id"),
inverseJoinColumns=@JoinColumn(name="student_id")
)
private List<Student> students;

// getter / setters

www.luv2code.com © luv2code LLC


Add @ManyToMany annotation
@Entity
@Table(name="course")
public class Course {
Refers to “student_id” column
… in “course_student” join table

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="course_id"),
inverseJoinColumns=@JoinColumn(name="student_id")
)
private List<Student> students;

// getter / setters

www.luv2code.com © luv2code LLC


More: @JoinTable
• @JoinTable tells Hibernate

• Look at the course_id column in the course_student table

• For other side (inverse), look at the student_id column in the course_student table

• Use this information to find relationship between course and students

public class Course {

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="course_id"),
inverseJoinColumns=@JoinColumn(name="student_id")
)
private List<Student> students;

www.luv2code.com © luv2code LLC


More on “inverse”
• In this context, we are defining the relationship in the Course class

• The Student class is on the “other side” … so it is considered the “inverse”

• “Inverse” refers to the “other side” of the relationship

We are here Inverse /


other side

www.luv2code.com © luv2code LLC


Now, let’s do a similar thing for Student

just going the other way …

www.luv2code.com © luv2code LLC


Step 3: Update Student - reference courses
@Entity
@Table(name="student")
public class Student {

private List<Course> courses;

// getter / setters

www.luv2code.com © luv2code LLC


Add @ManyToMany annotation
@Entity
@Table(name="student") Refers to “student_id” column
public class Student { in “course_student” join table

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="course_id")
)
private List<Course> courses;

// getter / setters

www.luv2code.com © luv2code LLC


Add @ManyToMany annotation
@Entity
@Table(name="student")
public class Student { Refers to “course_id” column

in “course_student” join table

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="course_id")
)
private List<Course> courses;

// getter / setters

www.luv2code.com © luv2code LLC


More: @JoinTable
• @JoinTable tells Hibernate

• Look at the student_id column in the course_student table

• For other side (inverse), look at the course_id column in the course_student table

• Use this information to find relationship between student and courses

public class Student {

@ManyToMany
@JoinTable(
name="course_student",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="course_id")
)
private List<Course> courses;

www.luv2code.com © luv2code LLC


More on “inverse”
• In this context, we are defining the relationship in the Student class

• The Course class is on the “other side” … so it is considered the “inverse”

• “Inverse” refers to the “other side” of the relationship

Inverse /
other side We are here

www.luv2code.com © luv2code LLC


Real-World Project Requirement
• If you delete a course, DO NOT delete the students DO NOT
apply cascading
deletes!

Course Student

Course Student

Course Student

Course Student

www.luv2code.com © luv2code LLC


Other features
• In the next set of videos, we’ll add support for other features

• Lazy Loading of students and courses

• Cascading to handle cascading saves … but NOT deletes

• If we delete a course, DO NOT delete students

• If we delete a student, DO NOT delete courses

www.luv2code.com © luv2code LLC

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy