ขั้นตอนการใช้งาน greenDAO บน Android (ภาคล่าสุด)


บทความนี้เป็นบทความ rewrite จากบทความเก่านะครับ Day 19 : GreenDAO เนื่องจากว่าบทความค่อนข้างเก่า และทำในซีรีย์ Learn 30 Android Libraries in 30 Days ซึ่งก็บอกอยู่แล้วว่าใช้เวลาศึกษาแค่วันเดียว ฉะนั้นก็อาจจะมีผิดพลาดบ้าง บทความนี้ผมก็เลยถือโอกาสอัพเดท ให้มันล่าสุด โดยลองรับกับ Android Studio เวอร์ชัน 1.1 (เพิ่งอัพเดทวันนี้)

โดยที่ผมเขียนวันนี้ ก็อ้างอิงจากบทความเก่านะ ทำเฉพาะส่วนที่ add library และ generator ส่วนโค๊ด การใช้งาน ORM เหมือนเดิมจากบทความเก่าทุกอย่าง :D

greenDAO คืออะไร?

greenDAO

greenDAO เป็น Open Source Project ที่ช่วยการทำงานร่วมกับ SQLite ในการ CRUD (CREATE, READ, UPDATE, DELETE) ทำได้ง่ายมากครับ โดยผ่าน ORM (Object Relational Model) คือใช้คลาส Java ของเรา mapping กับ model ของฐานข้อมูล SQLite โดยมี Dao Interface เป็นตัวเชื่อมต่ออีกที เช่นใน Java เรามีคลาส Person มี name และ age เราก็ใช้แค่ setName(), setAge() แล้วก็เซฟลงฐานข้อมูลโดยใช้ Dao Interface ก็ได้เลย ไม่ต้องมาเสียเวลาโยน ContentValues แบบ pure SQLite แล้วไอ้เจ้า ORM มันคืออะไร? หลายคนคงจะสงสัย ไปหาอ่านเพิ่มเติมกันเองนะครับ ORM คืออะไร?

greenDAO library Overview

การติดตั้ง greenDAO จะมีด้วยกัน 2 ตัวนะครับ นั่นก็คือ

  • de.greenrobot:greendao:1.3.7 : เป็น Library ของ GreenDAO ธรรมดา
  • de.greenrobot:DaoGenerator:1.3.0 : จะเป็นตัวเอาไว้ Generate ให้กลายเป็น ORM ให้เรานะครับ Library ของ Gradle คือ

โดยขั้นตอนการใช้งานของ greenDAO คือ

  1. ทำการสร้างโมดูล daoGenerator ขึ้นมา
  2. สร้างคลาสโมเดลต้นแบบของเราขึ้นมา
  3. ใช้ daoGenerator ทำการแปลงโมเดลของเรา ให้อยู่ในรูปแบบ ORM พร้อมใช้งาน
  4. เพิ่ม library ของ greenDAO (คนละตัวกับ greenDAO Generator)
  5. นำ ORM ของเราไปใช้งานได้ Insert/Update/Delete สบายๆ ~~

Step 1 : สร้าง daoGenerator

ขั้นตอนแรก ทำการสร้าง daogenerator ขึ้นมา (ผมใช้ตัวพิมพ์เล็กทั้งหมดนะครับ กันสับสนกับตัวต้นแบบ) โดยเปิดไปที่ File => New Module จากนั้นเลือก Java Library

Java Library

Create Library name

  • Library name : ใส่ชื่อโมดูลของเรา
  • java package name: ใส่ชื่อ package name ของโมดูล
  • java class name : ใส่ชื่อคลาสหลัก ที่จะเอาไว้ทำโมเดลต้นแบบ

เมื่อทำการสร้างโมดูลเสร็จ จะเห็นว่ามีโมดูลใหม่เพิ่มขึ้นมาในโปรเจ็คแล้ว

หากดูในมุมมอง Android จะเห็นแบบนี้

Android View

หากดูในมุมมอง Project ก็จะเห็นแบบนี้

Project View

ต่อมาเปิดไฟล์ build.gradle ในโมดูล daogenerator ขึ้นมา แล้วแก้ไขไฟล์เป็นแบบนี้

