Android ORM 框架之 Android中ORMLite应用基础

Posted Iaouei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ORM 框架之 Android中ORMLite应用基础相关的知识,希望对你有一定的参考价值。

ORMLite是常用的一个ORM框架,她不止可以用在android的sqlite数据库,也可以使用她操作其他常见的数据库。这儿是根据官方文档抽取来的android用法。

一,添加依赖

导入ormlite-core.jar和ormlite-android.jar:下载jar
或者build.gradle中添加:

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

二,创建Bean类

1>类使用@DatabaseTable注解,标识一个表。
2>成员变量使用 @DatabaseField 注解,表示表中的列。
3>相关属性设置可以查询其依赖包中的DatabaseTable和Column注解类。持久化的数据类型可以查阅ORMLite官方文档或库源码。
4>添加一个无参的构造函数。当执行查询返回结果时,ORMLite会使用Java反射构造函数创建对象。

@DatabaseTable(tableName = "accounts")
public class Account 
    //generatedId = true表示id主键自动生成
    @DatabaseField(generatedId = true)  
    private int id;
    //id = true表示这个列可以通过ID方法执行查询,更新,删除
    @DatabaseField(id = true)
    private String name;
    //canBeNull = false表示不可以为空
    @DatabaseField(canBeNull = false)
    private String password;

    public Account() 
        // ORMLite 需要一个无参构造器 
    

    public Account(String name, String password) 
        this.name = name;
        this.password = password;
    

    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 String getPassword() 
        return password;
    
    public void setPassword(String password) 
        this.password = password;
    

三,创建数据库

1>继承OrmLiteSqliteOpenHelper 创建database helper;
2>在onCreate方法中调用TableUtils.createTable创建表,onUpgrade方法执行数据库更新。TableUtils类提供了一些创建和删除表的静态方法,参考源代码。

public class DatabaseHelper extends OrmLiteSqliteOpenHelper 
    private static final String TABLE_NAME = "sqlite.db";

    public DatabaseHelper(Context context) 
        super(context, TABLE_NAME, null, 2);
    

    @Override
    public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) 

        try 
            TableUtils.createTable(connectionSource, Bean.class);
         catch (java.sql.SQLException e) 
            e.printStackTrace();
        

    
    @Override
    public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) 

    

四,创建DAO

在数据访问对象(Data Access Objects (DAO))类中隔离数据库操作,提供创建,删除,更新,等等操作。每个表都对应一个DAO,每个DAO有两个参数:使用DAO持久化数据的实体类和用来标示指定表中行的ID列,如果类中没设置ID列,可以把对象或null作为第二个参数。例如,在上面Account类中,“name”成员是ID列(id = true),所以ID列是字符串。
创建DAO的最简单的方法是调用DaoManager.createDao(),这样如果它们被内部ORMLite功能需要,可以重复使用,而不是重新创建(创建DAO很消耗资源,应该尽可能重复利用):


Dao<Account, String> accountDao =DaoManager.createDao(connectionSource, Account.class);
Dao<Order, Integer> orderDao =DaoManager.createDao(connectionSource, Order.class);

使用这种方式需要ConnectionSource参数,和实体类的Class对象,ConnectionSource参数可以通过OrmLiteSqliteOpenHelper的getConnectionSource()方法获得:

ConnectionSource connectionSource = OrmLiteSqliteOpenHelper.getConnectionSource();

如果想要一个更好的类层次结构,或者在DAO中需要添加较多的方法,可以定义一个接口,继承Dao接口,接口不是必须的,但这样做可以减少耦合:


/** Account DAO ,具有字符串id (Account.name) */
public interface AccountDao extends Dao<Account, String> 
    // 添加 DAO的方法

然后实现类继承BaseDaoImpl并实现上面定义的接口:


/** JDBC implementation of the AccountDao interface. */
public class AccountDaoImpl extends BaseDaoImpl<Account, String>implements AccountDao 
    // 这个构造器必须有
    public AccountDaoImpl(ConnectionSource connectionSource)
      throws SQLException 
        super(connectionSource, Account.class);
    

要使用自定义DAO类的,需要到相应的实体类@DatabaseTable注解中添加daoClass参数:


@DatabaseTable(daoClass = AccountDaoImpl.class)
public class Account 
   …

也可以在DatabaseHelper中调用OrmLiteSqliteOpenHelper.getDao创建:

    private Dao<Account , Integer> accountDao;  
    public Dao<Account , Integer> getAccountDao() throws SQLException  
      
        if (AccountDao == null)  
          
            AccountDao = getDao(Account.class);  
          
        return accountDao;  
      

五,运行时与SQL异常

默认情况下,大多数的DAO方法都会抛出SQLException,大部分的异常继承RuntimeException,所以会需要大量的try … catch语句,出于这个原因,RuntimeExceptionDao包装了调用DAO方法会出现的运行时异常SQL exceptions:

