February 3, 2014

ORM : Thinking about Performance of Associations

JPA အားအသုံးပြုရာတွင် Database အကြောင်းအား မေ့ထားပြီး Entity များအား Java Object များအနေနှင့် အသုံးပြုနိုင်သောကြောင့် အတော်များများ Database အား မေ့ထားတတ်ကြပါသည်။ သို့ရာတွင် ဘယ်လောက်ပင် JPA ကို အသုံးပြုပါစေ၊ နောက်ဆုံးတွင် Database အထဲတွင် Table များအား ဆောက်၍ အသုံးပြုမည်ဖြစ်သောကြောင့် မိမိရေးသားလိုက်သော Entity များသည် မည်ကဲ့သို့ Database အတွင်း တည်ဆောက်မည်၊ မည်ကဲ့သို့ Database အား Access လုပ်မည်ဆိုသည်ကို အမြဲစဉ်းစားထားသင့်ပါသည်။ Database အတွင်းရှိ Table တည်ဆောက်ပုံ Foreign Key တည်ဆောက်ပုံ၊ Data Access လုပ်ပုံတို့အပေါ်မှုတည်၍ Application ၏ Performance မှာ အလွန် ပြောင်းလည်း တတ်သောကြောင့် ဖြစ်ပါသည်။


Fetching Relationships


Object Relation ရဲ့ ဆက်နွယ်မှု့အား ဖော်ပြသော @OneToOne, @OneToMany, @ManyToOne နဲ့ @ManyToMany တို့တွင် fetching Attribute အား သတ်မှတ်ရေးသားနိုင်ပြီး၊ Target Entity အား ချက်ချင်း ခေါ်လာမည်၊ ဒါမှမဟုတ် Access လုပ်သည့်အခါမှ ခေါ်လာမည်ဆိုသည်ကို သတ်မှတ်နိုင်ခဲ့ကြ၏။ fetching ၏ တန်ဖိုးသည် EAGER ဖြစ်ပါက Owner Entity အား Access လုပ်သည်နှင့် Target Entity အား Memory အပေါ်သို့ ခေါ်လာမည်ဖြစ်ပြီး၊ LAZY ဖြစ်ပါက Target Entity အား get လုပ်သည့်အခါမှသာ Database အတွင်းမှ ခေါ်ယူလာမည် ဖြစ်ပါသည်။ Database အတွင်းမှ Data များအား Load လုပ်မည့် Timing သည် Application များ၏ Performance အပေါ် အကျိုးသက်ရောက်မှု့ များစွာရှိနိုင်ပါသည်။
အထက်ပါပုံစံတွင် Entity ၄ခုသည် ချိတ်ဆက်နေပြီး၊ ၎င်းတို့၏ fetching အမျိုးအစားအား EAGER ဟု သတ်မှတ်ထားပါသည်။ ထိုအခါမျိုးတွင် Class1 အား ခေါ်ယူသည်နှင့် ပတ်သက်နေသော Class ၄ ခုလုံးအား Database အတွင်းမှ ရှာဖွေခေါ်ယူလာမည် ဖြစ်သည်။
အထက်ပါပုံတွင် Entity ၄ခုလုံးအား LAZY နှင့် ချိတ်ဆက်ထား၏။ Class1 အားခေါ်ယူသည့်အခါတွင် အခြားသော Class များအား ခေါ်ယူလာခြင်းမရှိပါ။ Class1 တစ်ခုတည်းကိုသာ Database အတွင်းမှ ခေါ်ယူလာမည် ဖြစ်ပါသည်။ အကယ်၍ Class4 အထိခေါ်ယူလိုပါက အောက်ပါအတိုင်း ရေးသားရမည် ဖြစ်ပါသည်။
class1.getClass2().getClass3().getClass4();

Association အား ဖော်ပြသော Relationship Annotation များတွင် Default Fetching Type များရှိကြပြီး၊ ၎င်းတို့မှာ အောက်ပါအတိုင်း ဖြစ်ကြပါသည်။

