MS Lesson10: JPA and EntityManagerFactory

 

JPA

1. Method name query

mes: findById(1L);


2. Native query

@Query(value = "SELECT COALESCE(SUM(p.scholarship), 0) FROM persons p", nativeQuery = true)
BigDecimal sumAllScholarshipsNative();


3. @Modifying ve @Transactional neye gore lazimdir?

@Modifying
@Transactional
@Query("UPDATE Person p SET p.scholarship = p.scholarship + :amount WHERE p.id = :id")
int increaseScholarship(@Param("id") Long id, @Param("amount") BigDecimal amount);

@Modifying olmasa spring bunu select kimi anlayir, ve ona gore exception atir. Springe deyirki bu sorgu data deyishdirir. Bu annotasiya olmasa Spring select rejimde ishleyecek. 

@Transactional olmasa atomiklik ve commit ile tetbiq olunmur.

DB-de update/delete/insert transaction daxilinde olmalidir. 


Hetta native query yazsaq bele yene bize bu annotasiyalar lazimdir:

@Modifying
@Transactional
@Query(value = "UPDATE persons SET scholarship = scholarship + :amount WHERE id = :id", nativeQuery = true)
int increaseScholarshipNative(@Param("id") Long id, @Param("amount") BigDecimal amount);


EntityManagerFactory və EntityManager 

JPA / Hibernate dünyasında iki əsas oyunçu var:

  • EntityManagerFactory – “zavod”
  • EntityManager – “işçi”


1. Böyük şəkil: ApplicationContext → EntityManagerFactory → EntityManager → Connection → Database

Spring Boot tətbiqində axın təxmini belədir:

ApplicationContext
├── EntityManagerFactory (1 ədəd, singleton, bahalı obyekt)
│ │
│ └── EntityManager (çox ədəd, qısa ömürlü “session” obyektləri)
│ │
│ └── Connection (HikariCP-dən götürülən DB connection)
│ │
│ └── PostgreSQL session
└── Digər bean-lar (Service, Repository, Controller və s.)

Yadda saxla:

  • EntityManagerFactory – tətbiq boyu 1 dəfə yaradılır (ApplicationContext kimi).
  • EntityManager – hər transaction / iş üçün ayrıca yaradılıb bağlanan “session” kimidir.
  • Connection – Hikari hovuzundan götürülür, DB ilə TCP kanalıdır.

2. EntityManagerFactory nədir?

Bunu belə təsəvvür et:

EntityManagerFactory = Hibernate üçün “zavod” + metadata cache.

Server start olanda:

  1. JPA konfiqurasiyası oxunur (spring.jpa.*persistence-unit və s.)
  2. Bütün @Entity sinifləri (StudentCourse və s.) scan olunur.
  3. Hər entity üçün:
    • cədvəl adı
    • sütunlar
    • əlaqələr (OneToMany, ManyToOne və s.)
    • ID strategiyası
    • və s. metadata hazırlanır.
  4. Dialect (məs. PostgreSQLDialect) seçilir.
  5. Nəticədə 1 dənə EntityManagerFactory obyekt-i yaranır.

Logda bunu görürsən:

Initialized JPA EntityManagerFactory for persistence unit 'default'

Bu obyekt:

  • bahalıdır (çox iş görür)
  • amma nadirdir (tətbiq boyu 1 dənə)


3. EntityManager nədir?

EntityManager = Hibernate Session = Entity-lərlə işləyən “işçi” obyekt.

Onun işi:

  • find()persist()merge()remove() kimi əməliyyatlarla entity-lərlə işləmək
  • 1 transaction ərzində entity-ləri izləmək (dirty checking)
  • lazy loading etmək
  • cache / identity map saxlamaq (eyni entity-ni 2 dəfə DB-dən oxumamaq və s.)

Daha sadə:

  • EntityManagerFactory = planları bilən zavod
  • EntityManager = həmin plan əsasında real obyektlərlə işləyən ustalar (session-lar)

4. Spring Boot-da bunlar necə yaranır?

Spring Boot JPA starter (spring-boot-starter-data-jpa) sayəsində avtomatik:

  • DataSource (HikariCP)
  • EntityManagerFactory
  • PlatformTransactionManager (JPA transaction manager)
  • JPA repository-lər üçün proxy-lər


5. Əməliyyat zamanı nə baş verir? – Diaqramla

Tutaq ki, bu kod var:

@GetMapping("/students/{id}")
public StudentResponseDto getStudent(@PathVariable Long id) {
return studentService.findById(id);
}

findById içində də:

@Override
public StudentResponseDto findById(Long id) {
Student student = repository.findById(id).orElseThrow();
return mapToDto(student);
}

5.1. Transaction olan ssenari (@Transactional service-də)

@Service
public class StudentServiceImpl implements StudentService {
private final StudentRepository repository;
@Transactional(readOnly = true)
public StudentResponseDto findById(Long id) {
Student s = repository.findById(id).orElseThrow();
return mapToDto(s);
}
}

Daxildə baş verənlər (sadələşdirilmiş):

1) Controller → service.findById(id) çağırır.
2) Spring TransactionInterceptor:
- EntityManagerFactory-dən yeni EntityManager yaradır
- Hikari-dən Connection götürür
- DB-də BEGIN TRANSACTION edir
- EntityManager-i cari thread-ə “bağlayır”
3) repository.findById(id):
- JPA proxy-i həmin EntityManager-i istifadə edir
- SELECT ... FROM student WHERE id = ?
- Nəticəni Student entity obyektinə map edir
- EntityManager bu obyekt-i "izləyir" (managed state)
4) Service mapToDto çağırır, işini bitir.
5) TransactionInterceptor:
- COMMIT (və ya xəta varsa ROLLBACK)
- EntityManager-i bağlayır
- Connection-u hovuz-a qaytarır