Dao<Account, String> dao = DaoManager.createDao(connectionSource, Account.class);
RuntimeExceptionDao<Account, String> accountDao = new RuntimeExceptionDao<Account, String>(dao);

或者直接使用其createDao方法:


RuntimeExceptionDao<Account, String> accountDao =RuntimeExceptionDao.createDao(connectionSource, Account.class);

在DatabaseHelper中可以调用OrmLiteSqliteOpenHelper.getRuntimeExceptionDao。

六,使用DAO

插入新的行到数据库:

Account account = new Account();
account.name = "Jim Coakley";
accountDao.create(account);

如果对象具有注解声明的id参数,那么我们就可以使用其ID在数据库中查找此对象:


Account account = accountDao.queryForId(name);
if (account == null) 
  account not found handling … 

如果改变对象的成员属性,必须调用update()将改变更新到数据库中:

account.password = "_secret";
accountDao.update(account);

刷新对象

如果数据空正使用的某个对象被其他实体改变,需要刷新这个对象,保证这个对象是实时的:

accountDao.refresh(account);

删除对象对应的数据库中的行,一旦对象已经从数据库中删除,您可以继续使用在内存中的该对象,但任何更新或刷新会失效:

accountDao.delete(account);

迭代

该DAO本身也是一个迭代器,因此可以直接遍历数据库数据中表的行:

// page through all of the accounts in the database
for (Account account : accountDao) 
    System.out.println(account.getName());

这种方式必须遍历数据库中表的所有行,然后才会关闭潜在的sql对象。 如果没有通过循环遍历所有的行,
ORMLite并不会关闭sql对象,数据库的连接会被弱化并且只有GC回收才会被关闭,这样会导致程序Bug,所以最好使用
try…finally迭代器。以下for循环,是一个非常糟糕的代码:

for (Account account : accountDao) 
    if (account.getName().equals("Bob Smith")) 
        // you can't return, break, or throw from here
        return account;
    

也可以选择直接使用迭代器,因为for循环式并不总是最佳的。这样允许使用try…finally,达到一种更优化的代码:

 CloseableIterator<Account> iterator = accountDao.closeableIterator();
      try 
         while (iterator.hasNext()) 
            Account account = iterator.next();
            System.out.println(account.getName());
         
       finally 
         // close it at the end to close underlying SQL statement
         iterator.close();    
      

或者使用迭代包装器,for循环之后仍然可以关闭:

 CloseableWrappedIterable<Account> wrappedIterable =
       accountDao.getWrappedIterable();
       try 
          for (Account account : wrappedIterable) 
              …
          
        finally 
          wrappedIterable.close();
   

七,使用OpenHelperManager

DatabaseHelper可能在App或多个Activity的线程中被多次实例化使用,如果同时有多个对象,可能会出现过时的数据和意外的结果。所以建议使用OpenHelperManager管理DatabaseHelper的使用:

private DatabaseHelper databaseHelper = null;

private void releaseHelper() 
    if (databaseHelper != null) 
        OpenHelperManager.releaseHelper();
        databaseHelper = null;
    

private DatabaseHelper getHelper() 
    if (databaseHelper == null) 
        databaseHelper =
            OpenHelperManager.getHelper(this, DatabaseHelper.class);
    
    return databaseHelper;

还可以让Activity继承OrmLiteBaseActivity,OrmLiteBaseActivity对OpenHelperManager的使用做了进一步封装,还有OrmLiteBaseListActivity, OrmLiteBaseService, OrmLiteBaseTabActivity等,不过一般不这么做。但是可以在使用OpenHelperManager的时候拿来参考。
然后通过databaseHelper操作数据库:

    Dao<Account, String> accountDao = getHelper().getAccountDao();
    Account account = new Account ();
    account.setName("one");
    account.setPassword("123");
    accountDao.create(account);

八,ORMLite外键

ORMLite支持外键的概念,既一个对象的一个或者多个属性保存在相同数据库中其他表中。比如数据库中有一个Order对象,每个Order对象都对应一个Account对象,然后就说Order(是一个表)对象的Account(也是一个表)成员有外键属性。id成员在表创建的时候会生成”account_id”列:

@DatabaseTable(tableName = "orders")
public class Order 

    @DatabaseField(generatedId = true)
    private int id;

    @DatabaseField(canBeNull = false, foreign = true)
    private Account account;
    …

当创建有外键属性成员的对象(Order)时,外键对象(Account)不会被自动创建(Account表不会自动创建)。如果外键对象(Account对象)在数据库中有generated id(Order对象有 id成员),应该在创建引用这个对象(Order对象有Account成员)的对象(Order对象)之前创建外键对象(Account表):

Account account = new Account("Jim Coakley");
accountDao.create(account);
// 这时会创建一个account对象,并且设置generated ids

// 现在用order设置account,创建并插入account表
Order order = new Order("Jim Sanders", 12.34);
order.setAccount(account);
…
orderDao.create(order);

