Spring - Cascade Types
package az.etibarli.cascadetypes.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Data;
@Data
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String city;
private String state;
private String zipCode;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
}
package az.etibarli.cascadetypes.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.Data;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Data
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "customer")
private Set<Address> addresses = new LinkedHashSet<>();
}
package az.etibarli.cascadetypes;
import az.etibarli.cascadetypes.entity.Address;
import az.etibarli.cascadetypes.entity.Customer;
import az.etibarli.cascadetypes.repository.CustomerRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Set;
@SpringBootApplication
@RequiredArgsConstructor
public class CascadeTypesApplication implements CommandLineRunner {
private final CustomerRepository customerRepository;
public static void main(String[] args) {
SpringApplication.run(CascadeTypesApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Customer parvin = new Customer();
parvin.setName("Parvin");
Address kubinka = new Address();
kubinka.setCity("Baku");
kubinka.setState("Azerbaijan");
kubinka.setZipCode("AZ1014");
kubinka.setCustomer(parvin);
Address mkr = new Address();
mkr.setCity("Baku");
mkr.setState("Azerbaijan");
mkr.setZipCode("AZ1017");
mkr.setCustomer(parvin);
parvin.setAddresses(Set.of(kubinka, mkr));
customerRepository.save(parvin);
}
}
Burada xeta bash verir. Xetanin sebebi ise equals() ve hashCode() duzgun override olunmayib.
Hibernate o anda sənin obyekt qrafını aktiv şəkildə emal edir və bu prosesdə equals/hashCode
çağırışları tetiklənir. Səndə isə @Data
bütün sahələr üzrə hashCode
yaradır → bidirectional əlaqədə dövri zəncir yaranır → sonsuz rekursiya → StackOverflowError
.
1. Persist = save operation: o deməkdir ki, bir obyekti DB-yə “yeni kimi” saxlayanda (persist
/save
), onun bağlı (uşaq) obyektləri də avtomatik “yeni kimi” saxlanılsın.
2. Merge = update operation: o deməkdir ki, bir “ana” entity-ni merge
(yəni yenilə) edəndə, ona bağlı “uşaq” entity-lərin də dəyişiklikləri eyni əməliyyatla DB-yə tətbiq olunsun.
3. Remove = delete operation: Valideyni (parent) siləndə, bu əlaqə ilə görünən bütün uşaqları (children) da sil.
4. Refresh: obyektin RAM-dakı (Persistence Context-dəki) dəyərlərini atıb, DB-dən yenidən oxuyur. Yəni “sıfırla və bazadakı real vəziyyəti götür.
Misal:
package az.etibarli.cascadetypes;
import az.etibarli.cascadetypes.entity.Address;
import az.etibarli.cascadetypes.entity.Customer;
import az.etibarli.cascadetypes.repository.CustomerRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
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.util.LinkedHashSet;
@SpringBootApplication
@RequiredArgsConstructor
public class CascadeTypesApplication implements CommandLineRunner {
private final CustomerRepository customerRepository;
@PersistenceContext
private EntityManager em;
public static void main(String[] args) {
SpringApplication.run(CascadeTypesApplication.class, args);
}
@Override
@Transactional
public void run(String... args) throws Exception {
// 1) DB-yə ilkin yazma
Customer c = new Customer();
c.setName("DB_VALUE");
Address a1 = new Address();
a1.setCity("BAKU_DB");
a1.setCustomer(c);
Address a2 = new Address();
a2.setCity("GANJA_DB");
a2.setCustomer(c);
c.setAddresses(new LinkedHashSet<>());
c.getAddresses().add(a1);
c.getAddresses().add(a2);
em.persist(c);
em.flush(); // id-lər yaransın
// 2) RAM-da dəyişiklik (hələ DB-yə yazmamışıq)
c.setName("RAM_ONLY");
a1.setCity("RAM_CITY");
System.out.println("Before refresh -> c.name : " + c.getName()); // RAM_ONLY
System.out.println("Before refresh -> a1.city : " + a1.getCity()); // RAM_CITY
// 3) Yalnız parent-i refresh edirik (cascade=REFRESH YOXDUR)
em.refresh(c);
System.out.println("After refresh(parent) -> c.name : " + c.getName()); // DB_VALUE
System.out.println("After refresh(parent) -> a1.city : " + a1.getCity()); // HƏLƏ də RAM_CITY!
// 4) İndi uşaq üçün ayrıca refresh
em.refresh(a1);
System.out.println("After refresh(a1) -> a1.city : " + a1.getCity()); // BAKU_DB
// 5) “Kənardan” dəyişiklik: JPQL BULK UPDATE (native yerinə)
int updated = em.createQuery(
"update Address a set a.city = :city where a.id = :id")
.setParameter("city", "UPDATED_IN_DB")
.setParameter("id", a1.getId())
.executeUpdate();
System.out.println("Rows updated: " + updated);
// bulk update Persistence Context-i avtomatik yeniləmir:
System.out.println("Before refresh(a1) after bulk -> a1.city : " + a1.getCity()); // hələ köhnə RAM dəyəri
// 6) Uşağı yenidən refresh edirik ki, DB-dən ən son dəyər gəlsin
em.refresh(a1);
System.out.println("After refresh(a1) -> a1.city : " + a1.getCity()); // UPDATED_IN_DB
}
}
5. Detach:
package az.etibarli.cascadetypes;
import az.etibarli.cascadetypes.entity.Address;
import az.etibarli.cascadetypes.entity.Customer;
import az.etibarli.cascadetypes.repository.CustomerRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
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.util.LinkedHashSet;
@SpringBootApplication
@RequiredArgsConstructor
public class CascadeTypesApplication implements CommandLineRunner {
private final CustomerRepository customerRepository;
@PersistenceContext
private EntityManager em;
public static void main(String[] args) {
SpringApplication.run(CascadeTypesApplication.class, args);
}
@Override
@Transactional
public void run(String... args) throws Exception {
Customer parent = new Customer();
parent.setName("Parvin");
Address a = new Address(); a.setCity("Baku"); a.setCustomer(parent);
Address b = new Address(); b.setCity("Ganja"); b.setCustomer(parent);
parent.setAddresses(new LinkedHashSet<>());
parent.getAddresses().add(a);
parent.getAddresses().add(b);
// persist → hamısı managed
em.persist(parent);
print("after persist ", parent, a, b); // true, true, true
// yalnız parent-i detach edirik (cascade=DETACH YOXDUR → uşaqlar managed qalır)
em.detach(parent);
print("after detach P ", parent, a, b); // false, true, true
// indi child 'a'-nı detach edirik
em.detach(a);
print("after detach A ", parent, a, b); // false, false, true
// bütün konteksti təmizləyək
em.clear();
print("after clear() ", parent, a, b); // false, false, false
}
private void print(String stage, Customer p, Address a, Address b) {
System.out.printf("[%s] contains(parent)=%s, contains(a)=%s, contains(b)=%s%n",
stage, em.contains(p), em.contains(a), em.contains(b));
}
}
https://www.youtube.com/watch?v=LpALF2fVd7A
Комментарии
Отправить комментарий