Android 快速开发系列 ORMLite 框架最佳实践

时间:2021-08-07 11:48:53

比较靠谱的Helper的写法:

1、DatabaseHelper

  1. package com.zhy.zhy_ormlite.db;
  2. import java.sql.SQLException;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import android.content.Context;
  6. import android.database.sqlite.SQLiteDatabase;
  7. import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
  8. import com.j256.ormlite.dao.Dao;
  9. import com.j256.ormlite.support.ConnectionSource;
  10. import com.j256.ormlite.table.TableUtils;
  11. import com.zhy.zhy_ormlite.bean.Article;
  12. import com.zhy.zhy_ormlite.bean.Student;
  13. import com.zhy.zhy_ormlite.bean.User;
  14. public  class DatabaseHelper extends OrmLiteSqliteOpenHelper
  15. {
  16. private static final String TABLE_NAME = "sqlite-test.db";
  17. private Map<String, Dao> daos = new HashMap<String, Dao>();
  18. private DatabaseHelper(Context context)
  19. {
  20. super(context, TABLE_NAME, null, 4);
  21. }
  22. @Override
  23. public void onCreate(SQLiteDatabase database,
  24. ConnectionSource connectionSource)
  25. {
  26. try
  27. {
  28. TableUtils.createTable(connectionSource, User.class);
  29. TableUtils.createTable(connectionSource, Article.class);
  30. TableUtils.createTable(connectionSource, Student.class);
  31. } catch (SQLException e)
  32. {
  33. e.printStackTrace();
  34. }
  35. }
  36. @Override
  37. public void onUpgrade(SQLiteDatabase database,
  38. ConnectionSource connectionSource, int oldVersion, int newVersion)
  39. {
  40. try
  41. {
  42. TableUtils.dropTable(connectionSource, User.class, true);
  43. TableUtils.dropTable(connectionSource, Article.class, true);
  44. TableUtils.dropTable(connectionSource, Student.class, true);
  45. onCreate(database, connectionSource);
  46. } catch (SQLException e)
  47. {
  48. e.printStackTrace();
  49. }
  50. }
  51. private static DatabaseHelper instance;
  52. /**
  53. * 单例获取该Helper
  54. *
  55. * @param context
  56. * @return
  57. */
  58. public static synchronized DatabaseHelper getHelper(Context context)
  59. {
  60. context = context.getApplicationContext();
  61. if (instance == null)
  62. {
  63. synchronized (DatabaseHelper.class)
  64. {
  65. if (instance == null)
  66. instance = new DatabaseHelper(context);
  67. }
  68. }
  69. return instance;
  70. }
  71. public synchronized Dao getDao(Class clazz) throws SQLException
  72. {
  73. Dao dao = null;
  74. String className = clazz.getSimpleName();
  75. if (daos.containsKey(className))
  76. {
  77. dao = daos.get(className);
  78. }
  79. if (dao == null)
  80. {
  81. dao = super.getDao(clazz);
  82. daos.put(className, dao);
  83. }
  84. return dao;
  85. }
  86. /**
  87. * 释放资源
  88. */
  89. @Override
  90. public void close()
  91. {
  92. super.close();
  93. for (String key : daos.keySet())
  94. {
  95. Dao dao = daos.get(key);
  96. dao = null;
  97. }
  98. }
  99. }

1、整个DatabaseHelper使用单例只对外公布出一个对象,保证app中只存在一个SQLite Connection , 参考文章:http://www.touchlab.co/2011/10/single-sqlite-connection/

2、我们对每个Bean创建一个XXXDao来处理当前Bean的数据库操作,当然真正去和数据库打交道的对象,通过上面代码中的getDao(T t)进行获取

getDao为一个泛型方法,会根据传入Class对象进行创建Dao,并且使用一个Map来保持所有的Dao对象,只有第一次调用时才会去调用底层的getDao()。

2、Bean的Dao

  1. package com.zhy.zhy_ormlite.db;
  2. import java.sql.SQLException;
  3. import android.content.Context;
  4. import com.j256.ormlite.dao.Dao;
  5. import com.zhy.zhy_ormlite.bean.User;
  6. public class UserDao
  7. {
  8. private Context context;
  9. private Dao<User, Integer> userDaoOpe;
  10. private DatabaseHelper helper;
  11. public UserDao(Context context)
  12. {
  13. this.context = context;
  14. try
  15. {
  16. helper = DatabaseHelper.getHelper(context);
  17. userDaoOpe = helper.getDao(User.class);
  18. } catch (SQLException e)
  19. {
  20. e.printStackTrace();
  21. }
  22. }
  23. /**
  24. * 增加一个用户
  25. * @param user
  26. */
  27. public void add(User user)
  28. {
  29. try
  30. {
  31. userDaoOpe.create(user);
  32. } catch (SQLException e)
  33. {
  34. e.printStackTrace();
  35. }
  36. }//...other operations
  37. }

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

好了,基本了解了我们的代码的结构~~ps:如果觉得不合理可以留言指出,如果觉得不能接收,直接忽略。。。

3、ORMLite外键引用

现在我们有两张表一张User,一张Article;

Article中当然需要存储User的主键,作为关联~~那么在ORMLite中如何做到呢?

可能有人会直接在Article中声明一个int类型userId属性,当作普通属性处理搞定,这种做法并没有做,但是没有体现出面向对象的思想。

面向对象是这样的:Article属于某个User

类这么定义:

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

不会去定义一个int类型的userId,而是直接定义一个User成员变量,表示本Article属于该User;