某些情况下如果希望外键自建,可以在外键(account)的注解中设置foreignAutoCreate参数。
当你查询一个Order时,会获得一个account对象,这个对象只有id属性被设定了值,其他属性都是默认值(null, 0, false, etc.)。如果需要使用这个account对象的其他值,则必须调用accountDao类的refresh方法获得这个Account对象的属性:

Order order = orderDao.queryForId(orderId);
System.out.println("Account-id on the order should be set: " +
   order.account.id);
// 此时 order.account.name为null
System.out.println("But other fields on the account should not be set: "
   + order.account.name);

// 用AccountDao刷新一下account
accountDao.refresh(order.getAccount());
System.out.println("Now the account fields will be set: " +
   order.account.name);

也可以在注解中添加foreignAutoRefresh参数值设置自动刷新外键对象的数据。
有不同的方式查询外键的参数值,比如查询匹配某个account的所有Order,通过ID参数的成员和account的name查询:

// query for all orders that match a certain account
List<Order> results =
  orderDao.queryBuilder().where().
    eq("account_id", account.getName()).query();

也可以让ORMLite从该account中自动提取id获得:

// ORMLite will extract and use the id field internally
List<Order> results =
  orderDao.queryBuilder().where().
    eq("account_id", account).query();

九,外键集合

上面说了Account是Order的外键,如果一个Account对象对应多个Order对象,则可以将其以成员的形式通过 @ForeignCollectionField注解以以下形式添加到Account中去:

public class Account 
    …
    @ForeignCollectionField(eager = false)
    ForeignCollection<Order> orders;
    …

成员类型只可以使用ForeignCollection< T> 或 Collection< T>。注解参数可以查阅库中的ForeignCollectionField类。
当改变了Collection中的order时,需要调用update在数据库中更新它:

for (Order order : account.orders()) 
   // 如果改变了order的某些成员
   order.setAmount(123);
   // 必须在数据库中更新account
   account.orders.update(order);

十,事务

当对数据库有较多的执行动作时,用事务处理比较好,图个方便,下段代码摘自ormlite的事务使用

private boolean doBatchInTransaction(final List<T> list, final int batchType) 
        boolean doBatch = false;
        ConnectionSource connectionSource = ormLiteDao.getConnectionSource();
        TransactionManager transactionManager = new TransactionManager(connectionSource);
        Callable<Boolean> callable = new Callable<Boolean>() 
            @Override
            public Boolean call() throws Exception 
                return doBatch(list, batchType);
            
        ;
        try 
            doBatch = transactionManager.callInTransaction(callable);
         catch (SQLException e) 
            LogUtil.e(e);
        
        return doBatch;
    

    private boolean doBatch(List<T> list, int batchType) 
        int result = 0;
        try 
            for (T t : list) 
                switch (batchType) 
                    case DaoOperation.INSERT:
                        result += ormLiteDao.create(t);
                        break;
                    case DaoOperation.DELETE:
                        result += ormLiteDao.delete(t);
                        break;
                    case DaoOperation.UPDATE:
                        result += updateIfValueNotNull(t);
                        break;
                    default:
                        LogUtil.w("no this type.");
                        break;
                
            
         catch (SQLException e) 
            LogUtil.e(e);
        
        return result == list.size();
    

十一,更新数据库

更新app时,可能会修改现有的数据库,这时就需要在 创建的DatabaseHelper的onUpgrade方法中进行一些工作了。比如在表中增加一列,可以在onUpgrade方法中这么做:

Dao<Account, Integer> dao = getHelper().getAccountDao();
// 在表account中增加 "age" 列
dao.executeRaw("ALTER TABLE `account` ADD COLUMN age INTEGER;");

所有能做的就是重新命名表名和添加新列,不要重命名或删除列或更改列的属性。
最常用的,是根据版本条件更改数据库:

if (oldVersion < 2) 
  // we added the age column in version 2
  dao.executeRaw("ALTER TABLE `account` ADD COLUMN age INTEGER;");

if (oldVersion < 3) 
  // we added the weight column in version 3
  dao.executeRaw("ALTER TABLE `account` ADD COLUMN weight INTEGER;");

或者:

dao.executeRaw(
    "ALTER TABLE `account` ADD COLUMN hasDog BOOLEAN DEFAULT 0;");
dao.updateRaw("UPDATE `account` SET hasDog = 1 WHERE dogCount > 0;");

这儿用到了数据库语句,可以查阅sqlite官方文档。

更多请参考 ORMLite官方文档

以上是关于Android ORM 框架之 Android中ORMLite应用基础的主要内容,如果未能解决你的问题,请参考以下文章

Android ORM 框架之 ActiveAndroid应用基础

Android ORM 框架之 ActiveAndroid应用基础

Android ORM 框架之 ActiveAndroid应用基础

Android ORM 框架之 greenDAO应用基础

Android ORM 框架之 greenDAO应用基础

android_orm框架之greenDAO