ormlite的一对多详解

我们先看下怎么用,再在后面详细解说

我们演示的是平常消费的每个人可以下多个订单的案例。

    **一个账号(Account)** 创建 **多个订单(order)** 的一对多关系;

1. 使用演示

Account account = new Account("Jim Coakley");
accountDao.create(account);
//我们需要先保存账号,使我们的account有唯一的ID。

Order order = new Order("Jim Sanders", 12.34);
order.setAccount(account);
…
orderDao.create(order);
//最后我们把这个账号赋值给我们的订单。接着保存,这样,一对多的关系就成功建立了。

以上就是我们的使用案例,是不是觉得挺简单的呢?
后面我们有order1,order2,order3都是直接setAccount(account)就可以了。

1.1 详细代码如下

  1. 订单类

    @DatabaseTable(tableName = "orders")
    public class Order {
    
        @DatabaseField(generatedId = true)
        private int id;
    
        @DatabaseField(canBeNull = false, foreign = true)
        private Account account;
        …
    }
    
  2. 下面是账号类

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

2. 详解

  1. 外键
    当我们创建表order的时候,最后转化的sql语句大致如下:

    CREATE TABLE `orders`
       (`id` INTEGER AUTO_INCREMENT , `account_id` INTEGER,
        PRIMARY KEY (`id`)); 
    

    这里需要注意的是,外键是account_id而不是account ,如果查询,需要使用account_id这个fieldName.

  2. 查询
    所以如果你要获得订单,查询语句如下;

    //用id去直接查
    List<Order> results =orderDao.queryBuilder().where().
            eq("account_id", account.getName()).query();
    
    //除此之外,你也可以传递account,让romlite自己去解析 
     // ORMLite will extract and use the id field internally
    List<Order> results =orderDao.queryBuilder().where().
        eq("account_id", account).query();                              
    

    当然,一定情况下,为了自己方便,最好自定下columnName,遮掩就可以代替account_id去查询了。

       @DatabaseField(columnName = "你定义的列名",canBeNull = false, foreign = true)
       private Account account;
    

    注意
    这带来的问题还包括,在查询的时候,我们得到的order的外键account里面实际只有id是被赋值的,其余属性都是些默认值(0,false,null)等,如果你想使用别的属性,你必须调用accountDao的refrsh去获取。
    例如:

    Order order = orderDao.queryForId(orderId);
    System.out.println("只有id有值,Account-id =" +order.account.id); 
    System.out.println("别的字段为空,例如 name="+ order.account.name);
    
    //想要别的属性只能用AccountDao的refresh方法;
    accountDao.refresh(order.getAccount());
    System.out.println("现在,名字是: " +order.account.name); 
    

    当然你也可以使用foreignAutoRefresh让外键自动的属性,详情见

  3. 自动化外键
    我们上面的代码是先有Account,再设置到order里面去,这是因为这个外键是不会被自动生成的,如果要想自动话,可以看这个foreignAutoCreate.

  4. @ForeignCollectionField 支持的参数

    1. eager 加载策略
      不知道你注意到了没有,上面的Account类的这个注释有个参数eager

      @ForeignCollectionField(eager = false)
      ForeignCollection orders;
      这个是关于order的加载的模式的,现在有两种:“懒惰式加载”与“渴求式加载”( 或者说 ”延迟加载“和”立即加载“),具体意思和我们平时别框架的加载策略一样,
      如果把eager设置为真,那么当我们查询的时候,order就会以list的形式,就立即加载数据回来。
      如果为假(默认的),则等到我们调用到这个collection的时候,才回去加载回来。
      另外,如果我们想知道order的size(),时候,这个方法也会导致数据的加载。
      另外要注意的是:
      当我们使用类似Dao.iterator()方法的时候,这个通过懒惰加载返回的迭代器,当你处理完后,必须关闭释放掉,因为背后其实是有一个到数据库的链接的,这个连接的释放是当我们迭代完所有的数据或者我们主动的调用迭代器的close()方法,仅ForeignCollection会返回这种可关闭的迭代器iterator,所以循环遍历延迟加载的数据是一个不太好的模式,更多内容, 点击这里iterator. ,类似如下的

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

      再需要严重注意的事:
      当我们是延迟加载的时候,如果你还想要加回数据,请不要关闭数据库db.close(),否者数据就加载不回来了,你的Dao背后是靠他去把数据加回来的哦,另外如果用了Intent传递的话,很可能也是再也加不会拉了,因为你的Dao又睡觉了!
      再再另外要注意的:
      ormlite基于性能的考录,当我们的eager集合的属性里面也有一个饥渴加载的字段(例如货物的信息对象),那么他默认是会被延迟加载的。如果你想取消,你需要设置下maxEagerLevel 。

    2. maxEagerLevel 最大立即加载级别
      这个可以设置我们最多立即加载的外键的级别。
      如果当我们查询A,而且他有即可加载字段B,同时B也有饥渴加载字段C..一直这么下去,如果不加限制,那么当我们加载A的时候,数据的加载即将特别的慢,所以默认情况下,这个值被设置为1,所以除了B被立即加回来,后面都被延迟加载了。如果你还想加载C,D,E等,你需要设置下这个值为2,3,4。当然不怎么推荐,这带来的性能开销太大了。或许你应该考虑下改进你的数据库设计。

    3. columnName 列名
      这个没什么好说的,就是定义这一列的名字。当我们想查询具体的列时候需要这个名字,我们会特别定义下。

    4. orderColumnName
      在进行结果集排序时的外键对象名

    5. orderAscending
      如果被排序的列有定义了上面的orderColumnName,那么这个查询结果为按照这个值来排序.(默认是真,true时候ascending,false的时候descending)

    6. foreignFieldName
      这个是设置外键列名(不是上面的columnName),当我们设置超过两个外键(例如树形结构)而且你需要加以区分的时候需要用到。

3.级联删除

很有意思的是,ormlite在删除了Account后,并不会主动的去删除掉order
对于这样一个需要级联删除的事情,只能自己写触发子解决了。
在我们的DBhelper里面加下面的内容

@Override
public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
   try {
        TableUtils.createTable(connectionSource, Account.class);
        TableUtils.createTable(connectionSource, Order.class);
            //创建触发器,当删除Account时,自动删除该Account所有order.
        String accountTrigger = "CREATE TRIGGER FK_DELETE " +
                "BEFORE DELETE ON account " +
                "FOR EACH ROW BEGIN " +
                "DELETE FROM order WHERE account_id = OLD.id;" +
                "END;;" +
                "DELIMITER;";
        db.execSQL(accountTrigger);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}


经过上面的阐述,不知道你有没发现一件惊人的事实,就是这个一对多的玩意很鸡肋,特别是不能级联删除。

你完全可以自己在order加多一个字段id,然后加多个setAcccountId()方法了事的。

没必要使用这玩意。

好啦,这次关于ormlite的一对多就解释到这啦

参考来源:
1. Foreign Object Fields
2. Annotation Type ForeignCollectionField

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值