ORMLite框架实践详解

大家在Android项目中或多或少的都会使用数据库,为了提高我们的开发效率,当然少不了数据库ORM框架了,尤其是某些数据库操作特别频繁的app;本篇博客将通过一个实例来详细介绍ORMLite

1、下载 ORMLite Jar

首先去ORMLite官网下载jar包,对于Android为:ormlite-android-5.0.jar 和 ormlite-core-5.0.jar;
如果你使用的开发工具是Eclipse的话,有了jar,然后把jar拷贝到libs下,然后右键点击jar包点击add Libary(即添加依赖)。
如果你使用的开发工具是Android Studio的话,只需要在build.gradle文件里引入包就行了:

compile 'com.j256.ormlite:ormlite-android:5.0'
compile 'com.j256.ormlite:ormlite-core:5.0'

访问不了的朋友,这里会把jar、源码、doc与本篇博客例子一起打包提供给大家下载

2、配置Bean类

我们直接新建一个项目为:zhy_ormlite。
然后新建一个包:com.zhy.zhy_ormlite.bean专门用于存放项目中的Bean,首先新建一个User.java

package com.zhy.zhy_ormlite.bean;

import java.util.Collection;

import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;

@DatabaseTable(tableName = "tb_user")
public class User
{
    @DatabaseField(generatedId = true)
    private int id;
    @DatabaseField(columnName = "name")
    private String name;

    @ForeignCollectionField
    private Collection<Article> articles;

    public Collection<Article> getArticles()
    {
        return articles;
    }

    public void setArticles(Collection<Article> articles)
    {
        this.articles = articles;
    }

    public User()
    {
    }

    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;
    }

    @Override
    public String toString()
    {
        return "User [id=" + id + ", name=" + name + ", articles=" + articles
                + "]";
    }

}
  • 首先在User类上添加@DatabaseTable(tableName = “tb_user”),标明这是数据库中的一张表,标明为tb_user
  • 然后分别在属性上添加@DatabaseField(columnName = “name”) ,columnName的值为该字段在数据中的列名
  • @DatabaseField(generatedId = true) ,generatedId 表示id为主键且自动生成

3、ORMLite外键引用

现在我们有两张表一张User,一张Article;
Article中当然需要存储User的主键,作为关联~~那么在ORMLite中如何做到呢?
可能有人会直接在Article中声明一个int类型userId属性,当作普通属性处理搞定,这种做法并没有做,但是没有体现出面向对象的思想。
面向对象是这样的:Article属于某个User
类这么定义:

1. package com.zhy.zhy_ormlite.bean;  
2.   
3. import com.j256.ormlite.field.DatabaseField;  
4. import com.j256.ormlite.table.DatabaseTable;  
5.   
6. @DatabaseTable(tableName = "tb_article")  
7. public class Article  
8. {  
9.     @DatabaseField(generatedId = true)  
10.     private int id;  
11.     @DatabaseField  
12.     private String title;  
13.     @DatabaseField(canBeNull = true, foreign = true, columnName = "user_id")  
14.     private User user;  
15.   
16.     public int getId()  
17.     {  
18.         return id;  
19.     }  
20.   
21.     public void setId(int id)  
22.     {  
23.         this.id = id;  
24.     }  
25.   
26.     public String getTitle()  
27.     {  
28.         return title;  
29.     }  
30.   
31.     public void setTitle(String title)  
32.     {  
33.         this.title = title;  
34.     }  
35.   
36.     public User getUser()  
37.     {  
38.         return user;  
39.     }  
40.   
41.     public void setUser(User user)  
42.     {  
43.         this.user = user;  
44.     }  
45.   
46.     @Override  
47.     public String toString()  
48.     {  
49.         return "Article [id=" + id + ", title=" + title + ", user=" + user  
50.                 + "]";  
51.     }  
52.   
53. }  

不会去定义一个int类型的userId,而是直接定义一个User成员变量,表示本Article属于该User;
然后在User user属性上添加:

@DatabaseField(canBeNull = true, foreign = true, columnName = "user_id")
  • canBeNull表示不能为null;
  • foreign=true表示是一个外键;
  • columnName 列名

4、编写DAO类

我们可能会有很多表嘛,每个表一般我们都会单独写个Dao用于操作

public class UserDao
{
    private Context context;
    private Dao<User, Integer> userDaoOpe;
    private DatabaseHelper helper;

