November 22, 2016

Persistence Context

Persistence Context ဟာ Coding ရေးတဲ့ နေရာမှာ တစ်နေရာမှာမှ ပေါ်လာမှာ မဟုတ်ပေမဲ့ သဘောတရားကို နားမလည်ထားရင် JPA ကို လေ့လာတဲ့ နေရာမှာ အတော်လေးကို အခက်တွေ့နိုင်ပါတယ်။

Persistence Context ဆိုတာဟာ EntityManager တစ်ခုချင်းစီရဲ့ နောက်ကွယ်မှာ ရှိတဲ့ Cache Area တစ်ခုဖြစ်ပါတယ်။ EntityManager Object ကို တည်ဆောက်လိုက်တဲ့ အခါ နောက်ကွယ်မှာ Persistence Context Object ကိုလဲ တည်ဆောက်သွားမှာ ဖြစ်ပါတယ်။



ဥပမာအားဖြင့် Entity Object တစ်ခုကို Persist လုပ်မယ်ဆိုရင် အဲ့ဒီ Entity Object ကို Persistence Context ထဲကို ထည့်ပြီး Transaction ကို Commit လုပ်လိုက်တဲ့ အခါမှာ Database နှင့် Synchronise လုပ်ပေးမှာ ဖြစ်ပါတယ်။

EntityManager ကို သုံးပြီး Entity လေးရဲ့ Lifecycle ကို Manage လုပ်တယ်ဆိုတာက Entity Object ကို Persistence Context ထဲကို အသွင်း အထုတ်လုပ်နေတယ်လို့လဲ ပြောလို့ရပါတယ်။

Entity Object လေးဟာ Persistence Context ထဲမှာ ရှိနေတယ်ဆိုရင် Managed State မှာ ရှိနေတယ်လို့ ဆိုနိုင်ပါတယ်။ Managed State မှာ ရှိနေတယ်ဆီုရင် Database နဲ့ တိုက်ရိုက် ဆက်သွယ်နေတယ်လို့လဲ မြင်နိုင်ပါတယ်။




Managed State ထဲမှာ ရှိနေတဲ့ Entity Object တစ်ခုရဲ့ Property တစ်ခုခုကို ပြောင်းလိုက်မယ်ဆိုရင် Database ထဲမှာလဲ Update လုပ်ပေးပါမယ်။ တဖန် Lazy mode နဲ Fetch လုပ်မည့် Property တန်ဖိုးတွေကိုလဲ Manage State ထဲမှာ ရှိနေတုန်း get လုပ်မယ်ဆိုရင် Database ထဲကနေရှာပေးနိုင်ပါတယ်။

တဖန် EntityManager ကို သုံးပြီး Entity Object ကို Detach State ကို ရောက်အောင် ပြုလုပ်နိုင်ပါတယ်။ Detach State ကို ရောက်သွားတယ်ဆိုတာ Entity Object ဟာ Persistence Context ထဲကနေ ထုတ်ပစ်လိုက်တာပဲ။ အဲ့ဒီအတွက် Entity Object ဟာ Database နဲ့ ဆက်သွယ်မှု့ရှိမှာ မဟုတ်တော့ပါဘူး။ အဲ့ဒီအချိန်မှာ Lazy Fetch Mode ဖြစ်တဲ့ Property တွေကို Get လုပ်ရင် LazyInitializationException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်တယ်။ တဖန် Detach State မှာ ရောက်နေတဲ့ Entity Object ရဲ့ တန်ဖိုးတွေကို ပြောင်းမယ်ဆိုရင်လဲ Database ထဲကို Update လုပ်ပေးနိုင်မှာ မဟုတ်ပါဘူး။

Detach State ကို ရောက်နေတဲ့ Entity Object ကို Managed State ကို ပြန်ရောက်စေချင်ရင် EntityManager ရဲ့ merge method ကို အသုံးပြုနိုင်ပါတယ်။ Detach State ကို ရောက်နေတဲ့ Entity Object ကို Persistence Context ထဲကို ပြန်ထည့်ပြီး Database နဲ့ Synchronise လုပ်ပေးမှာ ဖြစ်ပါတယ်။ ပြုပြင်ထားတဲ့ Property တွေရှိမယ်ဆိုရင်လဲ Database ထဲကို Update လုပ်ပေးမှာ ဖြစ်ပါတယ်။

