February 4, 2014

ORM : Inheritance Mapping

Java ဘာသာရပ်သည် OOP အား အခြေခံထားပြီး၊ OOP ၏ အဓိက Concept တစ်ခု ဖြစ်သော Inheritance အား အသုံးပြုနိုင်ပါသည်။ Inheritance အား အသုံးပြုခြင်းအားဖြင့်၊ ရေးပြီးသား Resource များအား ပြန်လည်အသုံးချနိုင်ခြင်း၊ Object များအား အုပ်စုဖွဲ့၍ အသုံးချနိုင်ခြင်း၊ Polymorphism ကဲ့သို့သော အမျိုးအစားတူ၍ ပုံစံမတူသော Object များအားအသုံးပြုနိုင်ခြင်း၊ Polymorphic Query များအား အသုံးပြုနိုင်ခြင်း အစရှိသည့် အကျိုးများကို ရရှိနိုင်ပါသည်။ လက်တွေ့ Java ပရိုဂရမ်အတော်များများတွင်လည်း Inheritance အား များစွာအသုံးပြု၍ ရေးသားကြသည်ကို တွေ့ရပါသည်။

JPA တွင် Abstract Class ကော Concrete Class များပါ Entity အနေနှင့် အသုံးပြုနိုင်ပြီး၊ Entity Class တစ်ခုသည် အခြားသော Entity Class တစ်ခု ဒါမှမဟုတ် Non Entity Class များထံမှ မျိုးရိုးဆက်ခံ နိုင်ပါသည်။ ထို့အပြင် Non Entity Class တစ်ခုသည်လည်း အခြားသော Entity Class တစ်ခု ထံမှ မျိုးရိုး ဆက်ခံနိုင်ပါသည်။


Abstract Entity Class


Abstract Class တစ်ခုတွင် @Entity Annotation အား တပ်ဆင်၍ Entity Class တစ်ခု အဖြစ်အသုံးပြုနိုင်ပါသည်။ ၎င်းသည် တိုက်ရိုက် Instance အဖြစ် မပြုလုပ်နိုင်သည်ကလွဲ၍ အခြားသော Entity Class များကဲ့သို့ အသုံးပြုနိုင်ပါသည်။ ၎င်း၏ Concrete Sub Class များမှတဆင့် Abstract Entity အား Query မှ တဆင့် အသုံးပြုနိုင်ပါသည်။ ရေးသားပုံမှာ အောက်ပါအတိုင်း ဖြစ်ပါသည်။
နမှုနာအနေနှင့် Employee Abstract Entity အား Extends လုပ်ထားသော FullTimeEmployee နှင့် PartTimeEntity တို့အား ရေးသားကြည့်ပါမည်။
@Entity
public class Address implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String address;
    private String township;
    
    // constructor, serialize number, getter setter

}
အထက်ပါ Class သည် Abstract Entity Employee တွင် အသုံးပြုမည့် Address Entity Class တစ်ခု ဖြစ်ပါသည်။

@Entity
@DiscriminatorColumn(name="DISC")
public abstract class Employee implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    protected long id;
    protected String name;
    @OneToOne(cascade = ALL)
    @PrimaryKeyJoinColumn
    protected Address address;
    
    // constructor, serialize number, getter setter

}
အထက်ပါ Class သစ် Abstract Entity Class ဖြစ်ပြီး အရှေ့တွင် abstract Keyword အား အသုံးပြုကာ @Entity Annotation အား တပ်ဆင်ရေးသားထားပါသည်။ ဤကဲ့သို့ ရေးသားထားသောကြောင့် Abstract Entity အဖြစ်အသုံးပြုနိုင်ပြီး ၎င်းအား တိုက်ရိုက် Instance အဖြစ် ပြောင်းလည်း၍မရပါ။