    public UserDao(Context context)
    {
        this.context = context;
        try
        {
            helper = DatabaseHelper.getHelper(context);
            userDaoOpe = helper.getDao(User.class);
        } catch (SQLException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 增加一个用户
     *
     * @param user
     * @throws SQLException
     */
    public void add(User user)
    {
        /*//事务操作
        TransactionManager.callInTransaction(helper.getConnectionSource(),
                new Callable<Void>()
                {

                    @Override
                    public Void call() throws Exception
                    {
                        return null;
                    }
                });*/
        try
        {
            userDaoOpe.create(user);
        } catch (SQLException e)
        {
            e.printStackTrace();
        }

    }

    public User get(int id)
    {
        try
        {
            return userDaoOpe.queryForId(id);
        } catch (SQLException e)
        {
            e.printStackTrace();
        }
        return null;
    }

}

我们的所有的XXXDao遵循以上的风格~

public class ArticleDao {
    private Dao<Article, Integer> articleDaoOpe;
    private DatabaseHelper helper;

    @SuppressWarnings("unchecked")
    public ArticleDao(Context context) {
        try {
            helper = DatabaseHelper.getHelper(context);
            articleDaoOpe = helper.getDao(Article.class);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加一个Article
     *
     * @param article
     */
    public void add(Article article) {
        try {
            articleDaoOpe.create(article);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过Id得到一个Article
     *
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public Article getArticleWithUser(int id) {
        Article article = null;
        try {
            article = articleDaoOpe.queryForId(id);
            helper.getDao(User.class).refresh(article.getUser());

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return article;
    }

    /**
     * 通过Id得到一篇文章
     *
     * @param id
     * @return
     */
    public Article get(int id) {
        Article article = null;
        try {
            article = articleDaoOpe.queryForId(id);

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return article;
    }

    /**
     * 通过UserId获取所有的文章
     *
     * @param userId
     * @return
     */
    public List<Article> listByUserId(int userId) {
        try {
            return articleDaoOpe.queryBuilder().where().eq("user_id", userId)
                    .query();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

4、DatabaseHelper

我们使用ORMLite,需要自己写一个DatabaseHelper去继承OrmLiteSqliteOpenHelper,下面我们首先给出一个我认为比较靠谱的Helper的写法:

1. package com.zhy.zhy_ormlite.db;  
2.   
3. import java.sql.SQLException;  
4. import java.util.HashMap;  
5. import java.util.Map;  
6.   
7. import android.content.Context;  
8. import android.database.sqlite.SQLiteDatabase;  
9.   
10. import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;  
11. import com.j256.ormlite.dao.Dao;  
12. import com.j256.ormlite.support.ConnectionSource;  
13. import com.j256.ormlite.table.TableUtils;  
14. import com.zhy.zhy_ormlite.bean.Article;  
15. import com.zhy.zhy_ormlite.bean.Student;  
16. import com.zhy.zhy_ormlite.bean.User;  
17.   
18. public  class DatabaseHelper extends OrmLiteSqliteOpenHelper  
19. {  
20.     private static final String TABLE_NAME = "sqlite-test.db";  
21.   
22.     private Map<String, Dao> daos = new HashMap<String, Dao>();  
23.   
24.     private DatabaseHelper(Context context)  
25.     {  
26.         super(context, TABLE_NAME, null, 4);  
27.     }  
28.   
29.     @Override  
30.     public void onCreate(SQLiteDatabase database,  
31.             ConnectionSource connectionSource)  
32.     {  
33.         try  
34.         {  
35.             TableUtils.createTable(connectionSource, User.class);  
36.             TableUtils.createTable(connectionSource, Article.class);  
37.             TableUtils.createTable(connectionSource, Student.class);  
38.         } catch (SQLException e)  
39.         {  
40.             e.printStackTrace();  
41.         }  
42.     }  
43.   
44.     @Override  
45.     public void onUpgrade(SQLiteDatabase database,  
46.             ConnectionSource connectionSource, int oldVersion, int newVersion)  
47.     {  
48.         try  
49.         {  
50.             TableUtils.dropTable(connectionSource, User.class, true);  
51.             TableUtils.dropTable(connectionSource, Article.class, true);  
52.             TableUtils.dropTable(connectionSource, Student.class, true);  
53.             onCreate(database, connectionSource);  
54.         } catch (SQLException e)  
55.         {  
56.             e.printStackTrace();  
57.         }  
58.     }  
59.   
60.     private static DatabaseHelper instance;  
61.   
62.     /**
63.      * 单例获取该Helper
64.      *  
65.      * @param context
66.      * @return
67.      */  
68.     public static synchronized DatabaseHelper getHelper(Context context)  
69.     {  
70.         context = context.getApplicationContext();  
71.         if (instance == null)  
72.         {  
73.             synchronized (DatabaseHelper.class)  
74.             {  
75.                 if (instance == null)  
76.                     instance = new DatabaseHelper(context);  
77.             }  
78.         }  
79.   
80.         return instance;  
81.     }  
82.   
83.     public synchronized Dao getDao(Class clazz) throws SQLException  
84.     {  
85.         Dao dao = null;  
86.         String className = clazz.getSimpleName();  
87.   
88.         if (daos.containsKey(className))  
89.         {  
90.             dao = daos.get(className);  
91.         }  
92.         if (dao == null)  
93.         {  
94.             dao = super.getDao(clazz);  
95.             daos.put(className, dao);  
96.         }  
97.         return dao;  
98.     }  
99.   
100.     /**
101.      * 释放资源
102.      */  
103.     @Override  
104.     public void close()  
105.     {  
106.         super.close();  
107.   
108.         for (String key : daos.keySet())  
109.         {  
110.             Dao dao = daos.get(key);  
111.             dao = null;  
112.         }  
113.     }  
114.   
115. }  
  1. 整个DatabaseHelper使用单例只对外公布出一个对象,保证app中只存在一个SQLite Connection。 参考文章
  2. 我们对每个Bean创建一个XXXDao来处理当前Bean的数据库操作,当然真正去和数据库打交道的对象,通过上面代码中的getDao(T t)进行获取
    getDao为一个泛型方法,会根据传入Class对象进行创建Dao,并且使用一个Map来保持所有的Dao对象,只有第一次调用时才会去调用底层的getDao()。

5、我们的测试类:

1. public class OrmLiteDbTest extends AndroidTestCase  
2. {  
3.     public void testAddArticle()  
4.     {  
5.         User u = new User();  
6.         u.setName("张鸿洋");  
7.         new UserDao(getContext()).add(u);  
8.         Article article = new Article();  
9.         article.setTitle("ORMLite的使用");  
10.         article.setUser(u);  
11.         new ArticleDao(getContext()).add(article);  
12.   
13.     }  
14.   
15.     public void testGetArticleById()  
16.     {  
17.         Article article = new ArticleDao(getContext()).get(1);  
18.         L.e(article.getUser() + " , " + article.getTitle());  
19.     }  
20.   
21.     public void testGetArticleWithUser()  
22.     {  
23.   
24.         Article article = new ArticleDao(getContext()).getArticleWithUser(1);  
25.         L.e(article.getUser() + " , " + article.getTitle());  
26.     }  
27.       
28.     public void testListArticlesByUserId()  
29.     {  
30.   
31.         List<Article> articles = new ArticleDao(getContext()).listByUserId(1);  
32.         L.e(articles.toString());  
33.     }  

分别测试,添加一个Article;通过Id获取一个Article;通过Id获取一个Article且携带User;通过userId获取所有的Article;
主要看第三个:通过Id获取一个Article且携带User,testGetArticleWithUser(id)
如何值传一个Article的Id,然后能够拿到Article对象,且内部的user属性直接赋值呢?
两种方式:

  • 1、即上述写法

    1. article = articleDaoOpe.queryForId(id);  
    2.  helper.getDao(User.class).refresh(article.getUser());  
    
  • 2、在user属性的注解上:

    @DatabaseField(canBeNull = true, foreign = true, columnName = "user_id", foreignAutoRefresh = true)
    

添加foreignAutoRefresh =true,这样;当调用queryForId时,拿到Article对象则直接携带了user;

6、关联一个集合

每个User关联一个或多个Article,如果我在User中声明一个Collection

articles,我能否在查询User的时候,一并能够获取到articles的值呢?
答案是可以的。在User中添加如下属性,且注解如下:
@ForeignCollectionField
private Collection
articles;
我们在UserDao中书写查询User的代码:

1. public User get(int id)  
2.     {  
3.         try  
4.         {  
5.             return userDaoOpe.queryForId(id);  
6.         } catch (SQLException e)  
7.         {  
8.             e.printStackTrace();  
9.         }  
10.         return null ;  
11.     }  

测试代码:

1. public void testGetUserById()  
2.     {  
3.         User user = new UserDao(getContext()).get(1);  
4.         L.e(user.getName());  
5.         if (user.getArticles() != null)  
6.             for (Article article : user.getArticles())  
7.             {  
8.                 L.e(article.toString());  
9.             }  
10.     }  

输出:

1. 09-07 22:49:06.484: E/zhy(7293): 欧神  
2. 09-07 22:49:06.484: E/zhy(7293): Article [id=1, title=ORMLite的使用]  

可以看到,我们通过一个queryForId,成功的获取了User,以及User关联的所有的Articles;

7、条件查询QueryBuilder的使用

上述代码其实已经用到了简单的条件查询了:

  • 简单的where等于
    articleDaoOpe.queryBuilder().where().eq(“user_id”, userId).query();直接返回Article的列表
  • where and

    1. QueryBuilder<Article, Integer> queryBuilder = articleDaoOpe  
    2.                 .queryBuilder();  
    3.         Where<Article, Integer> where = queryBuilder.where();  
    4.         where.eq("user_id", 1);  
    5.         where.and();  
    6.         where.eq("name", "xxx");  
    7.   
    8.         //或者  
    9.         articleDaoOpe.queryBuilder().//  
    10.                 where().//  
    11.                 eq("user_id", 1).and().//  
    12.                 eq("name", "xxx");  
    

上述两种都相当与:select * from tb_article where user_id = 1 and name = ‘xxx’ ;

  • 更复杂的查询

    1. where.or(  
    2.                     //  
    3.                     where.and(//  
    4.                             where.eq("user_id", 1), where.eq("name", "xxx")),  
    5.                     where.and(//  
    6.                             where.eq("user_id", 2), where.eq("name", "yyy")));  
    

select * from tb_article where ( user_id = 1 and name = ‘xxx’ ) or ( user_id = 2 and name = ‘yyy’ ) ;
好了,再复杂的查询估计也能够凑出来了~~

8、updateBuilder、deleteBuilder

使用queryBuilder是因为我们希望执行完成查询直接返回List集合;
对于Update我们并不关注返回值,直接使用
articleDaoOpe.updateRaw(statement, arguments);传入sql和参数即可~~
何必在那
articleDaoOpe.updateBuilder().updateColumnValue(“name”,”zzz”).where().eq(“user_id”, 1);这样的痛苦呢~~~
同理还有deleteBuilder还是建议直接拼写sql,当然很简单的除外,直接使用它的API~

9、事务操作

在我们的Dao中直接写如下代码:

1. //事务操作  
2.         TransactionManager.callInTransaction(helper.getConnectionSource(),  
3.                 new Callable<Void>()  
4.                 {  
5.   
6.                     @Override  
7.                     public Void call() throws Exception  
8.                     {  
9.                         return null;  
10.                     }  
11.                 });  

10、其他操作

  • 1、当Bean继承BaseDaoEnabled时,可以使用bean.create(bean);bean.update(bean)一类操作
    例如:

    Student extends BaseDaoEnabled<Student, Integer>
    Dao dao = DatabaseHelper.getHelper(getContext()).getDao(Student.class);
    Student student = new Student();
    student.setDao(dao);
    student.setName("张鸿洋");
    student.create();
    

前提dao需要手动设置,如果dao为null会报错,尼玛,我觉得一点用没有。。。

  • 2、Join

    1. QueryBuilder<Article, Integer> articleBuilder = articleDaoOpe  
    2.                     .queryBuilder();  
    3.             QueryBuilder userBuilder = helper.getDao(User.class).queryBuilder();  
    4.             articleBuilder.join(userBuilder);  
    

Article与User做Join操作;

本篇主要想介绍在项目中如何写DataBaseHelper已经如何写BeanDao,以及列出了在项目中可能会用到的ORMLite的功能,如果需要详细了解,还请看ORMLite官方文档,源码中也会提供~~

源码点击下载

装载来自:张鸿洋的博客
Android ORMLite 框架的入门用法
Android ORMLite 框架最佳实践

-------------本文结束 感谢阅读-------------
坚持好文章的分享,您的支持将是对我最大的鼓励!