EntityManager ရဲ့ remove method ကို သုံးပြီး Entity လေးကို Remove လုပ်မယ်ဆိုရင်လဲ Entity ကို Persistence Context ထဲကနေ ထုတ်ပစ်ပြီး Transaction ကို Commit လုပ်လိုက်တဲ့ အခါမှာ Database ထဲကနေ Delete လုပ်ပစ်မှာ ဖြစ်ပါတယ်။

ဒါကြောင့် JPA ကို အသုံးပြုတဲ့အခါမှာ Entity Object လေးဟာ ဘယ်လို State မျိုးကို ရောက်ရှိနေတယ်ဆိုတာကို အမြဲ သတိထားဖို့လိုအပ်ပါတယ်။


Managing Entity

Entity Object တစ်ခုချင်းစီကို Manage လုပ်မယ်ဆိုရင် EntityManager ကို အသုံးပြုရပါမယ်။ EntityManager မှာ Entity Object တွေကို Handle လုပ်ဖို့အတွက် Method တွေကို ပြင်ဆင်ထားပါတယ်။

Method Description
void persist(Object entity) Entity Object ကို Managed State ကို ရောက်အောင်လုပ်ပြီး Database ထဲကို Insert လုပ်ပေးပါမယ်
<T> T find(Class<T> type, Object id) Database ထဲကနေ Primary Key ကို သုံးပြီး ရှာပေးပါမယ်။ ရရှိလာတဲ့ Entity Object ဟာလဲ Managed State ကို ရောက်နေမှာ ဖြစ်ပါတယ်
<T> T getReference(Class<T> type, Object id) Entity Object လေးကို Reference လုပ်နိုင်အောင် ဆောင်ရွက်ပေးမှာ ဖြစ်ပေမဲ့၊ Property တွေကတော့ Lazy Fetching ပုံစံဖြစ်နေနိုင်ပါတယ်
void remove(Object entity) Entity Object ကို Persistance Context ထဲကနေ Remove လုပ်ပစ်ပြီး Database ထဲကနေလဲ Delete လုပ်ပစ်မှာ ဖြစ်ပါတယ်
<T> T merge(T t) Entity Object ရဲ့ State တွေကို လက်ရှိ Persistence Context ထဲကို Merge လုပ်ပေးမှာဖြစ်တယ်။ ရလဒ်အနေနဲ့ Database ကို လဲ Update လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်
void refresh(Object entity) Database ထဲက တန်ဖိုးတွေနဲ့ Entity Object လေးရဲ့ State တွေကို ပြန်ပြီး Update လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်
void flush() Persistence Context ထဲမှာ ရှိတဲ့ Entity Object တွေရဲ့ State တွေအားလုံးကို Database ထဲကို Synchronise လုပ်ပေးနိုင်မှာ ဖြစ်တယ်
void clear() Persistence Context ထဲမှာ ရှိတဲ့ Entity Object တွေကို Clear လုပ်ပစ်မှာ ဖြစ်ပြီး၊ Entity Object တွေအားလုံး Detach State ကို ရောက်ရှိကုန်မှာ ဖြစ်ပါတယ်
void detach(Object entity) Parameter ဖြစ်တဲ့ Entity Object ကို Persistence Context ထဲကနေ ထုတ်ပယ်ပစ်ပြီး Detach State ကို ရောက်ရှိသွားမဟာ ဖြစ်ပါတယ်
boolean contains(Object entity) Parameter ဖြစ်တဲ့ Entity Object ဟာ Persistence Context မှာ ရှိသလားမရှိဘူးလား ဆိုတာကို စမ်းသပ်နိုင်တဲ့ Method ဖြစ်ပါတယ်။ Persistence Context ထဲမှာ ရှိနေတယ်ဆိုရင် true ကို ပြန်မှာ ဖြစ်ပြီး false ဆိုရင်တော့ Persistence Context ထဲမှာ မရှိဘူးဆိုတာ သိနိုင်ပါတယ်