Persistence Engine သည် အကဲ၍ အသုံးပြုနေသော Entity Class များတွင် Inheritance ပြုလုပ်ထားခြင်း အားတွေ့ရှိပါက အလိုအလျှောက် Relational Database နှင့် Mapping လုပ်ပေးမည် ဖြစ်သည်။ ထိုသို့ပြုလုပ်ရာတွင် အထွေအထူး Strategy အား ရွေးချယ်သတ်မှတ်ထားခြင်း မရှိပါက Default ဖြစ်သော Single Table Per Class Hierarchy Strategy အား ရွေးချယ်၍ သတ်မှတ်ပေးမည်ဖြစ်ပါသည်။ (Entity Class များ အကြား Inheritance လုပ်ရာတွင် Inheritance Strategy အား ရွေးချယ်သတ်မှတ်နိုင်ပြီး၊ နောက်အခန်းတွင် အသေးစိတ် ဖော်ပြသွားပါမည်) ဆိုလိုသည်မှာ မျိုးရိုးဆက်ခံထားသော Class အုပ်စုတစ်ခုလုံးအား Table တစ်ခုနှင့် Map လုပ်မည် ဖြစ်ပါသည်။

Sub Class များအားလုံး၏ Data များသည် Table တစ်ခု အတွင်းတွင် သိမ်းဆည်းနိုင်မည် ဖြစ်ပါသည်။ ထိုသို့ ရောနောနေသော Data များအား ခွဲခြားရန်  Discriminator Column အား အလိုလိုတည်ဆောက်ကာ အသုံးပြုမည်ဖြစ်သည်။ သို့ရာတွင် အထက်ပါနာ့မှုနာ၌  Discriminator Column ၏ အမည်အား ပြောင်းလည်းလိုသောကြောင့် @DiscriminatorColumn ၏  အမည်အား "DESC" ဟု ရေးသားအသုံးပြု နေခြင်း ဖြစ်ပါသည်။ အကယ်၍ Default အတိုင်းအသုံးပြုလိုပါက အထွေအထူး ပြောင်းလည်းရန် မလိုအပ်ပါ။

@Entity
@DiscriminatorValue("FE")
public class FullTimeEmployee extends Employee implements Serializable {

    private float salary;
    
    // constructor, serialize number, getter setter

}
@Entity
@DiscriminatorValue("PE")
public class PartTimeEmployee extends Employee implements Serializable {

    private float hourlyWage;
    
    // constructor, serialize number, getter setter

}
အထက်ပါ FullTimeEmployee နှင့် PartTimeEmployee တို့သည် အသီးသီး Employee အား Extends လုပ်ထားကြသောကြောင့်၊ Employee တွင် ပိုင်ဆိုင်သော အရာများအား အသုံးပြုနိုင်ကြ၏။ တဖန် ၎င်းတို့သည် Employee များဖြစ်ကြသောလည်း၊ FullTimeEmployee တွင် salary အား ပိုင်ဆိုင်ပြီး၊ PartTimeEmployee တွင်မှု hourlyWage အား ပိုင်ဆိုင်၏။ ဤကဲ့သို့ Inheritance အား အသုံးချခြင်းအားဖြင့် အမျိုးအစားတူသော်လည်း အသွင်သဏ္ဌာန်မတူညီသော Object များအား ခွဲခြား၍ အသုံးပြုနိုင်ပါသည်။

တဖန် ၎င်းတို့တွင် @Entity Annotation အပြင် @DiscriminatorValue အားအသုံးပြုထားပြီး အသီးသီး တွင် သတ်မှတ်ထားသော တန်ဖိုးများအား Discriminator Column ၏ တန်ဖိုးများအဖြစ် အသုံးပြုသွားမည် ဖြစ်သည်။ အကယ်၍ @DiscriminatorValue အား ရေးသားထားခြင်းမရှိပါက Default တန်ဖိုးအတိုင်း Class Name အား အသုံးပြုသွားမည် ဖြစ်ပါသည်။

CREATE TABLE EMPLOYEE (
    ID BIGINT AUTO_INCREMENT NOT NULL, 
    DISC VARCHAR(31), 
    NAME VARCHAR(255), 
    HOURLYWAGE FLOAT, 
    SALARY FLOAT, 
    PRIMARY KEY (ID))

CREATE TABLE ADDRESS (
    ID BIGINT AUTO_INCREMENT NOT NULL, 
    ADDRESS VARCHAR(255), 
    TOWNSHIP VARCHAR(255), 
    PRIMARY KEY (ID))
