使用Room的@Relation和ORDER BY

26

顺便说一下,我尝试了 Room 库,它非常令人印象深刻!

因此,我有两个实体 (@Entity),还有一个由这两个实体组成的 POJO。

我的第一个实体:

@Entity(tableName = "colis")
public class ColisEntity {
    @PrimaryKey
    private String idColis;
    private String label;
}  

我的第二个实体带有外键:

@Entity(tableName = "step",
    foreignKeys = @ForeignKey(
        entity = ColisEntity.class,
        parentColumns = "idColis",
        childColumns = "idColis",
        onDelete = CASCADE
    )
)
public class StepEntity {
    @PrimaryKey(autoGenerate = true)
    private Integer idStep;
    private String idColis;
    private Long date;
}

带有@Relation注解的POJO:

public class ColisWithSteps {
    @Embedded
    public ColisEntity colisEntity;

    @Relation(parentColumn = "idColis", entityColumn = "idColis")
    public List<StepEntity> stepEntityList;
}

我的@Dao和Repositories都很好用。

但我希望我的@Relation List<StepEntity>能按照日期排序,而且我不想让StepEntity实现Comparable然后使用Collections.sort()排序。

因为我认为排序应该在查询之后由数据库完成,而不是在之后进行。

有什么想法吗?

谢谢。


我也遇到了这个问题,希望在未来的版本中Room能够支持对关系进行排序。我知道你说你不想使用Collections.Sort(),但考虑到情况,我认为这是最好和最干净的方法,这就是我所做的。使用Comparable将帮助您避免每次访问数据时需要执行两个单独的查询。 - Jraco11
5个回答

12

我知道 OP 拒绝使用 Collection.Sort(),但考虑到情况,这似乎是最清晰的方法。正如我上面所说,我认为这样做更好,避免每次访问数据时都要执行单独的查询。首先实现 Comparable 接口。

public class StepEntity implements Comparable<StepEntity >{
    @PrimaryKey(autoGenerate = true)
    private Integer idStep;
    private String idColis;
    private Long date;

   @Override
    public int compareTo(@NonNull StepEntity stepEntity ) {
        int dateOther = stepEntity.date;

        //ascending order
        return this.date - dateOther;

       //descending order
       //return dateOther - this.date;
    }
}

现在要使用它,只需在您现有的@Relation POJO中添加一个方法包装器

public class ColisWithSteps {
        @Embedded
        public ColisEntity colisEntity;

        @Relation(parentColumn = "idColis", entityColumn = "idColis")
        public List<StepEntity> stepEntityList;

        //add getter method
        public List<StepEntity> getSortedStepEntityList(){
           Collections.sort(stepEntityList);
           return stepEntityList;
       }
    }

1
这帮助我解决了任务。只是注意,Collections类的“sort”方法以小写字母开头。 - Marcell

4
关系文档中提到的一点,暗示了它是如何工作的:

该方法需要Room运行两个查询,因此请向此方法添加@Transaction注释,以确保整个操作是原子执行的。

这实际上泄露了关系背后的整个过程。您需要为每个实体编写一个查询,然后是单个事务查询,但最终结果与使用关系获得的结果无异(至少我所看到的)。
以下是(Kotlin)代码:
@Query("SELECT * FROM colis WHERE idColis = :id")
fun getColisEntity(id : Int) : 

@Query("SELECT * FROM step WHERE idColis = :id ORDER BY date")
fun getStepEntities(id : Int) : List<StepEntity> 

@Transaction
fun getColisWithSteps(id : Int) : ColisWithSteps{
    return ColisWithSteps(getColisEntity(id), getStepEntities(id))                      
}

getColisWithSteps(id : Int)将会返回与Relation相同结果,但是具有更多的排序自由,它会准确地返回你所需要的内容。


1

我认为在当前版本的Room中没有内置的方法来实现这个功能。

我可以提供两种可能的解决方案。

  1. 不使用带有@Relation的POJO,而是使用两个单独的查询来获取您的对象。这样,您可以按照自己喜欢的方式对StepEntity实例进行排序。当您获取ColisEntity和所有有序的相应StepEntity记录时,可以在存储库层构造一个ColisWithSteps对象并返回它。
  2. 您可以创建一个数据库视图,按所需顺序排序StepEntity记录,然后使用此答案Room中的视图来使用它。

我认为选项1是您的最佳选择 - 是的,它将涉及使用两个查询,但至少它不会破坏您的DB迁移,并且您将能够充分利用Room。


1
我认为方案1是最好的,但建立自己的对象而@Relation应该做到这一点,这真是太可惜了。谢谢你的回答。 - olivejp
3
第一种方法的问题在于,您会失去可观察属性。 - Rodrigo Borba
不完全是这样。您可以在存储库层中完成所有操作,仍然将某些流公开为结果,并在数据在数据库中更改时获得更新。这只是一些流组合的问题。 - Danail Alexiev

0

这是一个可行的解决方案,可以在Room v1.0.0中使用关系进行排序。

我用Kotlin编写了代码。

@Query("SELECT * FROM colis INNER JOIN step ON colis.idColis= step.idColis ORDER BY date DESC")
fun getAllColisWithSteps():Flowable<List<ColisWithSteps>>

这是 Java 版本:

@Query("SELECT * FROM colis INNER JOIN step ON colis.idColis= step.idColis ORDER BY date DESC")
public List<ColisWithSteps> getAllColisWithSteps()

更新的ColisWithSteps类:

public class ColisWithSteps {
        @Embedded
        public ColisEntity colisEntity;

        @Relation(parentColumn = "idColis", entityColumn = "idColis",entity = StepEntity.class)
        public List<StepEntity> stepEntityList;
    }

stepEntityList 没有被排序。Relation 从来不允许使用排序,它是一个独立于您实现的查询运行的查询,并且这就是为什么具有关系 POJO 的查询必须作为事务运行的原因。 - Shadow

-1

我曾经遇到过同样的问题,这里有一个解决方案:

@Query("SELECT * FROM step INNER JOIN colis ON step.idColis = colis.idColis ORDER BY date DESC")
fun getAllColisWithSteps():LiveData<List<ColisWithSteps>>

这里有一个解释 INNER JOIN 的例子:

要从多个表中查询数据,您可以使用 INNER JOIN 子句。INNER JOIN 子句将相关表中的列组合在一起。

假设您有两个表:A 和 B。

A 表具有 a1、a2 和 f 列。B 表具有 b1、b2 和 f 列。A 表使用名为 f 的外键列链接到 B 表。对于 A 表中的每行,INNER JOIN 子句将比较 f 列的值与 B 表中 f 列的值。如果 A 表中的 f 列的值等于 B 表中的 f 列的值,则结合 a1、a2、b1、b2 列的数据,并将此行包含在结果集中。

https://www.sqlitetutorial.net/sqlite-inner-join/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接