အထက်ပါ​ Method များထဲက persist, merge, remove method တွေကို အသုံးပြုတဲ့အခါ Managed State အတွင်းမှာ ရှိတဲ့ Entity ရဲ့ State တွေကို ပြောင်းလိုက်တဲ့ အခါမျိုးမှာ Database ထဲက တန်ဖိုးတွေပါ ပြောင်းလည်း ကုန်မှာ ဖြစ်ပါတယ်။ ဒါကြောင့် အဆိုပါ method တွေကို အသုံးပြုတဲ့ နေရာမှာ Transaction အတွင်းမှာ အသုံးပြုရပါမယ်။ ဆိုလိုတာက မပြုလုပ်မှီ Transaction ကို Begin လုပ်ထားပြီး အားလုံးပြီးဆုံးသွားတဲ့အခါ Transaction ကို Commit လုပ်ပေးရမှာ ဖြစ်ပါတယ်။


Persisting Entity


Persisting လုပ်တယ်ဆိုတာက Database ထဲမှာ မရှိတဲ့ Entity Object ကို Database ထဲကို ရောက်အောင် Insert လုပ်လိုက်ခြင်းပဲ ဖြစ်ပါတယ်။ Persist လုပ်တော့မယ်ဆိုရင် Entity Object ကို new Operator သုံးပြီး တည်ဆောက်ပါမယ်။ ပြီးပါမှ Object ရဲ့ State တွေကို setter method တွေသုံးပြီး သတ်မှတ်ပါမယ်။ ထို့နောက် EntityManager Object ရဲ့ persist method ကို အသုံးပြုရုံပါပဲ။
@Entity
public class Township implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String name;

    // Default Constructors and Getters Setters

}

အထက်ပါ Township Entity Class မှာတော့ id ကို Auto Generate လုပ်ဖို့ GeneratedValue ရဲ့ strategy မှာ IDENTITY လို့ ပေးထားပါတယ်။ ပြီးတော့ Attribute အနေနဲ့ name တစ်ခုလဲပါဝင်ပါတယ်။

အထက်ပါ Entity ကို Persist လုပ်တော့မယ်ဆိုရင် အောက်ပါ အတိုင်း ရေးသားရပါမယ်။
    @Test
    public void test1() {
        // insert
        Township t = new Township();
        t.setName("Yankin");
        
        em.getTransaction().begin();
        em.persist(t);
        em.getTransaction().commit();
        
        assertEquals(1, t.getId());
    }
အရင်ဆုံး Township Object ကို တည်ဆောက်ပါမယ်။ name တန်ဖိုးကို t.setName method ကို သုံးပြီး သတ်မှတ်ပါတယ်။ id ရဲ့ တန်ဖိုးကတော့ Auto Generate လုပ်ပေးမှာ ဖြစ်တဲ့အတွက် သတ်မှတ်စရာ မလို အပ်ပါဘူး။

JPA မှာ persist, merge, remove Operation တွေဟာ Transaction ကိုလိုအပ်ပါတယ်။ အဲ့ဒီအတွက် persist မလုပ်ခင်မှာ em.getTransaction().begin() ကို ခေါ်ပြီး transaction ကို စတင်စေဖို့လိုအပ်ပါတယ်။

Persist လုပ်ပြီးတဲ့ အခါမှာ em.getTransaction().commit() လို့ ခေါ်ပြီး Commit လုပ်ပေးရပါမယ်။ Commit လုပ်လိုက်တဲ့အချိန်ကျမှ Database ထဲကို တကယ်ရောက်ရှိသွားမှာ ဖြစ်ပါတယ်။ တဖန် Database ထဲကို Insert လုပ်လိုက်တဲ့ အခါမှာ Generate လုပ်လိုက်တဲ့ ID ရဲ့ တန်ဖိုးကိုလဲ Township ရဲ့ ID အဖြစ် Set လုပ်ပေးမှာ ဖြစ်ပါတယ်။  ဒါ့ကြောင့် assertEquals(1, t.getId()) ဆိုပြီး ID ရဲ့ တန်ဖိုးကို စစ်ဆေးနေတာဖြစ်ပါတယ်။

တနည်းဆိုရသော် Township Object t ရဲ့ id ဟာ 1 ဖြစ်ရင် Township Object ဟာ Database ထဲကို ရောက်သွားပြီဆိုတာကို သက်သေပြနိုင်လို့လဲ ဖြစ်ပါတယ်။


Find & Get Reference


