Day 19 : GreenDAO

Day 19 : GreenDAO Cover Image

สวัสดีครับ บทความนี้เป็นบทความที่ 19 แล้วนะครับ ที่ผมจะมาเขียน ในซีรีย์ Learn 30 Android Libraries in 30 days

สำหรับบทความทั้งหมด อ่านได้จากด้านล่างครับ

สำหรับวันนี้ขอนำเสนอเรื่อง greenDAO เป็น ORM Tool ตัวหนึงบน Android ครับ (ORM คือ Object Relational Mapping) มันคือการ Mapping ตัว Java class ของเรา กับ SQLite ผ่าน Interface

GreenDAO คืออะไร?

GreenDAO

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

ORM คืออะไร?

Step 1 : Installation

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

  1. GreenDAO : เป็น Library ของ GreenDAO ธรรมดา
  2. DaoGenerator : จะเป็นตัวเอาไว้ Generate ให้กลายเป็น ORM ให้เรานะครับ

Step 2 : Create Generator Module

ฉะนั้นขั้นตอนแรก เราจะต้องมาทำตัว Generate กันก่อนครับ เริ่มด้วยการสร้าง Module ขึ้นมาใหม่ 1 โมดูล คลิ๊กขวาที่โปรเจ็ค => ์New => Module

Module

Module2

ผมทำการตั้งชื่อโมดูลว่า DaoGenerator จะเห็นว่า มี folder มาอยู่ใน Project ของเราแล้วครับ

Dao

ลองตรวจสอบไฟล์ settings.gradle ว่าได้ include โมดูลที่เราสร้างไปหรือยัง ต้องเป็นแบบนี้ (app คือชื่อโมดูลหลักของผมนะครับ ตัวโปรเจ็คนั่นแหละ)

include ':app', ':DaoGenerator'

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

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

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

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile('de.greenrobot:DaoGenerator:1.3.0')
    }

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

    run {
        args outputDir
    }
}

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

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

Step 3 : Create Generator Class

ต่อมาทำการสร้างคลาสสำหรับ Generate ชื่อว่า MyDaoGenerator.java ขึ้นมา

package com.devahoy.learn30androidlibraries.daogenerator;

import java.io.File;

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.learn30androidlibraries");
        Entity player = schema.addEntity("Player");

        player.addIdProperty();
        player.addStringProperty("name");
        player.addStringProperty("club");

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

โค๊ดด้านบน ผมทำการสร้าง Schema ขึ้นมา เป็นเหมือนการสร้าง Database ขึ้นมา้ก้อนนึง ชื่อว่า com.devahoy.learn30androidlibraries และเป็นเวอร์ชัน 1 ต่อมาผมสร้าง Entity ที่ชื่อว่า Player ส่วนนี้โค๊ดจะถูก generate ไปเป็นคลาส Player และ PlayerDAO โดยภายในคลาสโมเดล Player มี 3 field คือ Id, Name และ Club ครับ สุดท้าย สั่ง DaoGenerator().generateAll() โดยระบุเป็น args[0] คือ outputDir ที่เซตไว้ใน build.gradle

ต่อมา กดที่แท็ป Gradle อยู่ขวามือๆ แล้วมองหา :DaoGenerator แล้วดับเบิลคลิกที่ script run จะได้ดังภาพ

Gradle

ไฟล์จะถูก generate ไปที่โฟลเดอร์ที่ระบุไว้

Gradle2

ที่นี้ก็ลองไปดูที่โฟลเดอร์ app/main/src/java-gen จะเห็นคลาสที่ถูก generate มาเต็มเลย ในส่วนนี้แหละครับ ที่เราจะเอาไปใช้ในโปรเจ็คของเรา สำหรับส่วน Generate ก็หมดหน้าที่เพียงเท่านี้ :D

Java Gen

Step 4 : Setup greenDAO Project

หลักจากที่เรา generate เรียบร้อยแล้ว ต่อมาก็ต้องมา setup ตัวโปรเจ็คของเราบ้าง ให้ทำการเปิดไฟล์ build.gradle แล้วเพิ่ม dependencies ของ greenDao ลงไป (คนละตัวกับตัว generate นะครับ)

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