အထက်ပါ Entity များအား အသုံးပြု၍ DDL အား Generate လုပ်ကြည့်သောအခါ အထက်ပါအတိုင်း Table များအား တည်ဆောက်ပေးသည် ကိုတွေ့ရပါသည်။ Employee Entity နှင့် Sub Class များ၏ Member များအား ENTITY Table အဖြစ် Map လုပ်ပေးပါသည်။ ထို့အပြင် Data အသီးသီးအား ခွဲခြားနိုင်ရန် DISC Column အား တည်ဆောက် အသုံးပြုထားပါသည်။

ဤနည်းအားဖြင့် FullTimeEmployee နှင့် PartTimeEmployee တို့အား EMPLOYEE Table အတွင်းတွင် အတူတကွ သိမ်းဆည်းပေးနိုင်မည် ဖြစ်ပါသည်။ Employee Entity Class သည် Abstract Class ဖြစ်သောကြောင့် Instance အဖြစ် ပြုလုပ်နိုင်ခြင်းမရှိသောကြောင့် Database အတွင်း သိမ်းဆည်းနိုင်မည် မဟုတ်ပေ။

အကယ်၍ Employee Class အား အသုံးပြုလိုပါက abstract Keyword အား ဖြုတ်၍ Concrete Class အဖြစ် ပြောင်းလည်း အသုံးပြုနိုင်ပါသည်။


Mapped Superclass


Entity Class တစ်ခုသည် Superclass တစ်ခုထံမှ Persistence Entity State နှင့် Mapping Information များအား ဆက်ခံရယူလိုသော်လည်း၊ ၎င်းအား Entity အဖြစ် မသတ်မှတ်လိုသည့်အခါ မျိုးရှိတတ် ပါလိမ့်မည်။ ထိုအခါမျိုးတွင် Mapped Superclass အား အသုံးပြုနိုင်ပြီး၊ ၎င်းသည် Persistence Entity State နှင့် Mapping Information များအား Subclass များအကြားတွင် ဘုံအနေနှင့် အသုံးပြုနိုင်ရန် အကူအညီ ပေးနိုင်ပါသည်။

Mapped Superclass သည် အခြားသော Entity ကဲ့သို့ ရှာဖွေ၍ မရနိုင်ပဲ၊ EntityManager နှင့် Query Operation တွေမှာလည်း Argument အနေနှင့် အသုံးပြု၍ မရနိုင်ပါ။ Relationship Mapping များအား ရေးသာရာတွင်လည်း Mapped Superclass အား Uni-Directional အဖြစ်သာ အသုံးပြုနိုင်မည် ဖြစ်သည်။

Abstract ကော Concrete Class များတွင် @MappedSuperclass Annotation အား အသုံးပြု၍ ရေးသားနိုင်မည် ဖြစ်သည်။ ဤကဲ့သို့ ရေးသားထားသော Mapped Superclass များသည် Entity State နှင့် Mapping Information များအား Subclass များဆီသို့ လက်ဆင့်ကမ်းပေးနိုင်ပြီး၊ ၎င်းကိုတိုင်မှာမူ Table တစ်ခု အနေနှင့် Map လုပ်ခြင်း မရှိနိုင်ပါ။ အကယ်၍ Mapped Superclass များမှ အမွေဆက်ခံထားသော Entity State နှင့် Mapping Information များအား Subclass များတွင် ပြုပြင်၍ အသုံးပြုလိုပါက @AttributeOverride နှင့် @AssociationOverride Annotation များအား အသုံးပြု၍ ပြောင်းလည်း ရေးသားနိုင်ပါသည်။ အထက်တွင် ရေးသားခဲ့သော နမှုနာအား Mapped Superclass အား အသုံးပြု၍ ရေးသားကြည့်ပါမည်။

@MappedSuperclass
public abstract class Employee implements Serializable {
   
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    protected long id;
    protected String name;
    @OneToOne(cascade = ALL)
    @JoinColumn(name = "ADDR", referencedColumnName = "id")
    protected Address address;
    
    // constructor, serialize number, getter setter

}
အထက်ပါ Employee Class အား Mapped Superclass အနေနှင့် အသုံးပြုလိုသောကြောင့် @MappedSuperclass အား အသုံးပြု ရေးသားထားပါသည်။ Employee သည် Mapped Superclass ဖြစ်၍ Table အဖြစ်တည်ဆောက်ခြင်း မရှိသောကြောင့် ၎င်းတွင် Discriminator Column အား သတ်မှတ်ရေးသားထားခြင်း မရှိပါ။