Primary Key ရဲ့ တန်ဖိုးနဲ့ Entity Object ကို ပြန်ရှာချင်တဲ့ အခါမှာ EntityManager ရဲ့ find method နဲ့ getReference method ကို အသုံးပြုနိုင်ပါတယ်။ Method နှစ်မျိုးလုံးရဲ့ Argument တွေဟာ အတူတူပါပဲ။ Argument အနေနဲ့ ရှာချင်တဲ့ Entity ရဲ့ Class ကို ပထမ Argument အနေနဲ့ ပေးပြီး Id Object ကို ဒုတိယ Argument အနေနဲ့ ပေးရပါမယ်။
    @Test
    public void test2() {
        // find
        Township t = em.find(Township.class, 1);
        em.detach(t);
        assertEquals("Yankin", t.getName());
    }
    
    @Test(expected=LazyInitializationException.class)
    public void test3() {
        // Reference
        Township t = em.getReference(Township.class, 1);
        assertNotNull(t);
        em.detach(t);
        
        assertEquals("Yankin", t.getName());
    }

Return Type အနေနဲ ရှာဖွေလိုတဲ့ Entity Object ကို ပြန်ရရှိပါမယ်။ မတူညီတာကတော့ Entity Object ရဲ့ State တွေကို ဘယ်လိုပုံစံနဲ့ ရှာမလဲ ဆိုတဲ့အချက် ဖြစ်ပါတယ်။ find method နဲ့ ရှာလာရင် Entity ရဲ့ State တွေကို Egar ပုံစံနဲ့ရှာလာပါမယ်။ ဆိုလိုတာက Entity ရဲ့ Property တွေကို တစ်ခါထဲရှာလာခြင်းဖြစ်တယ်။ Entity ကို find နဲ့ရှာလာပြီး၊ EntityManager ကို clear လုပ်ပြီးတဲ့နောက် Property တွေကို getter နဲ့ ခေါ်ရင် တန်ဖိုးတွေကို ရရှိနေမှာ ဖြစ်ပါတယ်။ Select လုပ်ကထဲက တစ်ခါထဲ ရှာလာခဲ့လို့ပါ။

အထက်ပါ နမူနာထဲတွင် find နဲ့ရှာလာတဲ့ t ကို detach လုပ်ပြီး c.getName() ကို ခေါ်ရင်လဲ့ "Yankin" ကို ရရှိနိုင်မှာ ဖြစ်ပါတယ်။

getReference နဲ့ ရှာလာတဲ့ Entity Object ဆိုရင်တော့ သူတို့ရဲ့ State တွေကို Lazy Mode နဲ့ ရှာလာမှာ ဖြစ်ပါတယ်။ အဲ့ဒီအတွက် Detach State ကို ရောက်ပြီးမှ Status တွေကို Getter နဲ့  ခေါ်ရင် LazyInitializeException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်ပါတယ်။

အထက်နမူနာထဲမှာလို getReference နဲ့ရှာလာတဲ့ t ကို Detach လုပ်ပြီး t.getName() ကို ခေါ်ရင် LazyInitializationException ကို ဖြစ်ပေါ်စေမှာ ဖြစ်ပါတယ်။


Updating Entity


Database ကို Update လုပ်နိုင်တာ အနေအထား ၂ မျိုးမှာ ရှိနိုင်ပါတယ်။

ပထမအခြေအနေကတော့ Managed State အတွင်းမှာ ရှိတဲ့ Entity Object ရဲ့ State တွေကို setter method ကို အသုံးပြုပြီး ပြောင်းလိုက်တဲ့ အခါမျိုးဖြစ်ပါတယ်။ Managed State ဆိုတာက Database နဲ့ Synchronise လုပ်ထားတဲ့ State ဖြစ်တဲ့ အတွက် အဲ့လို အခြေအနေမျိုးမှာ Transaction ကို Commit လုပ်လိုက်ရင် Database ထဲကို Update ဖြစ်သွားစေနိုင်ပါတယ်။
    @Test
    public void test4() {

        em.getTransaction().begin();
        Township t = em.find(Township.class, 1);
        t.setName("Bahan");
        em.getTransaction().commit();
        em.clear();
        
        t = em.find(Township.class, 1);
        assertEquals("Bahan", t.getName());
    }