Default Fetching Type အား ပြောင်းလဲလိုသည့်အခါ အောက်ပါအတိုင်း Annotation ၏ fetching Attribute အား အသုံးပြုလိုသည့် တန်ဖိုးအား ရေးသားအသုံးပြုနိုင်ပါသည်။
public class Student implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String name;
    
    @Temporal(TemporalType.DATE)
    private Date birthDate;

    @ManyToMany(fetch=FetchType.EAGER)
    @JoinTable(joinColumns = @JoinColumn(name = "STUDENT", 
        referencedColumnName = "id"), 
    inverseJoinColumns = @JoinColumn(name = "CLASS", 
        referencedColumnName = "id"))
    private List<Class> classes;
    
    // constructors, getter and setter

}
EAGER အား အသုံးပြုပါက ဆက်နွယ်နေသော Entity များအား တစ်ပါတည်းခေါ်ဆောင်လာမည် ဖြစ်သော်လည်း၊ Data အားလုံးကို တစ်ခါတည်း ခေါ်ဆောင်လာမည် ဖြစ်သောကြောင့် တစ်ခုတည်းအား ခေါ်ဆောင်လာသည်နှင့် စာလျှင် လေးလံမည် ဖြစ်သည်။ တဖန် LAZY အား အသုံးပြုပါက Data တစ်ခုတည်းအား ခေါ်ဆောင်မည်ဖြစ်သောကြောင့် ပေါ့ပါးမည်ဖြစ်သော်လည်း၊ အခြားသော Data များအား ခေါ်ဆောင်သည့်အခါတိုင်း Database အား Access လုပ်ရမည် ဖြစ်သောကြောင့် Access Count တွေ များပြားလာနိုင်ပါသည်။

Performance အား ကောင်းအောင်မည်သို့ပြုလုပ်ရမည် ဆိုသည်မှာ မိမိ၏ Application အပေါ်တွင် မှုတည်ပါသည်။ List ကိုသာဖော်ပြ၍ အသေးစိတ်အား ဖော်ပြစရာမလိုသည့် အခါမျိုးတွင် LAZY အား အသုံးပြုသင့်၍၊ အသေးစိတ်အား တစ်ခါတည်း ဖော်ပြလိုသည့် အခါမျိုးတွင် EAGER အား အသုံးပြုသင့်ပါသည်။


Ordering Relationships


@OneToMany နှင့် @ManyToMany တို့အား အသုံးပြုရာတွင် Entity များသည် အခြားသော Object များအား Collection များအနေနှင့် အသုံးပြုကြရ၏။ အကယ်၍  အဆိုပါ Collection များအတွင်းရှိ Element များအား အစီအစဉ်တကျ အသုံးပြုလိုသည့်အခါမျိုးတွင် Program ဖြင့် Sort လုပ်၍သော်၎င်း၊ JPQL ၏ ဝါကျတွင် ORDER BY အား ရေးသား၍ သော်၎င်း ရေးသားရမည်ဖြစ်သည်။
JPA တွင် Collection များ၏ အစီအစဉ်အား သတ်မှတ်နိုင်ရန် အသုံးဝင်သော Annotation များအား ပြင်ဆင်ထားပါသည်။ မိမိကိုယ်တိုင် ပရိုဂရမ်အား ရေးသား၍ Sort လုပ်သည်ထက်စာလျှင် Framework မှပြင်ဆင်ထားသော Function များအား အသုံးပြုသည်က Code ရေးရသက်သာပြီး၊ Test လုပ်ရသည့် အချက်များကိုလည်း လျှော့ပါးစေပြီး Performance အပိုင်းဆိုင်ရာတွင်လည်း ပိုမိုကောင်းမွန်စေပါသည်။


@OrderBy