project(':daogenerator') {
    apply plugin: 'application'
    apply plugin: 'java'

    mainClassName = "com.devahoy.daogenerator.MyDaoGenerator"
    def outputDir = "../app/src/main/java-gen"

    dependencies {
        compile('de.greenrobot:DaoGenerator:1.3.0')
    }

    task createDocs {
        def docs = file(outputDir)
        docs.mkdirs()
    }

    run {
        args outputDir
    }
}

อธิบายโค๊ดด้านบน

  • mainClassName : เป็นคลาสที่เราจะทำการ generate ก็คือคลาสต้นแบบของโมเดลของเรา (ตอนนี้คลาสนี้ยังไม่ได้สร้าง แต่ว่าประกาศใช้แบบนี้ไปก่อน โดยสิ่งที่ต้องเปลี่ยนไม่ให้เหมือนผมคือ package name ส่วน MyDaoGenerator จะใช้ชื่อเดียวกันก็ได้
  • outputDir : เป็น path ของ folder ที่เราจะให้ generate ไป ตัวอย่างคือจะ gen ไปที่โมดูล app/src/main/java-gen ครับ app คือชื่อโมดูลแรกเวลาสร้างโปรเจ็ค Android หากใครเป็นชื่ออื่นก็เปลี่ยนตามด้วย
  • run() : เป็น gradle script เอาไว้สั่งรัน โดยมี argument เป็น outputDir ที่ประกาศไว้

Step 2 : Create Generator Class

ต่อมาถึงคิวที่ต้องสร้างตัวคลาส Generator แล้ว โดยการสร้างคลาสขึ้นมาใหม่ ยังอยู่ในโมดูล daogenerator ผมทำการตั้งชื่อว่า MyDaoGenerator

package com.devahoy.daogenerator;

import de.greenrobot.daogenerator.DaoGenerator;
import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Schema;

public class MyDaoGenerator {

    public static void main(String[] args) throws Exception {
        Schema schema = new Schema(1, "com.devahoy.daogenerator");
        Entity player = schema.addEntity("Player");

        player.addIdProperty();
        player.addStringProperty("name");
        player.addStringProperty("club");
        player.addIntProperty("age");
        player.addStringProperty("nationality");

        new DaoGenerator().generateAll(schema, args[0]);
    }
}

Note: ดูด้วยว่า import package ของ Schema และ Entity ถูกต้องหรือไม่?

โค๊ดด้านบน ผมทำการสร้าง Schema ขึ้นมา เป็นเหมือนการสร้าง Database ขึ้นมาก้อนนึง ชื่อว่า com.devahoy.daogenerator และเป็นเวอร์ชัน 1 หากคุณเคยเขียน SQLiteOpenHelper มาก่อน จะคุ้นกับการสร้าง constructor แบบนี้

public class MySQLiteHelper extends SQLiteOpenHelper {
...
    public MySQLiteHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
...
}

ต่อมาผมสร้าง Entity ที่ชื่อว่า Player ส่วนนี้โค๊ดจะถูก generate ไปเป็นคลาส Player และ PlayerDAO สุดท้ายสั่งรันสคริป Gradle เพื่อให้มัน generator โดยกดที่แท็ป Gradle อยู่ขวามือๆ แล้วมองหา :DaoGenerator แล้วดับเบิลคลิกที่ script run จะได้ดังภาพ

Run script gradle

หรือทำผ่าน Terminal ของ Android Studio โดย change directory ไปที่โมดูล daogenerator แล้วสั่งรันสคริป run()

../GreenDaoReturn/app$ .. 
../GreenDaoReturn$ cd daogenerator
../GreenDaoReturn/daogenerator$ gradle run

จะได้ผลลัพธ์ลักษณะนี้ มีคลาส 4 ตัวที่ถูกสร้างขึ้นในโมดูล app โมดูลหลักของเรา

greenDAO Generator                
Copyright 2011-2013 Markus Junginger, greenrobot.de. Licensed under GPL V3.
This program comes with ABSOLUTELY NO WARRANTY
Processing schema version 1...    
Written ../GreenDaoReturn/app/src/main/java-gen/com/devahoy/daogenerator/PlayerDao.java
Written ../GreenDaoReturn/app/src/main/java-gen/com/devahoy/daogenerator/Player.java
Written ../GreenDaoReturn/app/src/main/java-gen/com/devahoy/daogenerator/DaoMaster.java
Written ../GreenDaoReturn/app/src/main/java-gen/com/devahoy/daogenerator/DaoSession.java
Processed 1 entities in 116ms 

Step 3 : เพิ่ม Library greenDAO

ตรงส่วน greenDao Generator เรียบร้อยแล้ว ต่อมาจะเป็นตัว core ของ greenDAO กันบ้าง โดยการเพิ่ม library ผ่าน gradle ง่ายๆ ด้วยการเปิด build.gradle ในโมดูล app แบบนี้

dependencies {  
    compile 'de.greenrobot:greendao:1.3.7'
}

ถัดมาทำการ setup SourceSets ด้วยครับ เนื่องจาก default มันรู้จักแค่ src/main/java ไม่ได้รู้จักโฟลเดอร์ src/main/java-gen ที่เราทำการสร้างมาใหม่ ต้องไปบอกให้มันรู้ครับ โดยเพิ่มโค๊ดไปใน block android ดังนี้

SourceSets คืออะไร? ดูที่นี่

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
            // for greenDAO
            srcDir 'src/main/java-gen'
        }
    }
}