အထက်နမူနာထဲတွင် Township Object t ကို find နဲ့ရှာထားတဲ့အတွက် Managed State အတွင်းမှာ ရှိနေပါတယ်။ ထို့နောက် t.setName(“Bahan”) ဟု name ကို ပြောင်းလိုက်ပြီး commit လုပ်လိုက်တဲ့ အခါ Database ထဲကို Update လုပ်သွားမှာ ဖြစ်ပါတယ်။

ဒါ့ကြောင့် ပြန်ပြီး find လုပ်ပြီးတဲ့အခါ t ရဲ့ Name ဟာ “Bahan” ဖြစ်နေမယ်ဆိုပြီး စစ်ဆေးနေတာ ဖြစ်ပါတယ်။

ဒုတိယ အနေအထားကတော့ Detach ဖြစ်နေတဲ့ Entity Object ရဲ့ တန်ဖိုးတွေကို ပြုပြင်ပြီး merge လုပ်လိုက်တဲ့ အခါဖြစ်ပါတယ်။ EntityManager Object ရဲ့ merge method ကို ခေါ်လိုက်တဲ့အခါ Entity Object ဟာ Managed State ကို ရောက်ရှိသွားပြီး Commit လုပ်လိုက်တဲ့ အခါမှာ Database ကို Update လုပ်သွားမှာ ဖြစ်ပါတယ်။
    @Test
    public void test5() {
        
        Township t = em.find(Township.class, 1);
        em.detach(t);
        t.setName("Kamayut");
        
        em.getTransaction().begin();
        em.merge(t);
        em.getTransaction().commit();
        em.clear();
        
        t = em.find(Township.class, 1);
        assertEquals("Kamayut", t.getName());
    }



Removing Entities

Managed State အတွင်းမှာရှိတဲ့ Entity Object ကို EntityManager ကနေ remove method သုံးပြီး Remove လုပ်လိုက်ရင် Entity Object ဟာ Removed State ကို ရောက်ရှိသွားပြီး Transaction ကို Commit လုပ်လိုက်တဲ့အခါ Database ထဲကနေ Delete လုပ်သွားမှာ ဖြစ်ပါတယ်။ Remove Operation ကို ဆောင်ရွက်တဲ့ အခါမှာလဲ Database ကို Effect ဖြစ်စေနိုင်တာ ဖြစ်တဲ့အတွက် Transaction အထဲမှာ ဆောင်ရွက်ဖို့လိုအပ်ပါတယ်။
    @Test
    public void test6() {
        em.getTransaction().begin();
        
        Township t = em.find(Township.class, 1);
        em.remove(t);
        
        em.getTransaction().commit();
        em.clear();
        
        assertNull(em.find(Township.class, 1));
    }

အထက်ပါ နမူနာထဲတွင် အရင်ဆုံး find ဖြင့် Customer Object ကို ရှာဖွေထားပြီး em.remove ဖြင့် Customer Object c ကို ဖျက်ထုတ်ပစ်နေပါတယ်။ ပြီးနောက် find ဖြင့်ပြန်ရှာသောအခါမှာ မတွေ့ရပါဘူး လို့စစ်နေပါတယ်။

Remove လုပ်ထားတဲ့ Entity Object တွေဟာ Transaction တွေကို Commit လုပ်လိုက်တဲ့ အခါ Database ထဲကနေ တကယ်ဖျက်သွားမှာ ဖြစ်တဲ့အတွက် Commit လုပ်ပြီး ပြန်ရှာတဲ့အခါ မတွေ့ရဘူးလို့ စစ်ဆေးနေတာ ဖြစ်ပါတယ်။


Flushing Entities

EntityManager Object ရဲ့ Flush Method ကို ခေါ်လိုက်တဲ့အခါ Persistence Context အတွင်းမှာရှိတဲ့ Entity Object တွေကို Commit မလုပ်ခင် Database ကို သွားပြီး Update လုပ်ပေးနိုင်မှာ ဖြစ်ပါတယ်။ နမူနာ အနေနဲ့ စမ်းသပ်ကြည့် ကြရအောင်။
@Test
public void test7() {

    Customer c = new Customer();
    c.setName("Thidar");

    Customer c1 = new Customer();
    c1.setName("Nilar");

    em.getTransaction().begin();
    em.persist();

    em.flush();
    assertNotNull(em.find(Customer.class,1));

    em.persist(c1);

    em.getTransaction().commit();    
}