android {
    sourceSets {
        main {
            java {
                srcDir 'src/main/java'
                // for greenDAO
                srcDir 'src/main/java-gen'
            }
        }
    }
}
dependencies {
    compile 'de.greenrobot:greendao:1.3.7'
}

ทำการ Sync Gradle เป็นอันเรียบร้อย

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

Step 5 : Create Database

ต่อมาทำการสร้าง GreenDaoApplication.java ขึ้นมาก่อนครับ เพื่อเอาไว้สร้าง Database โดยทำการ extends Application ดังนี้

package com.devahoy.learn30androidlibraries.day19;

import android.app.Application;
import android.database.sqlite.SQLiteDatabase;

import com.devahoy.learn30androidlibraries.DaoMaster;
import com.devahoy.learn30androidlibraries.DaoSession;

public class GreenDaoApplication extends Application {

    DaoSession mDaoSession;

    @Override
    public void onCreate() {
        super.onCreate();
        setupDatabase();
    }

    private void setupDatabase() {
        DaoMaster.DevOpenHelper helper =
                new DaoMaster.DevOpenHelper(this, "MyGreenDao.db", null);
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        mDaoSession = daoMaster.newSession();
    }

    public DaoSession getDaoSession() {
        return mDaoSession;
    }
}

อธิบายโค๊ดด้านบน new DaoMaster.DevOpenHelper() เปรียบเสมือนการใช้งาน SQLiteOpenHelper ครับ เราไม่ต้องมานั่งทำการ implement แล้ว สั่ง CREATE TABLE เลยครับ ตัว greenDAO มันทำให้เราเรียบร้อยแล้ว โดยใช้ DaoMaster สุดท้ายมีเมธอด getDaoSession() เพื่อทำการส่ง DaoSession อันนี้จะเอาไปใช้ใน Activity ครับ ก่อนที่เราจะทำการ insert, update, delete เราต้องมี Session ก่อน

Session คืออะไร? อ่านเพิ่มเติม

เมื่อเราทำการสร้าง Custom Application เราจำเป็นต้องไปตั้งค่าที่ไฟล์ AndroidManifest.xml ด้วยครับ ตรงส่วน android:name=".GreenDaoApplication"> ดังนี้

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.devahoy.learn30androidlibraries" >
    <application
        ...
        android:name=".day19.GreenDaoApplication">
        <activity>
            ...
        </activity>
    </application>

</manifest>

Step 6 : Create Activity

สุดท้ายละครับ ทำการสร้าง Activity ขึ้นมา โดยตัวอย่าง ผมจะให้มันแสดงข้อมูลจาก ฐานข้อมูลแบบ List ง่ายๆครับ

package com.devahoy.learn30androidlibraries.day19;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

import com.devahoy.learn30androidlibraries.DaoSession;
import com.devahoy.learn30androidlibraries.Player;
import com.devahoy.learn30androidlibraries.PlayerDao;

import java.util.ArrayList;
import java.util.List;

public class GreenDAOActivity extends ListActivity {

    ArrayList<String> mDataset = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mDataset);
        setListAdapter(adapter);
    }
}

ด้านบนผมจะแค่โชว์ ข้อมูลเป็นลิสท์เฉยๆ ทำให้ใช้แค่ ListActivity ก็พอครับ ส่วนข้อมูล mDataset นี่แหละครับ ที่จะเอามาจากฐานข้อมูล มาดูวิธีการกันดีกว่า ว่าจะเรียกข้อมูลได้ยังไง?

Insert

การ Insert ข้อมูลทำได้ผ่านตัว PlayerDAO ครับ (อันนี้ถ้าตรง generate เราเซตค่า Entity เป็นชื่ออื่น มันก็จะเป็นชื่อนั้นๆ + DAO นะครับ)

Player player = new Player(1L, "my name", "my club");
PlayerDAO.insert(player);

READ / QUERY

การ Query ข้อมูล ก็ทำผ่าน PlayerDAO เช่นกันครับ แบบนี้

PlayerDAO.loadAll();  
PlayerDao.load(key); // query แบบระบุ id