ทำการกด Sync Now 1 ที สุดท้ายไฟล์ build.gradle ในโมดูล app จะเป็นแบบนี้

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "com.devahoy.greendaoreturn"
        minSdkVersion 14
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
                // for greenDAO
                srcDir 'src/main/java-gen'
            }
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile 'de.greenrobot:greendao:1.3.7'
}

ส่วนอื่นๆ เริ่มทำตาม Step 5 ของบทความเก่าได้เลยครับ (มีการเพิ่ม Schema ขึ้นมาเล็กน้อย) Day 19 : GreenDAO

Workshop

จริงๆแล้วบทความถ้า จำมือทำไปเลย คนที่ได้ประโยชน์ที่สุดคงเป็นแค่ผมคนเดียวละมั้ง เพราะได้คิดเอง ได้ทำเอง ประยุกต์เอง ส่วนคนอ่าน ก็แค่อ่าน แล้วทำตาม สิ่งที่ได้คุณได้โปรเจ็คจริงๆ นั่นแหละ แต่บางคนเข้าใจไหมที่ทำตามคืออะไรบ้าง ฉะนั้นผมเลยฝาก workshop เล็กๆน้อยๆ ให้ไปทำเพิ่มเติมดูครับ ไม่ทำไม่เป็นไร ไม่ได้มีผลกระทบอะไรกับผมอยู่แล้ว แต่ถ้าทำ มันจะมีประโยชน์แก่ตัวผู้ทำเองครับ :D

  1. จากบทความเก่า Schema มีแค่ name กับ club แต่อันใหม่มี age และ nationality เพิ่มขึ้นมา คุณสามารถประยุกต์ใช้ได้หรือไม่?

  2. เปลี่ยนจาก ArrayAdapter เป็น Custom Adapter ได้หรือไม่?

  3. ไม่อยากเพิ่มข้อมูลในโปรแกรมอะ ทำหน้า AddData เพิ่มขึ้นมา เพื่อเอาไว้ใส่ข้อมูล

  4. อยากให้มีปุ่ม Add เพิ่มเข้าไปบน Menu เพื่อเปิดหน้า AddData หรือจะทำ floating button แบบ Lollipop ดี :D

  5. อยากแก้ไขข้อมูลทำไงดี? เพิ่มปุ่ม Edit โดยเปิดไปหน้า AddData นั่นแหละ แต่ส่งข้อมูลเก่าไปด้วย

  6. อยากให้ลบข้อมูลได้ด้วย โดยเมื่อคลิกที่ ListView ค้างไว้ จะมี Dialog ขึ้นมาให้ยืนยันว่าจะลบหรือไม่

จริงๆมันก็เกี่ยวกับฐานข้อมูล SQLite ฉะนั้นถ้าหากว่าอ่านบทความ 2 บทความนี้ประกอบ และเข้าใจ ทำได้แน่นอนครับ

Happy Coding <3

Chai Chai Phonbopit : Web Developer @Nimbl3 • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Node.js, JavaScript และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด