woong's

Android ORM 소개 & ORMLite 사용하기 본문

Develop/Android

Android ORM 소개 & ORMLite 사용하기

dlsdnd345 2016. 2. 13. 23:21

Android ORM 소개 & ORMLite 사용하기


ORM

ORM은 논쟁을 끌고 다니는 기술

지지자 - ORM을 활용하면 생산성이 높아지고 캐시 등 다양한 저장소를 활용하기에 유연한 구조를 만들어 준다는 장점을 내세웁니다.

회의론자 - 초기에 배워야 할 지식이 많아서 생산성이 단기에는 나타나지 않으며, 숙련된 개발자만이 원하는 SQL을 유도할 수 있기 때문에 대용량 데이터를 다루기에는 실용적이지 않다고 주장

 

Android ORM


다행히 모바일 단말기의 개발 환경에서는 ORM 회의론자들의 걱정거리가 줄어들음.

  • 첫째, 단말기의 데이터베이스에서는 대용량 데이터를 다루지 않는다.
    - 기기마다 가지고 있는 데이터베이스인 SQLite에는 사용자 한 명만을 위한 데이터가 주로 들어가고, 그런 데이터가 수백만 건이 되는 경우는 거의 없다.
  • 둘째, 데이터베이스의 스키마가 상대적으로 단순해서 객체 매핑 작업의 부담이 덜하다.
    - 저장해야 할 엔티티의 수가 서버에 비하면 적은 편이고, 엔티티의 관계가 복잡하게 얽혀있는 경우도 많지 않다.
  • 셋째, 객체를 다양한 방식으로 저장하고 데이터베이스의 비중이 절대적으로 높지 않다.
    -Android에서는 파일, SharedPreference, 데이터베이스에 데이터를 저장할 수 있다.

aBatis


ORM의 범주로 분류하기에는 논란
iBatis나 MyBatis의 인기가 높은 편이기 때문에 참고삼아 포함

iBatis처럼 쿼리를 res/values/sqlmaps.xml 파일에 저장한다.

 

 

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="getCategoryById">
        SELECT * FROM category WHERE id = #id#;
    </string>
</resources>
 

 

 

 

1
2
3
4
5
6
public Note get(long noteId) {
    Map<String, Object> params = new HashMap<String, Object>();
    params.put(ID, noteId);
    return abatis.executeForBean("getNoteById", params, Note.class);
}
 

 

 

테이블의 칼럼명과 Java 객체의 멤버변수명의 매핑은 관례에 따라서 알아서 처리한다. 'user_id' 칼럼을 'userId'로 매핑하는 변환도 자동으로 지원된다.

 

 

ActiveAndroid

 

Ruby on Rails처럼 Active Record 패턴을 지원하는 라이브러리이다. AndroidManifest.xml 파일에 데이터베이스 이름과 버전을 선언하고, Application 클래스에서 ActiveAndroid의 initialize 메서드와 dispose 메서드를 호출해야 한다.

@Table과 @Column 애노테이션을 사용해서 엔티티 클래스를 정의하면 데이터베이스에 테이블이 생성된다. 엔티티로 사용할 클래스는 <예제 4>와 같이 Model 클래스를 상속하고 애노테이션으로 매핑 정보를 기술한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
@Table(name = "Note")
public class Note extends Model {
     @Column(name = "title")    private String title;
     @Column(name = "contents")    private String contents;
     @Column(name = "date")    private java.util.Date date;
     @Column(name = "categoryId")     private Long categoryId;
 
    // To One Relationship
    @Column(name = "category", onDelete = ForeignKeyAction.CASCADE)
    private Category category;
//  GETTER, SETTER 생략
}
@Table(name = "Category")
public class Category extends Model {
    @Column(name = "name")  private String name;
    @Column(name = "categoryinfo")  private String categoryInfo;
 ...
    // To-many Relationship
    public List<Note> getNote() {
        return getMany(Note.class"category");
    }
}

 

 

 

Category와 Note는 1:N 관계이다. Note는 하나의 Category에 속할 수 있으며 이를 클래스에서 참조로 구현한다. @Column(name = "category", onDelete = ForeignKeyAction.CASCADE)에서 볼 수 있듯이 ActiveAndroid는 Cascade delete mode를 지원한다. iOS의 Core Data에서는 기본적으로 릴레이션을 설정할 때 Delete Mode를 설정하는 기능을 제공하지만, Android에서는 ActiveAndroid가 유일하게 이를 지원한다.

Category 클래스에서는 상위클래스인 'Model'에 정의된 getMany 메서드를 사용해 사용해 To Many 관계에 있는 레코드를 손쉽게 얻을 수 있다. getMany 메서드는 Select 클래스 객체를 사용해 릴레이션 관계에 있는 레코드를 로딩하는데, 별도의 캐싱 기법 없이 매번 쿼리문을 수행하고 결과를 메모리로 로딩한다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class NoteDaoActiveAndroid implements NoteDaoInterface {
    @Override
    public void insert(Note note) {
        note.save();
    }
     @Override
    public Note get(long noteId) {
        return new Select().from(Note.class).where("Id = ?", noteId).executeSingle();
    }
     @Override
    public void delete(Note note) {
        note.delete();
    }
 

 

입력, 조회, 삭제 구현

 

 

Android-Active-Record

 

 마찬가지로 Active Record 패턴을 구현한 라이브러리이다. 데이터베이스 테이블을 생성하려면 사용자가 수동으로 코드를 추가해야 한다는 단점이 있다

 

 Colored By Color Scripter

1
2
3
4
5
6
7
8
9
public class Note extends ActiveRecordBase {    public String title;
    public String title
    public String contents;
    public long categoryId;
...
    // Primary Key는 ActiveRecordBase로부터 상속받는다.
    public long getId() {
        return _id;
}

 

 

 엔티티 클래스는 ActiveRecordBase를 상속받아야 한다. ActiveRecordBase에는 insert, update, find 등의 메서드가 미리 구현되어 있어서 DAO 같은 클래스 없이 Model 객체만으로 CRUD 기능을 사용한다. 테이블의 Primary Key가 되는 _id는 ActiveRecordBase에 _id로 선언되어 있고 getID라는 Accessor 메서드가 정의되어 있다. 엔티티 간의 관계를 설정하는 기능은 제공되지 않는다.

 

 

1
2
3
4
5
6
7
8
9
 
public void insert(Note note)  throws ActiveRecordException {
    note.setDatabase(database);
    note.save();
}
public List<Note> getByTitleQuery(final String query) )  throws ActiveRecordException {
        return  activeRecordBase.find(Note.class"title LIKE ?"new String[]{"%"+query+"%"});
}
 

 

 

ActiveRecord는 ActiveRecordException이라는 Exception을 발생시키는데 이는 Checked Exception이기 때문에 try-catch 구문을 사용하여 처리할 것을 권장

 

ORMLite

 

Android ORM 라이브러리 중 가장 강력한 기능을 제공
ORMLite만이 유일하게 일반 Java 환경과 Android 환경 모두에서 사용할 수 있다.

 

1
2
3
4
5
6
7
8
9
@DatabaseTable
public class Category extends BaseDaoEnabled<Category, Long> {
    @DatabaseField(generatedId = true)    private Long Id;
    @DatabaseField(canBeNull = false)    private String name;
    @DatabaseField    private String categoryInfo;
    @ForeignCollectionField   private ForeignCollection<Note> note;
 // getter, setter 생략
}
 

 

 

 

Foreign Key를 지원하며 To-Many Relationship에서는 Collection 인터페이스를 상속받는 ForeignCollection를 사용하여 자신을 참조하는 엔티티 객체를 담을 수 있다. ORMLite는 가장 많은 애노테이션을 지원한다. 인덱싱이나 참조하는 객체가 변경되었을 때 이를 반영하는 옵션도 애노테이션으로 설정할 수 있다.

데이터베이스의 스키마 생성은 <예제 10>과 같이 OrmLiteSqliteOpenHelper의 하위 클래스의 onCreate 메서드와 onUpgrade 메서드 안에서 사용자가 구현해야 한다. onUpgrade 메서드는 사용자가 커스터마이징할 수 있으므로 Raw Query를 사용하여 마이그레이션을 수행할 수 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
    private static final String DB_NAME = "Note.db";
    private static final int DB_VERSION = 1;
    private Dao<Category, Long> categoryDao = null;
    private Dao<Note, Long> noteDao = null;
    public DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
 
    @Override
    public void onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource) {
        try {
            TableUtils.createTable(connectionSource, Category.class);
            TableUtils.createTable(connectionSource, Note.class);
            onCreate(sqliteDatabase, connectionSource);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
    public Dao<Category, Long> getCategoryDao() throws SQLException {
        if (categoryDao == null)
            categoryDao = getDao(Category.class);
        return categoryDao;
    }
 
    public Dao<Note, Long> getNoteDao() throws SQLException {
        if (noteDao == null)
            noteDao = getDao(Note.class);
        return noteDao;
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource, int oldVer,  int newVer) {
        try {
            TableUtils.dropTable(connectionSource, Category.classtrue);
            TableUtils.dropTable(connectionSource, Note.classtrue);
            onCreate(sqliteDatabase, connectionSource);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
public class NoteDaoOrmLite implements NoteDao {
    private RuntimeExceptionDao<Note, Long> noteDao = null;
 
    public void init(Context context) {
        DatabaseHelper helper = new DatabaseHelper(context);
        noteDao = helper.getNoteDao();
    }
 
    @Override
    public void insert(Note note) {
        noteDao.create(note);
    }
 
@Override
    public List<Note> getByTitleQuery(String query) {
        try {
            return noteDao.queryBuilder().where()
                    .like("title""%" + query + "%").query();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
 

 

 

지원 플랫폼

 


 

 

greenDAO와 ORMLite, stORM은 최근까지 업데이트가 지속적으로 이루어지고 있다. aBatis와 ActiveAndroid, Android-Active-Record는 최종 커밋이 발생한 이래 꽤 오랜 기간 동안 커밋이 없었다.

이중에 가장 좋다고 하는 ORMLite 에 대해서 사용방법을 적어 보겠습니다.

 

 

 

ORMlite

ORMlite 홈페이지 : http://ormlite.com/sqlite_java_android_orm.shtml
영문 설명 사이트 링크 : http://logic-explained.blogspot.kr/2011/12/using-ormlite-in-android-projects.html
sample 프로젝트 : https://github.com/justadreamer/WishListManager

 

ORMlite 준비

http://ormlite.com/sqlite_java_android_orm.shtml 홈페이지를 통해서 jar 파일을 다운로드 합니다.


 

가운데 있는 get started 를 선택합니다.

 

 

ORMLite release page 를 선택 합니다 .

 

core jar , android jar 파일을 다운로드 해서 프로젝트 lib 에 넣습니다.

준비 과정은 끝났습니다 .

이제 코드를 통해서 사용하는 방법에 대해 알아 보겠습니다.

 

ORMlite 사용

 

 Colored By Color Scripter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
 
    private static final String DATABASE_NAME = "ContentListDB.sqlite";
    private static final int DATABASE_VERSION = 1;
 
    private Dao<GroupList, Integer> groupListDao = null;
    private Dao<Content, Integer> contentDao = null;
 
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
 
    @Override
    public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
        try {
            TableUtils.createTable(connectionSource, GroupList.class);
            TableUtils.createTable(connectionSource, Content.class);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource,
            int oldVersion, int newVersion) {
        try {
            TableUtils.dropTable(connectionSource, GroupList.classtrue);
            TableUtils.dropTable(connectionSource, Content.classtrue);
            onCreate(database, connectionSource);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
    public Dao<GroupList, Integer> getGroupListDao() {
        if (null == groupListDao) {
            try {
                groupListDao = getDao(GroupList.class);
            } catch (java.sql.SQLException e) {
                e.printStackTrace();
            }
        }
        return groupListDao;
    }
 
    public Dao<Content, Integer> gerContentListDao() {
 
        if (null == contentDao) {
            try {
                contentDao = getDao(Content.class);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return contentDao;
    }
 
}
 

 

 

SqlLite 를 통해서 데이터베이스를 생성합니다 .
기존 안드로이드 에서 사용하는 SqlLite 와 조금 다른 점이 있습니다.
extends OrmLiteSqliteOpenHelper 상속을 하고 
ORMlite 메서드들 을 이용하여 데이터 베이스 생성및 테이블 수정을 구성하였습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 
@DatabaseTable
public class Content {
 
    @DatabaseField(generatedId = true)
    int id;
    @DatabaseField
    String name;
    @DatabaseField(foreign = true, foreignAutoRefresh = true)
    GroupList list;
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public GroupList getList() {
        return list;
    }
 
    public void setList(GroupList list) {
        this.list = list;
    }
 
}
 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@DatabaseTable
public class GroupList {
 
    @DatabaseField(generatedId = true)
    int id;
 
    @DatabaseField
    String name;
 
    @DatabaseField
    int order;
 
    @ForeignCollectionField
    ForeignCollection<Content> items;
 
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getOrder() {
        return order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    public ForeignCollection<Content> getItems() {
        ArrayList<Content> itemList = new ArrayList<Content>();
        for (Content item : items) {
            itemList.add(item);
        }
        return items;
    }
    public void setItems(ForeignCollection<Content> items) {
        this.items = items;
    }
}

 

@DatabaseTable , @DatabaseField ,@ForeignCollectionField 어노테이션 등을 이용하여
데이터베이스 테이블과 필드를 매칭 작업을 합니다 .

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class DatabaseManager {
    static private DatabaseManager instance;
    private final DatabaseHelper helper;
 
    static public void init(Context context) {
        if (null == instance) {
            instance = new DatabaseManager(context);
        }
        DatabaseHelper helper = new DatabaseHelper(context);
        helper.getGroupListDao();
    }
 
    static public DatabaseManager getInstance() {
        return instance;
    }
 
    private DatabaseManager(Context context) {
        helper = new DatabaseHelper(context);
    }
 
    public DatabaseHelper getHelper() {
        return helper;
    }
 
    public List<GroupList> getAllGroupLists() {
        List<GroupList> wishLists = null;
        try {
            wishLists = getHelper().getGroupListDao().queryForAll();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (java.sql.SQLException e) {
            e.printStackTrace();
        }
        return wishLists;
    }
 
    public void addGroupList(GroupList groupList) {
        try {
            try {
                DatabaseManager.getInstance().getHelper().getGroupListDao().create(groupList);
            } catch (java.sql.SQLException e) {
                e.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
    public void updateGroupList(GroupList groupList) {
        try {
            try {
                DatabaseManager.getInstance().getHelper().getGroupListDao().update(groupList);
            } catch (java.sql.SQLException e) {
                e.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
    public List<GroupList> getGroupList() {
        List<GroupList> groupList = null;
        groupList = DatabaseManager.getInstance().getAllGroupLists();
        return groupList;
    }
 
    public GroupList getGroupListWithId(int groupListId) {
        GroupList groupList = null;
 
        try {
            try {
                groupList = DatabaseManager.getInstance().getHelper().getGroupListDao().queryForId(groupListId);
            } catch (java.sql.SQLException e) {
                e.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return groupList;
    }
 
}
 

 

데이터 베이스 생성 및 매핑작업을 마치고 , 사용할 구문을 ORMLite 를 통해서 만들어 줍니다 .
addGroupList , updateGroupList , getGroupList , getGroupListWithId 메서드를 보시면
ORMLite 문법을 통해서 만든것을 볼수 있습니다 .

문법에 대해서 자세한 정보는 홈페이지 가면 자세히 나와있습니다 .

이제 마지막으로 Manager 를 구성한 메서드를 사용하면 되겠습니다 .

Activity 에서 사용한 예제 입니다 .

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MainActivity extends Activity {
 
    EditText editText;
    TextView textView;
    String names;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        DatabaseManager.init(this);
 
        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.textView);
    }
 
    public void addGroupList(View view) {
        GroupList groupList = new GroupList();
        groupList.setName(editText.getText().toString());
        DatabaseManager.getInstance().addGroupList(groupList);
 
        List<GroupList> GroupLists = DatabaseManager.getInstance().getAllGroupLists();
 
        for (GroupList group : GroupLists) {
            names = names + group.getName();
        }
        textView.setText(names);
    }
 
}
 

 

ORMLite 장단점

 

ORMLite 를 사용하지 않으면

 

1
2
3
4
5
6
7
 
            String str = "insert into book(title, author, publisher,publishDate,isbn,bookimg,booklink,status)" +
                    " values('" + bookList.get(position).getTitle() + "','" + bookList.get(position).getAuthor() + "'," +
                    "'" + bookList.get(position).getPublisher() + "', '" + bookList.get(position).getPublishDate() + "','" 
                    + bookList.get(position).getIsbn() + "','" + bookList.get(position).getImg() + "','" 
                    + bookList.get(position).getLink() + "','"+status+"');";
 

 

 

이와 같은 sql 문을 사용하게 되는데 '' 을하다보면 실수를 하는 경우가 많습니다 .

ORMLite 의 장점은 이러한 오타로 인한 오류를 피할수 있습니다 .
또한 트랙잰션 관리를 할수 있습니다 .

하지만 ORMLite 단점은 학습이 필요하다는 것입니다.

조금의 학습을 통해서 익혀 놓으면 단점보다는 도움이 많이 될수 있을것 같습니다 .


'Develop > Android' 카테고리의 다른 글

Google Drive 사용하기(1)  (0) 2016.02.13
Google Drive 준비 하기  (0) 2016.02.13
Android Universal Image Loader 사용하기  (0) 2016.02.13
Jsoup 사용하기  (0) 2016.02.13
Android Gson 사용하기  (0) 2016.02.13
Comments