Hibernate. Entity Lifecycle, EntityManagerFactory, EntityManager
1.Session - is a pipe that connects java application to database.
3 stages in life cycle:
1 - Transient
2 - Persistent
3 - Detached
As soon as we create an object it is in Transient state.
Transient state - Object Data is not yet inserted into the database.
package az.etibarli.jpa_relationships.service;
import az.etibarli.jpa_relationships.repository.ProductRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.PersistenceUnit;
import org.springframework.stereotype.Component;
@Component
public class EntityMangerTestComponent {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public void foo() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
System.out.println("EntityManger itself: " + entityManager);
System.out.println("EntityMangerImpl: " + entityManager.getDelegate());
}
}
package az.etibarli.jpa_relationships.controller;
import az.etibarli.jpa_relationships.service.EntityMangerTestComponent;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class EntityMangerTestController {
private final EntityMangerTestComponent component;
@PostMapping
public void test() {
component.foo();
}
}
Burada Postman-la sorgu atdiqda her defe yeni EntityManger yaradir ve hikary pool-dan bir thread goturub bazaya qowulur.
L1 Cache:
package az.etibarli.jpa_relationships.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// private Integer quantity;
// @OneToOne(orphanRemoval = true)
// @JoinColumn(name = "product_detail_id")
// private ProductDetail productDetail;
}
package az.etibarli.jpa_relationships.service;
import az.etibarli.jpa_relationships.entity.Product;
import az.etibarli.jpa_relationships.repository.ProductRepository;
import jakarta.persistence.*;
import org.springframework.stereotype.Component;
@Component
public class EntityMangerTestComponent {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public void foo() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
System.out.println("EntityManger itself: " + entityManager);
System.out.println("EntityMangerImpl: " + entityManager.getDelegate());
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
entityManager.persist(product);
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
transaction.commit();
}
}
---
package az.etibarli.jpa_relationships.service;
import az.etibarli.jpa_relationships.entity.Product;
import az.etibarli.jpa_relationships.repository.ProductRepository;
import jakarta.persistence.*;
import org.springframework.stereotype.Component;
@Component
public class EntityMangerTestComponent {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public void foo() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
entityManager.persist(product);
entityManager.flush();
product.setName("Armud");
entityManager.persist(product);
entityManager.flush();
product.setName("Gavali");
entityManager.persist(product);
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
transaction.commit();
}
}
Product is in L1 Cache: false
Hibernate: insert into product (name) values (?)
Hibernate: update product set name=? where id=?
Product is in L1 Cache: true
Hibernate: update product set name=? where id=?
---
public void foo2() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
entityManager.persist(product);
entityManager.flush();
entityManager.clear();
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
transaction.commit();
}
In this code snippet, the foo2()
method demonstrates using JPA EntityManager to interact with the database. Here's a step-by-step explanation of what happens:
It creates an
EntityManager
instance usingentityManagerFactory.createEntityManager()
.It creates a new
Product
entity instance and sets its name to "Alma".It gets an
EntityTransaction
instance from theEntityManager
and begins a new transaction.It checks if the
Product
instance is in the L1 cache by usingentityManager.contains(product)
. Since this is a new entity that hasn't been persisted yet, it will returnfalse
.It persists the
Product
entity usingentityManager.persist(product)
. This makes the entity managed by theEntityManager
.It flushes the changes to the database using
entityManager.flush()
. This forces the insert operation to be executed immediately in the database, even before the transaction is committed.It clears the persistence context using
entityManager.clear()
. This detaches all entities from the persistence context, meaning theProduct
entity is no longer managed by theEntityManager
.It again checks if the
Product
instance is in the L1 cache. This time, it will returnfalse
because theProduct
entity has been cleared from the persistence context and is no longer managed.It commits the transaction using
transaction.commit()
, which finalizes the changes in the database. However, since theProduct
entity was detached from the persistence context before committing, no additional changes will be made to the database during the commit.
In JPA, the L1 (first-level) cache, managed by the EntityManager
, is used to track the state of entities and manage their lifecycle. When you call entityManager.clear()
, you are clearing the L1 cache, which detaches all managed entities from the persistence context.
However, clearing the L1 cache does not affect the changes that have been queued for synchronization with the database. When you call entityManager.persist(product)
and entityManager.flush()
, Hibernate queues an insert operation for the Product
entity. Even after clearing the L1 cache, this insert operation is still queued and will be executed when you commit the transaction with transaction.commit()
.
In summary, clearing the L1 cache does not prevent queued database operations from being executed when the transaction is committed.
In the context of Hibernate and JPA, "queued" refers to operations that have been scheduled to be executed against the database but have not yet been executed. When you interact with entities managed by the EntityManager
(such as calling persist()
, merge()
, remove()
, etc.), Hibernate doesn't immediately execute corresponding SQL statements against the database. Instead, it queues these operations for later execution.
For example, when you call entityManager.persist(product)
to persist a new Product
entity, Hibernate queues an insert operation for this entity. Similarly, when you call entityManager.remove(product)
to delete an entity, Hibernate queues a delete operation.
These queued operations are then executed when certain conditions are met, such as when you explicitly call entityManager.flush()
to synchronize the changes with the database, or when you commit the transaction. This queuing mechanism allows Hibernate to optimize database operations and batch them together for better performance.
---
package az.etibarli.jpa_relationships.service;
import az.etibarli.jpa_relationships.entity.Product;
import az.etibarli.jpa_relationships.repository.ProductRepository;
import jakarta.persistence.*;
import org.springframework.stereotype.Component;
@Component
public class EntityMangerTestComponent {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public void foo() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
System.out.println(product);
entityManager.persist(product);
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
System.out.println(product);
// transaction.commit();
}
public void foo2() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Product loadedProduct = entityManager.find(Product.class, 10L);
entityManager.clear();
System.out.println("Loaded Product is in L1 Cache: " + entityManager.contains(loadedProduct));
System.out.println("Product is in L1 Cache: " + entityManager.contains(product));
transaction.commit();
System.out.println("Loaded Product is in L1 Cache: " + entityManager.contains(loadedProduct));
}
}
Product is in L1 Cache: false
Product(id=null, name=Alma)
Hibernate: insert into product (name) values (?)
Product is in L1 Cache: true
Product(id=12, name=Alma)
---
public void foo2() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Product product = new Product();
product.setName("Alma");
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(product);
System.out.println("After persist");
System.out.println(entityManager.contains(product));
System.out.println(product);
entityManager.clear();
product.setName("Changed");
System.out.println("After clear");
System.out.println(entityManager.contains(product));
System.out.println(product);
transaction.commit();
}
After persist
true
Product(id=14, name=Alma)
After clear
false
Product(id=14, name=Changed)
Initially, you create a new
Product
entity and set its name to "Alma". You then persist this entity usingentityManager.persist(product)
. At this point, Hibernate schedules an insert operation for this entity in the database, but the actual insert does not happen immediately.After persisting the entity, you check if the
Product
instance is in the L1 cache (i.e., managed by theEntityManager
) usingentityManager.contains(product)
. Since you just persisted it, the entity is indeed in the L1 cache, andentityManager.contains(product)
returnstrue
.You then print the
Product
object usingSystem.out.println(product)
. This will print the details of theProduct
object, including its name, which is "Alma".Next, you call
entityManager.clear()
, which detaches all entities from the persistence context. This means that theProduct
entity is no longer managed by theEntityManager
, and any changes to the entity will not be tracked by Hibernate.After clearing the persistence context, you set the name of the
Product
entity to "Changed".You then print the
Product
object again usingSystem.out.println(product)
. This will print the details of theProduct
object, including its name, which is now "Changed".Finally, you commit the transaction using
transaction.commit()
. Since theProduct
entity was detached from the persistence context before committing, only the state of the entity at the time of detachment (i.e., with the name "Alma") will be considered for insertion into the database.
Therefore, the name "Alma" will be saved to the database, regardless of the subsequent change to the name of the Product
entity to "Changed" after clearing the persistence context.
Hibernate typically does not save detached objects. However, in this specific scenario, the behavior is a bit different due to the sequence of operations and the use of entityManager.clear()
.
When you call entityManager.clear()
, you are detaching all entities from the persistence context, which means Hibernate will no longer track changes to those entities. However, any changes made to the detached entities after clearing the persistence context will not be considered for persistence.
In your code, the Product
entity is detached after calling entityManager.clear()
, but you change its name to "Changed" after detaching it. When you commit the transaction, Hibernate only considers the state of the Product
entity at the time it was detached (i.e., with the name "Alma"). Therefore, Hibernate will save the Product
entity with the name "Alma" to the database, even though it is detached at the time of committing the transaction.
So, while Hibernate typically does not save detached objects, in this specific scenario, the detached object's state at the time of detachment is still considered for persistence because the detachment occurs before committing the transaction.
---
public void foo2() {
Product product = new Product();
product.setName("Parvin");
product.setId(17L);
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// Product product = entityManager.find(Product.class, 17L);
System.out.println(entityManager.contains(product));
entityManager.persist(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
org.hibernate.PersistentObjectException: detached entity passed to persist: az.etibarli.jpa_relationships.entity.Product
Lakin:
public void foo2() {
Product product = new Product();
product.setName("Parvin");
product.setId(17L);
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// Product product = entityManager.find(Product.class, 17L);
System.out.println(entityManager.contains(product));
entityManager.merge(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
bu halda ise bazada update gedecek
Lakin:
public void foo2() {
Product product = new Product();
product.setName("Parvin");
product.setId(20L);
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
// Product product = entityManager.find(Product.class, 17L);
System.out.println(entityManager.contains(product));
entityManager.merge(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
bu halda ise bazada insert gedecek lakin id 20 olmayacaq, cunki bazada sonuncu id 17 idi.
---
public void foo2() {
Product product = new Product();
product.setName("x");
product.setId(25L);
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
System.out.println(entityManager.contains(product));
// Product product = entityManager.find(Product.class, 17L);
entityManager.merge(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
false
Hibernate: select p1_0.id,p1_0.name from product p1_0 where p1_0.id=?
Hibernate: insert into product (name) values (?)
false
--- bu here:
public void foo2() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Product product = entityManager.find(Product.class, 17L);
System.out.println(entityManager.contains(product));
// Product product = entityManager.find(Product.class, 17L);
entityManager.merge(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
Hibernate: select p1_0.id,p1_0.name from product p1_0 where p1_0.id=?
true
true
--- and finally:
public void foo2() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
Product product = entityManager.find(Product.class, 17L);
product.setName("test");
System.out.println(entityManager.contains(product));
// Product product = entityManager.find(Product.class, 17L);
entityManager.merge(product);
System.out.println(entityManager.contains(product));
transaction.commit();
}
Hibernate: select p1_0.id,p1_0.name from product p1_0 where p1_0.id=?
true
true
Hibernate: update product set name=? where id=?
---
package az.etibarli.jpa_relationships.service;
import az.etibarli.jpa_relationships.entity.Product;
import az.etibarli.jpa_relationships.repository.ProductRepository;
import jakarta.persistence.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class EntityMangerTestComponent {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
private final ProductRepository repository;
public void foo() {
Product product = new Product();
product.setName("Kitab");
product.setId(21L);
repository.save(product);
}
}
arxa fonda bu iwlenir:
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (this.entityInformation.isNew(entity)) {
this.entityManager.persist(entity);
return entity;
} else {
return this.entityManager.merge(entity);
}
}
Комментарии
Отправить комментарий