@Entity
@Table(name="F_EMP")
public class FullTimeEmployee extends Employee implements Serializable {

    private float salary;
    
    // constructor, serialize number, getter setter

}
@Entity
@Table(name="P_EMP")
@AssociationOverride(
    name = "address", 
    joinColumns = @JoinColumn(
        name = "ADDR_ID", 
        table = "P_EMP", 
        referencedColumnName = "id"
    )
)
public class PartTimeEmployee extends Employee implements Serializable {

    @Column(name="WAGE")
    private float hourlyWage;
    
    // constructor, serialize number, getter setter

}
အထက်ပါ FullTimeEmployee နှင့် PartTimeEmployee တို့သည် Mapped Superclass ဖြစ်သော Employee အား Extends လုပ်ထားပြီး၊ Employee ၏ Persistence Entity State နှင့် Mapping Information များအား ဆက်ခံ အသုံးပြုကြပါသည်။ တဖန် PartTimeEmployee တွင် Superclass မှ လက်ခံရယူထားသော Mapping Information အား ပြုပြင်အသုံးပြုလိုသောကြောင့် @AssociationOverride အား အသုံးပြုကာ ပြုပြင်ရေးသားထားပါသည်။

CREATE TABLE P_EMP (
    ID BIGINT AUTO_INCREMENT NOT NULL, 
    WAGE FLOAT, 
    NAME VARCHAR(255), 
    ADDR_ID BIGINT, 
    PRIMARY KEY (ID)
)

CREATE TABLE F_EMP (
    ID BIGINT AUTO_INCREMENT NOT NULL, 
    NAME VARCHAR(255), 
    SALARY FLOAT, 
    ADDR BIGINT, 
    PRIMARY KEY (ID)
)

CREATE TABLE ADDRESS (
    ID BIGINT AUTO_INCREMENT NOT NULL, 
    ADDRESS VARCHAR(255), 
    TOWNSHIP VARCHAR(255), 
    PRIMARY KEY (ID)
)

ALTER TABLE P_EMP ADD 
    CONSTRAINT FK_P_EMP_ADDR_ID 
    FOREIGN KEY (ADDR_ID) 
    REFERENCES ADDRESS (ID)

ALTER TABLE F_EMP ADD 
    CONSTRAINT FK_F_EMP_ADDR 
    FOREIGN KEY (ADDR) 
    REFERENCES ADDRESS (ID)

အထက်ပါ Entity များအား အသုံးပြု၍ DDL အား Generate လုပ်ကြည့်သောအခါ FullTimeEmployee နှင့် PartTimeEmployee တို့အား Employee Mapped Superclass မှ Entity State နှင့် Mapping Information များအား ရယူကာ အသီးသီး Table များအား တည်ဆောက်နိုင်သည်ကို တွေ့ရပါသည်။ ထို့အတူ PartTimeEmployee တွင် Override လုပ်ထားသော Information ကို အသုံးပြု၍လည်း P_EMP Table တွင် ထည့်သွင်းပေးနိုင်သည်ကို တွေ့ရပါသည်။



Nonentity Class in Entity Inheritance Hierarchy



Entity Class များ၏ မျိုးရိုးစဉ်ဆက်များအကြားတွင် Abstract အနေနဲ့ကော Concrete အနေနဲ့ပါ Nonentity Class များကား အသုံးပြုနိုင်ပြီး၊ ၎င်း၏ Inheritance အပြုအမူများကိုသာ အသုံးပြုနိုင်မည် ဖြစ်သည်။ Nonentity Superclass အတွင်းမှ State များအား Database အတွင်းတွင် သိမ်းဆည်းပေးနိုင်မည် မဟုတ်ပါ။

Nonentity Class အတွင်းမှ State များသည် Subclass ဖြစ်သော Entity Class များအတွင်း non-persistence State များအနေနှင့် inheritance လုပ်သွားပြီး၊ Database အတွင်း ထည့်သွင်း သိမ်းဆည်းသွားမည် မဟုတ်ပါ။ ထို့အတူ Nonentity Class များသည် Entity အနေနှင့် အသုံးပြုနိုင်ခြင်း မရှိသောကြောင့် Query များနှင့် EntityManager များ၏ Argument အနေနှင့်လည်း အသုံးပြုနိုင်မည် မဟုတ်ပါ။