အထက်ပါ အတိုင်း Commit မလုပ်ခင် Flush လုပ်ပြီးတဲ့နောက် ID နဲ့ ရှာဖွေရင် တွေ့ရှိမှာ ဖြစ်ပါတယ်။ ဆိုလိုတာက Commit မလုပ်ခင် Database ထဲကို ရောက်အောင် လုပ်ပေးလိုက်နိုင်တာကို တွေ့ရပါမယ်။


Cascading Events

JPA မှာ Entity Object ကို တစ်ခုခုလုပ်လိုက်ချိန်မှာ အဲ့ဒီ Object နဲ့ Relationship အတွင်းမှာ ရှိတဲ့ Object တွေကို အလားတူ Operation တွေကို ဆောင်ရွက်နိုင်အောင် Cascade Operation တွေကို ဆောင်ရွက်နိုင်အောင် ပြင်ဆင်ထားပါတယ်။
@Entity
public class Address implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String phone;
    private String email;
    private String address;

    // Default Constructor & Getters Setters
}

ဥပမာအားဖြင့် Customer Entity မှာ Address Entity ကို One To One Relationship နဲ့ ချိတ်ဆက်ထားတယ် ဆိုကြပါစို့။ ပုံမှန်အားဖြင့် Default အတိုင်းဆိုပါက Address Entity ကို Persist လုပ်ပြီးမှ Customer Entity ကို Persist လုပ်ပေးရပါမယ်။ ဒါမှသာ Address ရဲ့ Primary Key က Customer ရဲ့ Foreign Key အနေနဲ့ အသုံးပြုနိုင်မှာ ဖြစ်မယ်။ လက်တွေ့ ရေးကြည့်ကြရအောင်။
@Entity
public class Customer implements Serializable{
    
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String name;
    @OneToOne
    private Address address;

    // Default Constructor & Getters Setters

}

အထက်ပါ Address Entity ကို Customer Entity ထဲမှာ Reference လုပ်နေပြီး One to One Relationship နဲ့ ချိတ်ဆက်ထားပါတယ်။

ပုံမှန်အတိုင်းဆိုလို့ကတော့ Customer Object ကို Persist လုပ်တော့မယ်ဆိုရင် Address Object ကို အရင်ဆုံး Persist လုပ်ထားဖို့လိုအပ်ပါမယ်။
    @Test
    public void test1() {
        Customer c = new Customer();
        c.setName("Aung Aung");
        
        Address address = new Address();
        address.setPhone("098789999");
        address.setEmail("aung@gmail.com");
        address.setAddress("Kamayut, Yangon");
        
        c.setAddress(address);
        
        em.getTransaction().begin();
        em.persist(address);
        em.persist(c);
        em.getTransaction().commit();
        
        assertEquals(1, c.getId());
    }

အထက်ပါ အတိုင်း Transaction တစ်ခုအတွင်းမှာ Address နဲ့ Customer Object တွေကို Persist လုပ်မှသာ Customer Table ထဲက Foreign Key အဖြစ် Address ရဲ့ Primary Key ကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

ဒါပေမဲ့ Cascade Event ကို အသုံးပြုမယ်ဆိုရင် Customer ကို Persist လုပ်လိုက်တာနဲ့ အလို အလျောက် Address Object ကို လဲ Persist လုပ်ပေးမှာ ဖြစ်ပါတယ်။

ရေးသားပုံမှာ အောက်ပါ အတိုင်း ဖြစ်ပါတယ်။
@Entity
public class Customer implements Serializable{
    
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String name;
    @OneToOne(cascade = { PERSIST })
    private Address address;

    // Default Constructor & Getters Setters
}

အထက်ပါ နမူနာတွင် Customer ထဲကနေ Address ကို Reference လုပ်တဲ့ နေရာမှာ OneToOne ကို အသုံးပြုထားပြီး cascade = PERSIT လို့ရေးထားတဲ့ အတွက် Customer ကို Persiste လုပ်ရင် တပြိုင်နက်ထဲ Address ကိုပါ persist လုပ်ပေးမှာ ဖြစ်ပါတယ်။
    @Test
    public void test1() {
        Customer c = new Customer();
        c.setName("Aung Aung");
        
        Address address = new Address();
        address.setPhone("098789999");
        address.setEmail("aung@gmail.com");
        address.setAddress("Kamayut, Yangon");
        
        c.setAddress(address);
        
        em.getTransaction().begin();
        em.persist(c);
        em.getTransaction().commit();
        
        assertEquals(1, c.getId());
    }
