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:

  1. It creates an EntityManager instance using entityManagerFactory.createEntityManager().

  2. It creates a new Product entity instance and sets its name to "Alma".

  3. It gets an EntityTransaction instance from the EntityManager and begins a new transaction.

  4. It checks if the Product instance is in the L1 cache by using entityManager.contains(product). Since this is a new entity that hasn't been persisted yet, it will return false.

  5. It persists the Product entity using entityManager.persist(product). This makes the entity managed by the EntityManager.

  6. 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.

  7. It clears the persistence context using entityManager.clear(). This detaches all entities from the persistence context, meaning the Product entity is no longer managed by the EntityManager.

  8. It again checks if the Product instance is in the L1 cache. This time, it will return false because the Product entity has been cleared from the persistence context and is no longer managed.

  9. It commits the transaction using transaction.commit(), which finalizes the changes in the database. However, since the Product 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)

  1. Initially, you create a new Product entity and set its name to "Alma". You then persist this entity using entityManager.persist(product). At this point, Hibernate schedules an insert operation for this entity in the database, but the actual insert does not happen immediately.

  2. After persisting the entity, you check if the Product instance is in the L1 cache (i.e., managed by the EntityManager) using entityManager.contains(product). Since you just persisted it, the entity is indeed in the L1 cache, and entityManager.contains(product) returns true.

  3. You then print the Product object using System.out.println(product). This will print the details of the Product object, including its name, which is "Alma".

  4. Next, you call entityManager.clear(), which detaches all entities from the persistence context. This means that the Product entity is no longer managed by the EntityManager, and any changes to the entity will not be tracked by Hibernate.

  5. After clearing the persistence context, you set the name of the Product entity to "Changed".

  6. You then print the Product object again using System.out.println(product). This will print the details of the Product object, including its name, which is now "Changed".

  7. Finally, you commit the transaction using transaction.commit(). Since the Product 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);
}
}






















Комментарии

Популярные сообщения из этого блога

Lesson1: JDK, JVM, JRE

SE_21_Lesson_11: Inheritance, Polymorphism

SE_21_Lesson_9: Initialization Blocks, Wrapper types, String class