Burada bütün əməliyyatlar eyni EntityManager və eyni Connection ilə gedir.


5.2. Transaction OLMADAN, open-in-view=false

Tutaq ki, service-də heç @Transactional yoxdur, open-in-view də false-dur.

public StudentResponseDto findById(Long id) {
Student s = repository.findById(id).orElseThrow();
return mapToDto(s);
}

Bu halda:

1) Controller → service.findById(id)
2) repository.findById(id) çağırılır:
- Spring data, JPA proxy vasitəsilə:
- EntityManagerFactory-dən (lazım olsa) EntityManager yaradır
- DBConnection götürür
- DB-də BEGIN (implicit və ya qısa transaction)
- SELECT ...
- COMMIT
- EntityManager-i bağlayır
- Connection-u pool-a qaytarır
3) Service artıq bağlı EntityManager/Connection-larla qalmayıb.
4) Əgər buradan sonra LAZY load etsən (məs. `student.getCourses().size()`),
LazyInitializationException ala bilərsən, çünki session yoxdur.

Burada hər repository çağırışı öz kiçik EntityManager ömrünə sahib ola bilər.

6. “Bir əməliyyatda neçə EntityManager yaranır?” – Real sual

  • Service-də @Transactional varsa:
    • Adətən bir transaction = bir EntityManager = bir Connection (HTTP request-in həmin hissəsi üçün).
  • @Transactional yoxdursa, open-in-view=false isə:
    • Hər repository metodu üçün ayrıca qısa ömürlü EntityManager yarana bilər.
    • Hər sorğu öz implicit / auto-commit transaction-u ilə işləyir.

Ən sağlam pattern:
Business əməliyyatlarını (məs. createStudenttransferMoneyplaceOrderservice layer-də @Transactional ilə idarə etmək → 1 use-case = 1 transaction = 1 EntityManager.


7. EntityManagerFactory / EntityManager / Connection – kodla təsəvvür

Blogda göstərmək üçün belə bir pseudo-kod yerləşdirə bilərsən:

// Tətbiq start edəndə (ApplicationContext qurulanda)
DataSource dataSource = createHikariDataSource(); // Hikari hovuzu
EntityManagerFactory emf = createEntityManagerFactory(dataSource); // metadata + zavod
// Hər @Transactional metod çağırışında:
EntityManager em = emf.createEntityManager(); // session
Connection conn = dataSource.getConnection(); // Hikari-dən
em.joinTransaction(); // EntityManager ↑ bu connection ilə işləyir
try {
// buradan sonra JPA əməliyyatları:
Student s = em.find(Student.class, id); // SELECT ...
...
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close(); // session bağlanır
conn.close(); // əslində Hikari hovuzuna qayıdır
}

Real Spring-də bu kodu sən yazmırsan, Spring + Hibernate + Hikari birlikdə icra edir, amma məntiq budur.


8. Diaqram – blog üçün gözəl görünən forma

Blogda belə bir ASCII / şəkil diaqramı verə bilərsən:

╔════════════════════════════════════════════════════════════╗
║ APPLICATION STARTS (Spring Boot) ║
╚════════════════════════════════════════════════════════════╝
Create ApplicationContext
Create EntityManagerFactory (singleton bean)
Create Hikari DataSource (connection pool)
Ready to handle requests
╔════════════════════════════════════════════════════════════╗
║ ON EACH @Transactional SERVICE METHOD ║
╚════════════════════════════════════════════════════════════╝
EntityManager em = emf.createEntityManager()
Connection conn = hikari.getConnection()
BEGIN TRANSACTION (DB level)
JPA operations via EntityManager (SELECT/INSERT/UPDATE/DELETE)
COMMIT / ROLLBACK
├─► em.close()
└─► conn.close() → back to Hikari pool




Entity Manager-in hell etdiyi problemlerden biri:

package guru.springframework.cruddemo;

import guru.springframework.cruddemo.entity.Person;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootApplication
@RequiredArgsConstructor
public class CrudDemoApplication implements CommandLineRunner {

private final EntityManager entityManager;

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


@Override
@Transactional(readOnly = true)
public void run(String... args) throws Exception {
String nameContains = "a";
BigDecimal minScholarship = new BigDecimal("500");
BigDecimal maxScholarship = new BigDecimal("1500");

StringBuilder jpql = new StringBuilder("SELECT p FROM Person p WHERE 1=1");
Map<String, Object> params = new HashMap<>();

if (nameContains != null && !nameContains.isBlank()) {
jpql.append(" AND LOWER(p.name) LIKE LOWER(:name)");
params.put("name", "%" + nameContains + "%");
}
if (minScholarship != null) {
jpql.append(" AND p.scholarship >= :minScholarship");
params.put("minScholarship", minScholarship);
}
if (maxScholarship != null) {
jpql.append(" AND p.scholarship <= :maxScholarship");
params.put("maxScholarship", maxScholarship);
}
jpql.append(" ORDER BY p.createdAt DESC");

TypedQuery<Person> query = entityManager.createQuery(jpql.toString(), Person.class);
params.forEach(query::setParameter);

List<Person> people = query.setMaxResults(10).getResultList();
people.forEach(System.out::println);

}

}












































Комментарии

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

Interview questions

Lesson1: JDK, JVM, JRE

Lesson_2: Operations in Java