Collection များအတွင်းရှိ Element များအား အလိုအလျှောက် Sort လုပ်နိုင်ရန် @OrderBy Annotation အား အသုံးပြုနိုင်ပါသည်။ နမှုနာအနေနဲ့ အရင်ရေးခဲ့တဲ့ Student နဲ့ Address ကိုပဲ ပြန်ပြီး ရေးကြီးပါမည်။ Student တစ်ယောက်တွင် လိပ်စာအမျိုးမျိုးပိုင်ဆိုင်နိုင်ပြီး၊ လိပ်စာတွေကို အမျိုးအစားအလိုက် စီပြီးရယူလို့တဲ့ အခါမျိုးကို စဉ်းစားပြီး ရေးကြည့်ပါမည်။
@Entity
public class Address implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String address;
    @Enumerated(EnumType.STRING)
    private TYPE type;
    
    @ManyToOne(cascade = ALL)
    @JoinColumn(name = "STUDENT", referencedColumnName = "id")
    private Student student;

    // constructors, getter and setter

}
အထက်ဖော်ပြပါအတိုင်း Address မှာ id, type, address နဲ့ student တို့ကို Member အနေနဲ့ပိုင်ဆိုင်ပြီး၊ student ကို Many To One နဲ့ ချိတ်ဆက်ထားပါသည်။ ပြီးတော့ Address နဲ့ Student ကို Join Column နဲ့ ချိတ်ထားတဲ့ အတွက် Address Table ထဲမှာ STUDENT အမည်နဲ့ Student ရဲ့ PK ဖြစ်တဲ့ ID ကို Foreign Key အနေနဲ့ Reference လုပ်ပါစေပါလိမ့်မည်။
@Entity
public class Student implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String name;
    @OneToMany(mappedBy = "student", cascade = ALL, fetch = EAGER)
    @OrderBy("type DESC")
    private List<Address> addresses;
    
    // constructors, getter and setter

}
Student ထဲမှာတော့ id, name နဲ့ addresses ကို member အနေနဲ့ ပိုင်ဆိုင်စေပြီး၊ Address အား One To Many နဲ့ ချိတ်ဆက်ထားပါသည်။ Address List ရဲ့အပေါ်မှာ @OrderBy Annotation ကို အသုံးပြုပြီး၊ type DESC လို့ရေထားတဲ့အတွက် အဲ့ဒီ List ထဲက Address တွေကို type နဲ့ ပြောင်းပြန် စီပေးနိုင်ပါလိမ့်မည်။ အကယ်၍ အသေးအကြီးစီစဉ်လိုပါက ASC ကို အသုံးပြုနိုင်ပြီး၊ အကြီးအသေး စီလိုပါက DESC ကို အသုံးပြုရပါလိမ့်မည်။ အကယ်၍ Column အများနှင့် စီလိုပါက ကော်မာခံ၍ ရေးသားနိုင်ပါသည်။

ဥပမာအားဖြင့် address ဖြင့် အသေးအကြီးစီ၍၊ type ဖြင့် အကြီးအသေးစီလိုပါက "address ASC, type DESC" ဟု ရေးသားနိုင်ပါသည်။

တစ်ခုသတိပြုရန်မှာ @OrderBy အား အသုံးပြုခြင်းအားဖြင့် ပတ်သက်ရာ Entity အား ရှာဖွေယူသည့်အခါတွင် အစီအစဉ်စီ၍ Collection အတွင်းသို့ဖြည့်စွက်ပေး နေသည်ဖြစ်ပြီး၊ Database အတွင်းသို့ Insert လုပ်သည့်အခါတွင် စီ၍ ထည့်ပေးခြင်းမဟုတ် ဆိုသည့်အချက်ပင် ဖြစ်သည်။


@OrderColumn


အကယ်၍ Database အတွင်းသို့ အစီအစဉ်အားထိမ်း၍ သိမ်းဆည်းလိုပါက မည်သို့ပြုလုပ်မည်နည်း။ @OrderColumn အား အသုံးပြု၍ ရေးသားခြင်းအားဖြင့် Database အတွင်းတွင် အစီအစဉ်အား ထိမ်း၍ ထည့်သိမ်းပေးနိုင်မည် ဖြစ်ပါသည်။

အထက်တွင်ရေးသားခဲ့သော Student Entity အား @OrderColumn ဖြင့် ရေးသားကြည့်ပါဦးမည်။
@Entity
public class Student implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String name;
    @OneToMany(mappedBy = "student", cascade = ALL, fetch = EAGER)
    @OrderColumn(name="ADD_IDX")
    private List<Address> addresses;
    
    // constructors, getter and setter

}
အထက်ပါအတိုင်း ပြုပြင်ရေးသား၍ Test Data ဖြင့် စမ်းကြည့်သောအခါ အောက်ပါအတိုင်း Address Table အတွင်းတွင် ADD_IDX ကော်လန်အား တည်ဆောက်၍ ထည့်သွင်းခဲ့သည့် အစီအစဉ်အား ထိမ်းပေးနိုင်သည်ကို တွေ့ရပါသည်။


