Entity Lifecycle


 


1.

package com.appsdeveloperblog.ws.demo_transactional;

import com.appsdeveloperblog.ws.demo_transactional.entity.Account;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class DemoTransactionalApplication implements CommandLineRunner {

private final EntityManagerFactory emf;

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

@Override
public void run(String... args) throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// Transient mode: because it does not exist in DB
Account account = new Account();
// Managed mode: because it is in realm of Persistence Context
em.persist(account);
em.getTransaction().commit();
em.close();
}

}


2. 

package com.appsdeveloperblog.ws.demo_transactional;

import com.appsdeveloperblog.ws.demo_transactional.entity.Account;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class DemoTransactionalApplication implements CommandLineRunner {

private final EntityManagerFactory emf;

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

@Override
public void run(String... args) throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// Managed mode: because it retrieves it from DB
Account account = em.find(Account.class, 3L);
account.setBalance(100);
// Detached mode: not in realm of Persistence Context
em.remove(account);
account.setName("Parvin");
// em.persist(account);
em.getTransaction().commit();
em.close();
}

}



3. flush()

package com.appsdeveloperblog.ws.demo_transactional;

import com.appsdeveloperblog.ws.demo_transactional.entity.Account;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class DemoTransactionalApplication implements CommandLineRunner {

private final EntityManagerFactory emf;

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

@Override
public void run(String... args) throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// Managed mode: because it retrieves it from DB
Account account = em.find(Account.class, 4L);
account.setBalance(500);
// Detached mode: not in realm of Persistence Context
em.flush();
em.detach(account);
em.getTransaction().commit();
em.close();
}

}



4. *

ther is great difference between:

package com.appsdeveloperblog.ws.demo_transactional;

import com.appsdeveloperblog.ws.demo_transactional.entity.Account;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class DemoTransactionalApplication implements CommandLineRunner {

private final EntityManagerFactory emf;

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

@Override
public void run(String... args) throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Account account = em.find(Account.class, 4L);
account.setName("first");
em.flush();
em.detach(account);
em.merge(account);
account.setName("second");
em.flush();
em.getTransaction().commit();
em.close();
}

}

and

package com.appsdeveloperblog.ws.demo_transactional;

import com.appsdeveloperblog.ws.demo_transactional.entity.Account;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class DemoTransactionalApplication implements CommandLineRunner {

private final EntityManagerFactory emf;

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

@Override
public void run(String... args) throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Account account = em.find(Account.class, 4L);
account.setName("first");
em.flush();
em.detach(account);
account = em.merge(account);
account.setName("second");
em.flush();
em.getTransaction().commit();
em.close();
}

}




===========================================================================


System.out.println("=======================================================");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Account account1 = em.find(Account.class, 1L);
account1.setName("one");
em.detach(account1);
Account account2 = em.merge(account1);
System.out.println(account2);
em.persist(account2);
em.getTransaction().commit();
em.close();
System.out.println("=======================================================");


When you call em.find(Account.class, 1L), Hibernate loads the entity with id=1 from the database into the persistence context (Level 1 cache). You then modify the entity's name to "one" and detach it using em.detach(account1). Detaching removes the entity from the persistence context, but it remains in the heap memory as a regular Java object.

When you call em.merge(account1), Hibernate needs to merge the state of the detached entity back into a managed entity within the current persistence context. Here's what happens step by step:

  1. Select Query Generation: Hibernate issues a SELECT query to fetch the current state of the entity with id=1 from the database. This is necessary because the persistence context does not have any managed instance of this entity after detachment.

  2. Entity Retrieval: Hibernate retrieves the entity from the database, which still has the old name value ("x" at this point).

  3. State Merging: Hibernate then copies the state of the detached entity (account1 with name="one") onto the managed entity it just retrieved from the database.

  4. Managed Instance Creation: The result is a new managed entity (account2) in the persistence context that reflects the updated state (name="one").

When you print account2, it shows Account(id=1, name=one, balance=100) because the state from account1 (the detached entity) has been merged into it.

Why Does Hibernate Fetch from the Database?

  • Data Integrity: Fetching the current state ensures that you're working with the most recent data, accounting for any changes that might have occurred in the database since the entity was detached.

  • Versioning and Concurrency: Hibernate uses the fetched data to manage optimistic locking and versioning. This helps prevent overwriting changes made by other transactions.

  • Entity Identity: Hibernate ensures that there is only one managed instance of an entity with a given identifier within a persistence context.

Summary

  • Fetch from Database: Hibernate performs a SELECT query to retrieve the entity from the database, even though a detached instance exists in the heap.

  • State Merge: The state from the detached entity (account1) is merged into the managed entity (account2).

  • Resulting State: The managed entity reflects the updated state (name="one"), which is why printing account2 shows the updated name.

So, even though Hibernate fetches the entity from the database (hence the SELECT query), it merges the state from your detached entity into the managed entity. This is why account2 has the updated name "one", and the SELECT query is necessary for maintaining data integrity and consistency within the persistence context.

Answer:

Because when you call merge on the detached entity, Hibernate fetches the entity from the database (hence the select query), creates a managed instance (account2), and then copies the state from your detached entity (with name "one") onto it—so although it queries the database, account2 reflects your updated name because Hibernate merges the detached entity’s state into the managed entity after fetching it from the DB.



Yes, exactly. When you call em.merge(account1);, Hibernate generates a SELECT query using the identifier (id) from account1. Here's what happens step by step:

  1. Detached Entity: account1 is a detached entity after you call em.detach(account1). It still has its id property (which is 1L in this case).

  2. Merge Operation: When you call em.merge(account1);, Hibernate needs to merge the state of this detached entity back into a managed entity within the current persistence context.

  3. Check Persistence Context: Hibernate first checks if there's an entity with id=1 already managed in the current persistence context (Level 1 cache). Since you've detached account1, there isn't one.

  4. Generate SELECT Query: Because there's no managed entity with id=1, Hibernate generates a SELECT query to fetch the entity from the database using the id from account1.

    SELECT a1_0.id, a1_0.balance, a1_0.name FROM account a1_0 WHERE a1_0.id=?
    
  5. Fetch from Database: Hibernate executes the query and retrieves the current state of the entity from the database.

  6. Merge State: Hibernate then copies the state from your detached account1 (which has name="one") onto the newly fetched managed entity.

  7. Managed Instance: The result is a managed entity (account2) in the persistence context with the state from account1.

Why Does It Use account1's ID?

  • Identifier Consistency: The id property uniquely identifies the entity. Hibernate uses it to ensure it works with the correct entity.

  • Data Integrity: Fetching the entity by id ensures that any changes made to the entity in the database since it was detached are taken into account.

Conclusion

  • Yes, the SELECT query is generated using the id from account1.

  • This process ensures that Hibernate maintains consistency between the detached entity's state and the database's current state.

So, in your case, when you call em.merge(account1);, Hibernate generates a SELECT query based on account1's id to fetch the entity from the database for merging.






















































Комментарии

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

Lesson1: JDK, JVM, JRE

SE_21_Lesson_9: Initialization Blocks, Wrapper types, String class

SE_21_Lesson_11: Inheritance, Polymorphism