July 16, 2014

Abstract Service Class

ပြီးခဲ့တဲ့အခေါက်တုန်းက Entity တွေအားလုံးကိုအခြေခံ အသုံးပြုမယ့် Dao ကို ရေးခဲ့ပါတယ်။ ဒီတစ်ခေါက် အဲ့ဒီ Entity တွေကို Web အပေါ်မှာ Service အနေနဲ့ ပြဖို့အတွက် RESTFul Web Service ကို ရေးပါမယ်။ ဒီတစ်ခေါက်မှာလည်း Dao တုန်းကလိုပဲ Resource တွေကို Reference လုပ်မယ်၊ Create လုပ်မယ်၊ Update လုပ်ပြီး Delete လုပ်ပါလိမ့်မယ်။ Dao တုန်းကလိုပဲ Generices ကို အသုံးပြုပြီး ရေးသားပါမယ်။ ဒါပေမယ့် ဒီတစ်ခေါက်က သိပ်ပြီးမတူဘူးဗျ။ ဘာလို့လဲဆိုတော့ JAX-RS မှာ Resource တစ်ခုအတွက် unique ဖြစ်တဲ့ Path တစ်ခုကို ရေးရမှာဖြစ်တယ်။ အားလုံးကို ဒီ Class တစ်ခုတည်းနဲ့ ရေးမယ်လို့လုပ်လို့မရပါဘူး။ အဲ့ဒီအတွက် ဆင်တူတဲ့ Method တွေကို Abstract Class မှာရေထားပြီး၊ ထူးထူးခြားခြားရေးချင်တဲ့ အခါတွေမှပဲ Concrete Class တွေထဲမှာ ဖြည့်ရေးပါမယ်။ နောက်ပြီး Path တွေကိုလည်း Concrete Class တွေမှာရေသားပါမယ်။


Application Path


အရင်ဦးဆုံး ရေးထားတဲ့ အပလီမှာ JAX-RS ကို သုံးလို့ရအောင်လုပ်ပါဦးမယ်။ web.xml မှာ ဖြည့်ရေးတဲ့နည်းရယ်၊ Annotation ကို သုံးရေတဲ့နည်းရယ် ရှိပါတယ်။ ဒီအပလီမှာ အစထဲက web.xml ကို အသုံးမပြုထားပါဘူး။ အဲ့ဒီအတွက် Annotation ကိုပဲသုံးပြီးရေပါမယ်။ ရေးရမှာကတော့ javax.ws.rs.ApplicationPath ကို extends လုပ်ထားတဲ့ Class တစ်ခုကို ရေးရပါမယ်။ ပြီးရင်အဲ့ဒီမှာ @ApplicationPath ကို ဖြည့်ပြီးရေရုံပါပဲ။ အဲ့ဒီ Annotation ရဲ့ Parameter အဖြစ်နဲ့ အသုံးပြုလိုတဲ့ Path ကို ရေးလိုက်တာနဲ့ အဲ့ဒီ Path ဟာ Rest Application ရဲ့  Root End Point အဖြစ်အသုံးပြုနိုင်မှာဖြစ်ပါတယ်။
@ApplicationPath(value = "/")
public class ServiceApplication extends Application {

}

အထဲမှာ "/" လို့ရေထားတဲ့အတွက် Web Application ရဲ့ Root Path ကို Rest ရဲ့ Root End Point အဖြစ်အသုံးပြုနိုင်မှာဖြစ်ပါတယ်။



Abstract Service Class


RESTFul Web Service ကို ရေးလို့ရပြီဆိုတော့ Entity တွေကို Resources အဖြစ်ဖော်ပြဖို့အတွက် Abstract Class တစ်ခုကိုရေးပါမယ်။
public abstract class AbstractService<T> {

 @Inject
 protected Dao<T> dao;
 protected Class<T> entity;

 protected abstract void setEntity();

 @PostConstruct
 public void init() {
  setEntity();
 }

 @GET
 @Produces(value = MediaType.APPLICATION_JSON)
 public List<T> getAll() {
  return dao.findAll(entity);
 }

 @GET
 @Path("{id}")
 @Produces(value = MediaType.APPLICATION_JSON)
 public T findById(@PathParam(value = "id") Integer id) {
  return dao.findById(id, entity);
 }

 @POST
 @Consumes(value = MediaType.APPLICATION_JSON)
 public T create(T t) {
  dao.persist(t);
  return t;
 }
 
 @PUT
 @Consumes(value =MediaType.APPLICATION_JSON)
 public T update(T t) {
  dao.update(t);
  return t;
 }

 @DELETE
 @Path("{id}")
 @Produces(MediaType.APPLICATION_JSON)
 public Response delete(@PathParam("id") int id) {
  this.dao.delete(dao.findById(id, entity));
  return Response.noContent().build();
 }

}

ကျွှန်တော်တို့ဒီနေရာမှာ သတိထားရမှာက Abstract Class ကို Generics Type အနေနဲ့ရေးထားပါတယ်။ ပြီးတော့ အဲ့ဒီ Class ကို Extends လုပ်တဲ့ Concrete Class မှာ Type Parameter ကို ဖြည့်ပြီး ရေးပါမယ်။ ဥပမာအား ဖြင့် Student Resource အတွက် StudentService Class ကို ရေးမယ်ဆိုရင် StudentService extends AbstractService<Student> လို့ရေးပါမယ်။ Type Parameter အဖြစ်အသုံးပြုထားတဲ့ Student ကို အတွင်းမှာပါတဲ့ Type Parameter နေရာတွေမှာ အစားထိုးအသုံးပြုသွားနိုင်မှာဖြစ်ပါတယ်။

