大家在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. }
- 整个DatabaseHelper使用单例只对外公布出一个对象,保证app中只存在一个SQLite Connection。 参考文章
- 我们对每个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
答案是可以的。在User中添加如下属性,且注解如下:
@ForeignCollectionField
private Collection
我们在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官方文档,源码中也会提供~~