UPDATE

การ update ก็ทำเหมือนกับการ insert เลย คือใช้ Player เป็น parameter ครับ

oldPlayer;
PlayerDAO.update(oldPlayer);

DELETE

การ delete มีให้เลือกแบบ deleteAll() คือลบทั้งหมด ลบแบบระบุออปเจ็ค และลบแบบระบุ id

PlayerDAO.delete(player);
PlayerDAO.deleteByKey(id);
PlayerDAO.deleteAll();

สำหรับวิธีการ CRUD กับ SQLite ก็มีประมาณนี้ครับ สุดท้ายผมนำ Insert มาใช้อย่างเดียว ได้แบบนี้

package com.devahoy.learn30androidlibraries.day19;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

import com.devahoy.learn30androidlibraries.DaoSession;
import com.devahoy.learn30androidlibraries.Player;
import com.devahoy.learn30androidlibraries.PlayerDao;

import java.util.ArrayList;
import java.util.List;

public class GreenDAOActivity extends ListActivity {

    GreenDaoApplication mApplication;
    DaoSession mDaoSession;
    PlayerDao mPlayerDao;
    ArrayList<String> mDataset = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mApplication = (GreenDaoApplication) getApplication();
        mDaoSession = mApplication.getDaoSession();

        initSampleData();

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mDataset);
        setListAdapter(adapter);
    }

    void initSampleData() {
        mPlayerDao = mDaoSession.getPlayerDao();

        mPlayerDao.deleteAll();

        Player messi = new Player(1L, "Leonel Messi", "FC Barcelona");
        Player ronaldo = new Player(2L, "Cristiano Ronaldo", "Real Madrid");
        Player gerrard = new Player(3L, "Steven Gerrard", "Liverpool");
        Player persie = new Player(4L, "Robin Van Persie", "Man Utd");
        Player teerasil = new Player(5L, "Teerasil Dangda", "UD Almeria");

        mPlayerDao.insert(messi);
        mPlayerDao.insert(ronaldo);
        mPlayerDao.insert(gerrard);
        mPlayerDao.insert(persie);
        mPlayerDao.insert(teerasil);

        mDataset = new ArrayList<String>();

        List<Player> players = mPlayerDao.loadAll();
        for (Player player : players) {
            mDataset.add("Name : " + player.getName() + "\nClub : " + player.getClub());
        }
    }
}

จากโค๊ดด้านบน getApplication() เป็นการเรียก Application ที่แอพนี้ใช้อยู่ครับ นั่นก็คือ GreenDaoApplication และก็ getDaoSession() เพื่อทำการเรียก DaoSession มาก่อนครับ ถึงจะทำการ getPlayerDao() ได้ จะเห็นว่าผมใช้ PlayerDAO.insert() เพื่อทำการเพิ่มข้อมูลลงฐานข้อมูล ใช้ PlayerDao.loadAll() เพื่อทำการ query ข้อมูลทั้งหมดในฐานข้อมูลมาโชว์ใน ListView และใช้ PlayerDAO.deleteAll() ทำการลบข้อมูลทั้งหมด ทุกครั้งที่เปิดแอพครับ ส่วน update ไม่ได้ทำครับ :D

Result

สรุป

ส่วนตัว ผมก็เพิ่งมาเคยใช้ ORM ใน Android ก็วันนี้แหละครับ ปกติใช้แต่ SQLiteOpenHelper ส่วน ORM ผมเคยใช้แต่บน Java Spring หลักจากใช้ greenDAO ลองเล่นกับมันดูราวๆ 3-4 ชม. (หมดไปกับการติดตั้ง) แล้วรู้สึกว่าติดใจครับ ใช้งานง่าย ไม่ต้องไปทำการสร้าง SQL Syntax ไม่ต้องไป get Cursor เอง เรียกผ่าน Interface ที่ greenDAO เตรียมไว้ให้ได้เลย สำหรับใครที่ผ่านมาอ่าน ก็ลองนำไปใช้ดูนะครับ สุดท้ายโปรเจ็คอัพเดทล่าสุดครับ 30-android-libraries-in-30-days

Reference

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

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