နောက်တစ်ခု သတိထားကြည့်ရမယ့်နေရာက abstract method ဖြစ်တဲ့ setEntity ကိုပါ။ ဒီအထဲမှာ Dao မှာအသုံးပြုမယ့် Entity Class ရဲ့ အမျိုးအစားကို ဖြည့်ပေးဖို့လိုအပ်ပါတယ်။ အဲ့ဒီအတွက် member အနေနဲ့ Class<T> entity လို့ရေသားထားပါတယ်။ entity ရဲ့ တန်ဖိုးကို Concrete Class တွေမှာ အစားထိုးစေလိုတဲ့အတွက် abstract method setEntity ကို ရေးသားထားရခြင်း ဖြစ်ပါတယ်။ အဲ့ဒီ setEntity ကို @PostConstruct Annotation တပ်ထားတဲ့ init method ထဲကနေခေါ်ပါတယ်။ Enterprise Bean တွေကို Container ကနေ လိုအပ်တဲ့အခါမှာ Create လုပ်ပေးမှာဖြစ်တဲ့အတွက် Constructor ကို အသုံးပြုလို့မရပါဘူး။ အဲ့ဒီအတွက် Constructor ထဲမှာ ရေးချင်တဲ့ Logic တွေကို Life Cycle Annotation ဖြစ်တဲ့ PostConstruct Annotation နဲ့ရေးထားတဲ့ Method ထဲမှာ ရေးထားရခြင်း ဖြစ်ပါတယ်။ AbstractService ကို Extends လုပ်ထားတဲ့ Concrete Class ထဲမှာ setEntity ကို Override လုပ်ရေးထားပြီး၊ အတွင်းမှာ entity ကို အသုံးပြုမယ့် Class နဲ့ အစားထိုးထားမယ်ဆိုရင် အဲ့ဒီ Concrete Class ကို Construct လုပ်ပြီးတာနဲ့ entity ရဲ့တန်ဖိုးကို အခြား method တွေကနေ အသုံးပြုနိုင်မှာဖြစ်ပါတယ်။

ကျန်တာတွေကတော့ Resource တွေမှာ အသုံးပြုလိုတဲ့ CRUD Method တွေဖြစ်ပါတယ်။ Reference အတွက် အားလုံးကို ရှာမယ့် getAll method နဲ့ findById ကို ရေးသားထားပါတယ်။

getAll ရဲ့ Return Type ကတော့ List<T> ဖြစ်ပါတယ်။ ပြီးတော့ @GET Annotation ကို အသုံးပြုထားပါတယ်။ သက်ဆိုင်ရာ Resource ရဲ့ URI ကို HTTP Get method နဲ့ Request လုပ်လာရင် အဲ့ဒီ Resource အားလုံးကို ပြန်လည် ရရှိမှာဖြစ်ပါတယ်။ နောက်ပြီး ဒီအပလီမှာ Representation Type အနေနဲ့ JSON ကို အသုံးပြုချင်တဲ့အတွက် @Produces Annotation ရဲ့ Parameter မှာ MediaType.APPLICATION_JSON လို့ ရေးသားထားပါတယ်။

နောက်Method တစ်ခုကတော့ findById ပါ။ Argument အနေနဲ့ Integer id ကို အသုံးပြုထားပြီး၊ Return Type ကတော့ Generices Type ဖြစ်တဲ့ T ပါ။ Parameter အဖြစ် id ရဲ့တန်ဖိုးကို ပေးတာနဲ့ သက်ဆိုင်ရာ Object ကို ရရှိမှာဖြစ်ပါတယ်။ ဒီနေရာမှာ သတိပြုစေလိုတာက အဲ့ဒီ id ကို ဘယ်က ယူမလဲဆိုတဲ့အချက်ပါပဲ။ အဲ့ဒီ id ကို Path Parameter ကနေရယူလိုတဲ့အတွက် @Path("{id}") လို့ Path ကို ရေးထားပါတယ်။ ဥပမာအားဖြင့်  Student Resource ကို သုံးတဲ့နေရာမှာ /student/1 လို့ရေထားရင် 1 အား id အဖြစ်ရရှိမှာ ဖြစ်ပါတယ်။ အဲ့ဒီလို ရယူနိုင်ရန်အတွက်လည်း Argument id ရှေ့တွင် @PathParam("id") Integer id လို့ရေသားထားရခြင်းဖြစ်တယ်။ ထိုကဲ့သို့ရေးထားပါက Argument id ၏တန်ဖိုးအား Path Parameter id မှ ရယူသွားမည် ဖြစ်ပါသည်။

ကျန်တဲ့ Method တွေကတော့ create, update နဲ့ delete တို့ပါပဲ။ ၎င်းတို့ကိုလည်း သက်ဆိုင်ရာ HTTP POST, PUT နဲ့ DELETE တို့ကို အသုံးပြုထားပါတယ်။

အသုံးပြုလိုတဲ့ Resource Class တွေမှာ အထက်ပါ AbstractService ကို extends လုပ်ရေးရုံနဲ့ အထက်ပါ CRUD method တွေကို အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။နောက်ရက်များမှပဲ Resources တွေအတွက် Concrete Class တွေကို ရေးသားပါတော့မယ်။

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

3 comments:

  1. နားေတာ့မလည္ဘူး ဒါေပမဲ့ ယခုလိုေစတနာထားတာကိုေလးစားပါတယ္

    ReplyDelete
  2. I will write the concrete class for this abstract class. In this post I will explain more details. Anyway thank you for reading my blog.

    ReplyDelete
  3. အခုလိုသင်ဦကားေပးတဲဥအတွက်ေကျးဇူးတင်ပါတ,်

    ReplyDelete