လိုအပ်လာပါက @OrderBy ကော @OrderColumn ပါ ပူးတွဲ အသုံးပြုနိုင်မည် ဖြစ်ပါသည်။

@OrderColumn အားအသုံးပြုသည့် အခါတွင် Index Column တစ်ခုအား Performance အပိုင်းဆိုင်ရာမှာ သိသာသော ပြောင်းလည်းမှု့ကို ဖြစ်ပေါ်စေနိုင်တဲ့အတွက် သတိပြု၍ အသုံးပြုသင့်ပါသည်။ ထည့်သွင်းမည့် အချက်အလက်များ၏ Order အား ထိန်းသိမ်းရန် လိုအပ်သည့်အတွက် Insert, Delete, Update လုပ်တဲ့ အခါမှာ ပြန်ပြီးစီစဉ်ဖို့လိုအပ်ပါလိမ့်မည်။

အကယ်၍ အစီအစဉ်တဝက်မှ အချက်အလက်များအား အသစ်ဖြည့်စွက်လိုသည့် အခါမျိုးတွင် Persistence Provider ဟာ Index အားလုံးကို ပြန်ပြီး စီစဉ်ဖို့လိုအပ်ပါလိမ့်မည်။ ထို့အတွက် @OrderColumn အား အသုံးပြုသည့် အခါတွင် အထူးသတိပြု၍ အသုံးပြုရန် လိုအပ်ပါသည်။

ဆက်ပါဦးမည်။ လေးစားစွာဖြင့်။
မင်းလွင်

2 comments:

  1. မဂၤလာပါခင္ဗ်ာအကူညီေတာင္းလိုလို႔ပါဆရာ ဆရာ့ဘေလာ့တင္ေပးသမွ်ေတြကိုmailကေနပို႔ေပးလို႔ရေအာင္ကူညီေပးေစလိုတာပါရွာၾကည့္တာမေတြ႔တဲ့အတြက္မို႔ပါ
    nayminthura444@gmail.comပါဆရာသင္တန္းတက္ဖို႔အခက္ခဲရွိေနတုံးဆရာ့ဘေလာ့ေတြ႔၇တာေရႊေပးတာထက္အရေတာ္လို႔ေက်းဇူးတင္မဆုံးျဖစ္ေနရပါတယ္ဆရာသက္ရွည္ၾကမ္းမာအရာရာအလိုအတိုင္းျပည့္ပါေစဆုေတာင္းပါတယ္ဆရာ

    ReplyDelete
  2. အခုလို Message ရေးသွားတာ ကျေးဇူးပါပဲ။ ဒါပေမယ့် ညီရေးတဲ့ Message ကို ဖတ်လို့မရဘူး ဖြစ်နေတယ်။ ကျွှန်တော့်စက်ထဲမှာ ဇော်ဂျီ စာလုံးကိုလုံးဝထည့်မထားပါဘူး။ တတ်နိုင်ရင် ယူနီကုဒ်စာလုံးအမျိုးအစားကို သုံးပြီးရေးပေးပါ။
    အကြမ်းဖျဉ်းအားဖြင့် ကြည့်ရတာ Post တွေကို Mail ကတဆင့် လိုချင်ပုံရပါတယ်။ အားလုံးဆိုရင် ၁၀၀ကျော်သွားပြီဆိုတော့ ညီဘာလိုချင်တာလည်း မသိဘူး။
    Java SE ကိုတော့ပြန်ပြီး Word ပုံစံနဲ့ ပြင်ရေးနေပါတယ်။ Generics နဲ့ Exception ပြီးရင်တော့ တစ်ဝက်လောက်တော့ပြီးပါပြီ။ Essential API တွေ ဖြည့်ရေးပြီးရင်တော့ အားလုံးပြီးမှာပါ။ Java SE 8 ရဲ့ Feature တွေတော့ မပါသေးပါဘူး။ အခုလောလောဆယ် ပြီးသလောက် လိုချင်ရင်တော့ အဲ့ဒါတွေ ပေးလို့ရပါတယ်။

    ReplyDelete