နမှုနာအနေနှင့် Shopping Cart Application တစ်ခုအား ရေးသားကြည့်ပါမည်။
နမှုနာထဲတွင် Cart အတွင်းရှိ အရေအတွက်အား ထိမ်းသိမ်းရန် Cart Class အား အသုံးပြုပြီး ၎င်းသည် Database အတွင်းတွင် သိမ်းဆည်းရန် မလိုအပ်သောကြောင့် Nonentity Class အနေနှင့် ရေးသားပါမည်။ ထို့နောက် Database အတွင်းသိမ်းဆည်းရန် လိုအပ်သည့် ShoppingCart နှင့် Item တို့အား Entity အနေနှင့် ရေးသားပါသည်။

ShoppingCart သည် Cart ၏ State အား အသုံးပြုလိုသောကြောင့် Cart အား Extends လုပ်ကာ ရေးသားပါမည်။

@Entity
public class Item implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    private String name;
    private float price;
    
    // constructor, serialize number, getter setter

}
ဦးစွာ Shopping Cart တွင် အသုံးပြုမည့် Item အား ရေးသားပါသည်။ နမှုနာအနေနှင့် အသုံးပြုသောကြောင့် အနည်းဆုံး အချက်အလက်များကိုသာ အသုံးပြုထားပါသည်။

public abstract class Cart {

    private int count;
    
    protected void countUp() {
        this.count ++;
    }
    
    public int getCount() {
        return this.count;
    }
}
အထက်ပါ အတိုင်း abstract Class Cart အား အရေ အတွက်အား ထိမ်းပေးရန် ရေးသားပါသည်။ Cart အတွင်းရှိ အရေအတွက်သည် Memory ပေါ်မှာသာအသုံးပြု၍ Database တွင် သိမ်းမထားလိုသောကြောင့် Non-Entity Class အဖြစ် ရေးသားပါသည်။

@Entity
@Table(name="CART")
public class ShoppingCart extends Cart implements Serializable {
       
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;
    @Temporal(TemporalType.TIME)
    private Date date;
    @OneToMany(cascade = ALL)
    @JoinTable(name = "CART_ITEM", 
        joinColumns = @JoinColumn(name = "CART_ID"), 
        inverseJoinColumns = @JoinColumn(
                name = "ITEM_ID", referencedColumnName = "id")
    )
    private List<Item> items;
    
    public void addToCart(Item i) {
        this.items.add(i);
        super.countUp();
    }
    
    // constructor, serialize number, getter setter

}
နောက်ဆုံးတွင် Cart Non-Entity Class အား Extends လုပ်၍ ShoppingCart အား ရေးသားပါသည်။ Non-Entity Class အတွင်းမှ count နှင့် countUp များအား Database အတွင်းတွင် Persist လုပ်စရာမလိုပဲ ShoppingCart အတွင်းတွင် အသုံးပြုနိုင်မည် ဖြစ်ပါသည်။


အထက်ပါ Entity လုပ်၍ Database အား Create လုပ်သော အခါ အထက်ပါအတိုင်း Table များအား တည်ဆောက်ပေးသွားပါသည်။ Database အတွင်း Persist လုပ်စရာမလိုသော Cart ၏ အချက်အလက်များအား Table အဖြစ်မတည်ဆောက်ပဲ လိုအပ်သည့် အချက်အလက်များကိုသာ Database အတွင်း တည်ဆောက်သွားသည်ကို တွေ့ရပါသည်။

ဤနည်းအားဖြင့် Inheritance Mapping အားအသုံးပြုခြင်းအားဖြင့် Java Program ဘက်တွင် OOP ၏ အသုံးပြုပုံတို့အား အပြည့်အဝအသုံးချပြီး၊ Relational Database အပေါ်တွင်လည်း လိုအပ်သလို Table များအား ဒီဇိုင်းချပြီး ရေးသားသွားနိုင်မည် ဖြစ်ပါသည်။

နောက်တစ်ခန်းတွင် ဆက်လက်၍ Inheritance Strategy အကြောင်းကို ရေးသားသွားပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်

No comments:

Post a Comment