အထက်ပါ နမူနာထဲတွင် Address ကို Persist မလုပ်သော်လဲ Customer ထဲမှာ Address ကို set လုပ်ထားသောကြောင့် Customer ကို Persist လုပ်လိုက်တဲ့ အခါမှာ တစ်ခါထဲ Address ကိုပါ​ Persist လုပ်ပေးသွားမှာ ဖြစ်ပါတယ်။

Cascade အနေနဲ့ အသုံးပြုနိုင်တဲ့ Event တွေကတော့ ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH တို့ ဖြစ်ကြပါတယ်။ Cascade Operation ကို အသုံးပြုခြင်းအားဖြင့် Owner Entity Object ကို အထက်ပါ​ Operation တွေကို လုပ်ဆောင်ရင် Relationship လုပ်ထားတဲ့ Object တွေကိုပါ ဆက်တိုက် တူညီတဲ့ Operation တွေကို လုပ်ဆောင်ပေးနိုင်မှာ ဖြစ်ပါတယ်။


Orphan Removal

Orphan Removal ဆိုတာကတော့ Owner မရှိတော့တဲ့ Entity Object တွေကို အလိုအလျောက် ဖျက်ပေးနိုင်တဲ့ စနစ်ဖြစ်ပါတယ်။ ဥပမာအားဖြင့် Category နဲ့ Product ကို OneToMany နဲ့ ချိတ်ဆက်ထားတယ်လို့ ဆိုကြပါစို့။ တကယ်လို့ Category မရှိတော့ဘူးဆိုရင် အဲ့ဒီ Category နဲ့ ချိတ်ထားတဲ့ Product တွေဟာ အဓိပ္ပါယ်ရှိတော့မှာ မဟုတ်ဘူး။ ဒါကြောင့် Category ကို Remove လုပ်ရင် ပိုင်ရှင် မရှိတော့တဲ့ Produce တွေကိုပါ တစ်ခါထဲ ဖျက်သွားနိုင်အောင် Orphan Removal ကို အသုံးပြုနိုင်ပါတယ်။
@Entity
public class Product implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String name;
    private double price;
    @ManyToOne
    private Category category;

    // Default Constructor & Getters Setters
}

အထက်ပါ နမူနာထဲတွင် Product ဘက်က Category ကို Reference လုပ်ပြီး Relationship ကို Many to One ဟု သတ်မှတ်ထားပါတယ်။ ဆိုလိုတာက Category တစ်ခုမှာ Product အများရှိတယ်လို သတ်မှတ်ထားတာဖြစ်ပါတယ်။ တကယ်ဆိုရင် Product Table ထဲမှာ category_id ဆိုတဲ့ Foreign Key Column ကို ပိုင်ဆိုင်ပြီး Category ကို Reference လုပ်နိုင်မှာ ဖြစ်ပါတယ်။

တဘက် Category ဘက်ကနေကြည့်ကြရအောင်။
@Entity
public class Category implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private int id;
    private String name;
    
    @OneToMany(mappedBy = "category", orphanRemoval = true)
    private List<Product> products;

    // Default Constructor & Getters Setters
}

Category ထဲကနေ List<Product> ကို Reference လုပ်နေပါတယ်။ တဖန် @OneToMany နဲ့ Relationship ကို ချိတ်ဆက်ထားပါတယ်။ တကယ်လို့ Category ကို Remove လုပ်မယ်ဆိုရင် အဲ့ဒီ Category နဲ့ ပတ်သက်နေတဲ့ Product တွေကိုပါ​ တစ်ခါထဲ Remove လုပ်ပေးသွားမှာ ဖြစ်ပါတယ်။

အခု တစ်ခေါက်မှာတော့ Entity Object တစ်ခုကို EntityManager ကနေ ဘယ်လို အသုံးပြုမယ်ဆိုတာ တစ်ခုချင်း ရေးသားထားပါတယ်။ ဘယ်လို ရေးသင့်တယ်ဆိုတာကိုတော့ နောက်အခန်းများတွင် ဆက်လက်ဖေါ်ပြသွားပါမယ်။

ဆက်ပါဦးမည်
မင်းလွင်

No comments:

Post a Comment