然后在User user属性上添加: @DatabaseField(canBeNull = true, foreign = true, columnName = "user_id")

canBeNull -表示不能为null;foreign=true表示是一个外键;columnName 列名

User类暂且就两个属性:

  1. package com.zhy.zhy_ormlite.bean;
  2. import com.j256.ormlite.field.DatabaseField;
  3. import com.j256.ormlite.table.DatabaseTable;
  4. @DatabaseTable(tableName = "tb_user")
  5. public class User
  6. {
  7. @DatabaseField(generatedId = true)
  8. private int id;
  9. @DatabaseField(columnName = "name")
  10. private String name;
  11. public User()
  12. {
  13. }
  14. public int getId()
  15. {
  16. return id;
  17. }
  18. public void setId(int id)
  19. {
  20. this.id = id;
  21. }
  22. public String getName()
  23. {
  24. return name;
  25. }
  26. public void setName(String name)
  27. {
  28. this.name = name;
  29. }
  30. @Override
  31. public String toString()
  32. {
  33. return "User [id=" + id + ", name=" + name
  34. + "]";
  35. }
  36. }

现在看我们的ArticleDao

  1. package com.zhy.zhy_ormlite.db;
  2. import java.sql.SQLException;
  3. import java.util.List;
  4. import android.content.Context;
  5. import com.j256.ormlite.dao.Dao;
  6. import com.zhy.zhy_ormlite.bean.Article;
  7. import com.zhy.zhy_ormlite.bean.User;
  8. public class ArticleDao
  9. {
  10. private Dao<Article, Integer> articleDaoOpe;
  11. private DatabaseHelper helper;
  12. @SuppressWarnings("unchecked")
  13. public ArticleDao(Context context)
  14. {
  15. try
  16. {
  17. helper = DatabaseHelper.getHelper(context);
  18. articleDaoOpe = helper.getDao(Article.class);
  19. } catch (SQLException e)
  20. {
  21. e.printStackTrace();
  22. }
  23. }
  24. /**
  25. * 添加一个Article
  26. * @param article
  27. */
  28. public void add(Article article)
  29. {
  30. try
  31. {
  32. articleDaoOpe.create(article);
  33. } catch (SQLException e)
  34. {
  35. e.printStackTrace();
  36. }
  37. }
  38. /**
  39. * 通过Id得到一个Article
  40. * @param id
  41. * @return
  42. */
  43. @SuppressWarnings("unchecked")
  44. public Article getArticleWithUser(int id)
  45. {
  46. Article article = null;
  47. try
  48. {
  49. article = articleDaoOpe.queryForId(id);
  50. helper.getDao(User.class).refresh(article.getUser());
  51. } catch (SQLException e)
  52. {
  53. e.printStackTrace();
  54. }
  55. return article;
  56. }
  57. /**
  58. * 通过Id得到一篇文章
  59. * @param id
  60. * @return
  61. */
  62. public Article get(int id)
  63. {
  64. Article article = null;
  65. try
  66. {
  67. article = articleDaoOpe.queryForId(id);
  68. } catch (SQLException e)
  69. {
  70. e.printStackTrace();
  71. }
  72. return article;
  73. }
  74. /**
  75. * 通过UserId获取所有的文章
  76. * @param userId
  77. * @return
  78. */
  79. public List<Article> listByUserId(int userId)
  80. {
  81. try
  82. {
  83. return articleDaoOpe.queryBuilder().where().eq("user_id", userId)
  84. .query();
  85. } catch (SQLException e)
  86. {
  87. e.printStackTrace();
  88. }
  89. return null;
  90. }
  91. }

接下来看我们的测试类:

  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. public void testGetArticleById()
  14. {
  15. Article article = new ArticleDao(getContext()).get(1);
  16. L.e(article.getUser() + " , " + article.getTitle());
  17. }
  18. public void testGetArticleWithUser()
  19. {
  20. Article article = new ArticleDao(getContext()).getArticleWithUser(1);
  21. L.e(article.getUser() + " , " + article.getTitle());
  22. }
  23. public void testListArticlesByUserId()
  24. {
  25. List<Article> articles = new ArticleDao(getContext()).listByUserId(1);
  26. L.e(articles.toString());
  27. }

分别测试,添加一个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;

4、关联一个集合

每个User关联一个或多个Article,如果我在User中声明一个Collection<Article> articles,我能否在查询User的时候,一并能够获取到articles的值呢?

答案是可以的。在User中添加如下属性,且注解如下:

@ForeignCollectionField
private Collection<Article> 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;

5、条件查询QueryBuilder的使用

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

1、简单的where等于

articleDaoOpe.queryBuilder().where().eq("user_id", userId).query();直接返回Article的列表

2、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. articleDaoOpe.queryBuilder().//
  9. where().//
  10. eq("user_id", 1).and().//
  11. eq("name", "xxx");

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

3、更复杂的查询

  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' )  ;

好了,再复杂的查询估计也能够凑出来了~~

6、updateBuilder、deleteBuilder

使用queryBuilder是因为我们希望执行完成查询直接返回List<Bean>集合;

对于Update我们并不关注返回值,直接使用

articleDaoOpe.updateRaw(statement, arguments);传入sql和参数即可~~

何必在那articleDaoOpe.updateBuilder().updateColumnValue("name","zzz").where().eq("user_id", 1);这样的痛苦呢~~~

同理还有deleteBuilder还是建议直接拼写sql,当然很简单的除外,直接使用它的API~

7、事务操作

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

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

8、其他操作

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官方文档,源码中也会提供~~