- 1. What is Hibernate?
- 2. What is JPA?
- 3. Entity Class
- 4. DAO - Data Access Object
- 5. Spring @Transactional
- 6. Spring @Repository
- 7. JPA Query Language (JPQL)
- 8. Create database tables
- 9. Advanced Mappings
- 10. Important Database Concepts
- 11. Entity Lifecycle
- 12. More on mappedBy
- 13. Fetch Type
- 14. @JoinTable
- 15. Common Errors
- A framework for persisting / saving Java objects in a database.
- Hibernate.
- Hibernate handles all of the low-level SQL.
- Minimizes the amount of JDBC (Java Database Connectivity) code you have to develop.
- Hibernate provides the Object-to-Relational Mapping (ORM).
- Jakarta Persistence API (JPA)... previously known as Java Persistence API.
- Standard API for Object-to-Relational-Mapping (ORM).
- Only a specification
- Defines a set of interfaces.
- Requires an implementation to be usable.
- By having a standard API, you are not locked to vendor's implementation.
- Maintain portable, flexible code by coding to JPA spec (interfaces).
- Can theoretically switch vendor implementations.
- For example, if Vendor ABC stops supporting their product.
- You could switch to Vendor XYZ without vendor lock in.
@Override
@Transactional
public void save(Book book) {
entityManager.persist(book);
}
@Override
public Book findById(int id) {
return entityManager.find(Book.class, id);
}
@Override
public List<Book> findAll() {
return entityManager.createQuery("select s FROM Book s ORDER BY s.name ASC", Book.class).getResultList();
}
@Override
public List<Book> findAll() {
return entityManager.createQuery("select s FROM Book s ORDER BY s.name ASC", Book.class).getResultList();
}
- How does Hibernate / JPA relate to JDBC?
- In Spring Boot, Hibernate is the default implementation of JPA.
- EntityManager is main component for creating queries etc...
- EntityManager is from Jakarta Persistence API (JPA).
- Based on configs, Spring Boot will automatically create the beans:
- DataSource, EntityManager, ...
- You can then inject these into your app, for example your DAO.
- At Spring Initializr website, start.spring.io
- Add dependencies
- MySQL Driver:
mysql-connector-j
- Spring Data JPA:
spring-boot-starter-data-jpa
- MySQL Driver:
- Spring Boot will automatically configure your data source for us.
- DB connection info from
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/school spring.datasource.username=root spring.datasource.password=Master@123
- No need to give JDBC driver class name Spring Boot will automatically detect it based on URL.
- Configuring a DataSource Programmatically
@Configuration public class DataSourceConfig { @Bean public DataSource getDataSource() { DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.url("jdbc:mysql://localhost:3306/library"); dataSourceBuilder.username("root"); dataSourceBuilder.password("Master@123"); return dataSourceBuilder.build(); } }
- Java class that is mapped to a database table.
- At a minimum, the Entity class.
- Must be annotated with
@Entity
- Must have a public or protected no-argument constructor.
- The class can have other constructors.
- Must be annotated with
@Entity
@Table(name = "book")
public class Book {
public Book() {
}
}
- Actually, the use of
@Column
is optional. - If not specified, the column name is the same name as Java field.
- In general, I don't recommend this approach.
- If you refactor the Java code, then it will not match existing database columns.
- This is a breaking change and you will need to update database column.
- Same applies to
@Table
, database table name is same as the class.
@Column(name = "name")
private String name;
- Uniquely identifies each row in a table.
- Must be a unique value.
- Cannot contain NULL values.
CREATE TABLE `book` (
`id` int NOT NULL AUTO_INCREMENT,
`author` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`price` float DEFAULT NULL,
PRIMARY KEY (`id`)
)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
Name | Description |
---|---|
GenerationType.AUTO |
Pick an appropriate strategy for the particular database. |
GenerationType.IDENTITY |
Assign primary keys using database identity column. |
GenerationType.SEQUENCE |
Assign primary keys using a database sequence. |
GenerationType.TABLE |
Assign primary keys using an underlying database table to ensure uniqueness. |
GenerationType.UUID |
Assign primary keys using a globally unique identifier (UUID) to ensure uniqueness. |
- You can define your own CUSTOM generation strategy.
- Create implementation of
org.hibernate.id.IdentifierGenerator
- Override the method: public Serializable generate(...)
- Responsible for interfacing with the database.
- This is a common design pattern: Data Access Object (DAO).
- Our DAO needs a JPA Entity Manager.
- JPA Entity Manager is the main component for saving/retrieving entities.
- Our JPA Entity Manager needs a Data Source.
- The Data Source defines database connection info.
- JPA Entity Manager and Data Source are automatically created by Spring Boot.
- Based on the file:
application.properties
(JDBC URL, user id, password, etc ...)
- Based on the file:
- We can autowire/inject the JPA Entity Manager into our Student DAO
- Spring Data JPA has a
JpaRepository
interface. - This provides JPA database access with minimal coding.
- In Simple Terms
- If you need low-level control and flexibility, use
EntityManager
- If you want high-level of abstraction, use
JpaRepository
- If you need low-level control and flexibility, use
Entity Manager | JPA Repository |
---|---|
Need low-level control over the database operations and want to write custom queries | Provides commonly used CRUD operations out of the box, reducing the amount of code you need to write |
Provides low-level access to JPA and work directly with JPA entities | Additional features such as pagination, sorting |
Complex queries that required advanced features such as native SQL queries or stored procedure calls | Generate queries based on method names |
When you have custom requirements that are not easily handled by higher-level abstractions | Can also create custom queries using @Query |
- Choice depends on the application requirements and developer preference.
- You can also use both in the same project.
- For learning purposes, start with
EntityManager
then learnJpaRepository
.- This will help you understand the low-level coding behind the scenes.
- Knowing BOTH
EntityManager
andJpaRepository
will help you on future projects.
- Spring provides an
@Transactional
annotation. - Automagically begin and end a transaction for your JPA code.
- No need for you to explicitly do this in your code.
- Spring provides the @Repository annotation
- Applied to DAO implementations
- Spring will automatically register the DAO implementation
- thanks to component-scanning
- Spring also provides translation of any JDBC related exceptions
- Query language for retrieving objects.
- Similar in concept to SQL.
where
,like
,order by
,join
,in
, etc...
- However, JPQL is based on entity name and entity fields.
@Override
public List<Book> findAll() {
return entityManager.createQuery("select s FROM Book s ORDER BY s.name ASC", Book.class).getResultList();
}
- Note: this is NOT the name of the database table.
- All JPQL syntax is based on entity name and entity fields
@Override
@Transactional
public int delete(int id) {
Query query = entityManager.createQuery("DELETE FROM Book WHERE id = :id");
query.setParameter("id", id);
return query.executeUpdate();
}
- JPA/Hibernate provides an option to automagically create database tables.
- Creates tables based on Java code with JPA/Hibernate annotations.
- Useful for development and testing.
- In Spring Boot configuration file:
application.properties
.spring.jpa.hibernate.ddl-auto=create
- When you run your app, JPA/Hibernate will drop tables then create them.
- Based on the JPA/Hibernate annotations in your Java code.
Property Value | Property Description |
---|---|
none |
No action will be performed. |
create-only |
Database tables are only created. |
drop |
Database tables are dropped. |
create |
Database tables are dropped followed by database tables creation. |
create-drop |
Database tables are dropped followed by database tables creation. On application shutdown, drop the database tables. |
validate |
Validate the database tables schema. |
update |
Update the database tables schema. |
- When database tables are dropped, all data is lost.
- For basic projects, can use auto configuration.
spring.jpa.hibernate.ddl-auto=create
- Database tables are dropped first and then created from scratch.
- If you want to create tables once ... and then keep data, use:
update
. - However, will ALTER database schema based on latest code updates.
- Be VERY careful here... only use for basic projects.
- Don't do this on Production databases!!!
- You don't want to drop your Production data.
- All data is deleted!!!
- Instead for Production, you should have DBAs run SQL scripts.
spring.jpa.hibernate.ddl-auto=create
- Automatic table generation is useful for.
- Database integration testing with in-memory databases.
- Basic, small hobby projects.
- In general, I don't recommend auto generation for enterprise, real-time projects.
- You can VERY easily drop PRODUCTION data if you are not careful.
- I recommend SQL scripts.
- One-to-One.
- One-to-Many, Many-to-One.
- Many-to-Many.
- Inverse / opposite of One-to-Many.
- Primary key and foreign key.
- Cascade.
- 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.
- 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.
- You can
cascade
operations. - Apply the same operation to related entities.
- If we delete an
teacher
, we should also delete theirteacher_detail
. - This is known as CASCADE DELETE.
- Cascade delete DEPENDS on the use case.
- Developer can configure cascading.
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. |
DETACH | If entity is detached (not associated w/ session), 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. |
- Eager: Will retrieve everything.
- Eager loading will load all dependent entities.
- Load
teacher
and all of theirsubjects
at once.
- Load
- Eager loading will load all dependent entities.
- Lazy: Will retrieve on request.
- When we define the mapping relationship.
- We can specify the fetch type: EAGER or LAZY.
- Example
@Entity @Table(name="teacher") public class Teacher { ... @OneToMany(fetch=FetchType.LAZY, mappedBy="teacher") private List<Subject> subjects; ... }
Mapping | Default Fetch Type |
---|---|
@OneToOne | FetchType.EAGER |
@OneToMany | FetchType.LAZY |
@ManyToOne | FetchType.EAGER |
@ManyToMany | FetchType.LAZY |
- Specifying the fetch type, overrides the defaults:
@ManyToOne(fetch=FetchType.LAZY) @JoinColumn(name="teacher_id") private Teacer teacher;
- When you lazy load, the data is only retrieved on demand.
- However, this requires an open JPA/Hibernate session.
- Need an connection to database to retrieve data.
- If the Hibernate session is closed.
- And you attempt to retrieve lazy data.
- Hibernate will throw an exception.
- Only load data when absolutely needed.
- Prefer Lazy loading instead of Eager loading.
- If we load an
TeacherDetail
.- Then we'd like to get the associated Teacher.
- Can't do this with current uni-directional relationship.
- Bi-Directional relationship is the solution.
- We can start with
TeacherDetail
and make it back to the Teacher.
Operations | Description |
---|---|
Detach | If entity is detached, it is not associated with a Hibernate session. |
Merge | If instance is detached from session, then merge will reattach to session. |
Persist | Transitions new instances to managed state. Next flush / commit will save in db. |
Remove | Transitions managed entity to be removed. Next flush / commit will delete from db. |
Refresh | Reload / synch object with data from db. Prevents stale data. |
- Search about...
-
mappedBy
tells JPA/Hibernate:- Look at the
teacherDetail
property in theTeacher
class. - Use information from the
Teacher
class@JoinColumn
. - To help find associated teacher.
- Example
public class Teacher { ... @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="teacher_detail_id") private TeacherDetail teacherDetail; ... }
- Look at the
@JoinTable
tells Hibernate:- Look at the
student_id
column in thesubject_student
table. - For other side (inverse), look at the
subject_id
column in thesubject_student
table. - Use this information to find relationship between student and subject.
- Look at the
- In this context, we are defining the relationship in the
Student
class. - The
Subject
class is on the other side ... so it is considered the inverse. - "Inverse" refers to the other side of the relationship.
- Error:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.example.real_jpa_entity_relationships.models.Teacher.teacherDetail -> com.example.real_jpa_entity_relationships.models.TeacherDetail
- Solution: Forgot
CascadeType.ALL
orCascadeType.PERSIST
.
- Solution: Forgot
- Error: Infinite recursion stackoverflow problem, this happens as it's going to convert the Java Object into a JSON Object.
- Solution
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) @JsonManagedReference private List<Subject> subjects;