Android Room:将 json 结果转换为 db 对象的有效方法

Posted

技术标签:

【中文标题】Android Room:将 json 结果转换为 db 对象的有效方法【英文标题】:Android Room: Efficient way to transform json result into db object 【发布时间】:2019-03-08 05:58:09 【问题描述】:

问题

我有一个从 API 调用解析出来的 POJO,看起来像这样

public class Article 

  public Long id;

  @Expose
  @SerializedName("section")
  public String section;

  @Expose
  @SerializedName("title")
  public String title;

  @Expose
  @SerializedName("topics")
  public List<String> topics;

  @Expose
  @SerializedName("media")
  public List<Media> media;

为了尽量减少冗余和重复,我希望创建这样的架构

@Entity(foreignKeys =  
          @ForeignKey(entity = Article.class, parentColumns = "id", childColumns = "articleId"), 
          @ForeignKey(entity = Topic.class, parentColumns = "id", childColumns = "topicId"),
          @ForeignKey(entity = Media.class, parentColumns = "id", childColumns = "mediaId")

public class Articles 

    @PrimaryKey
    public Long articleId; 

    @ColumnInfo(name = "topic_id")
    public Long topicId;

    @ColumnInfo(name = "media_id")
    public Long mediaId;


@Entity
public class Article 
    // Left out


@Entity
public class Media 
    // Left out

如您所见,当我调用 DAO 方法访问数据库时,我不能直接传入 pojo 对象(除非我弄错了)。我相信我需要将对象转换为与数据库实体模型匹配的对象。

问题

android 框架是否提供了一种从 POJO 转换为数据库模型对象的自然方式?除了自己手动转换之外,还有其他方法吗?

我尝试过的事情

我知道我可以在我的 DAO 接口中的方法内实现转换。但是我必须创建一个新对象并手动设置所有值。 我最初认为类型转换器可以工作,但它们似乎可以转换单个列。

【问题讨论】:

只需创建一个类型转换器类。 【参考方案1】:

您所要做的就是为您的 POJO(Model Class) 使用 @Embedded 注释,它将引用另一个类。然后创建一个类型转换器类。

 @Embedded(prefix = "media")
private Meida media;

@TypeConverters(TypeConvertorClass.class)
@Database(entities = Article .class,Media.class, version = 1, exportSchema = false)
public abstract class `DataBaseExample` extends RoomDatabase 



public class Converters 
    @TypeConverter
    public static ArrayList<String> fromString(String value) 
        Type listType = new TypeToken<ArrayList<String>>() .getType();
        return new Gson().fromJson(value, listType);
    

    @TypeConverter
    public static String fromArrayLisr(ArrayList<String> list) 
        Gson gson = new Gson();
        String json = gson.toJson(list);
        return json;
    



    public class TypeConvertorClass 
    @TypeConverter
    public static Media getMedia(String longId) 
        return longId== null ? null : new Meida();
    


  @Entity(tableName = "Article")
    public class Article 
        @ColumnInfo (name = "article_id")
        public Long id;

        @Expose
    @SerializedName("section")
    public String section;

    @Expose
    @SerializedName("title")
    public String title;

    @Expose
    @SerializedName("topics")
    public List<String> topics;

   @Embedded(prefix = "media") // We need relation to Media table
    @Expose
    @SerializedName("media")
    public List<Media> media;


public class Media 
    @ColumnInfo (name = "media_id")
    public Long id;

【讨论】:

我可以看到 typeconverters 确实简化了这个过程。但是它如何处理我需要从“主题”之类的列表类型对象中查询的情况。如果我想获取某个主题的所有文章怎么办?如果我将“主题”分成它自己的实体,我相信我可以通过选择和连接查询来做到这一点。如果我使用类型转换器,它仍然有效吗? 我认为您不能使用DAO 中的查询来注释作为其中一列中的值保存的列表。您可能必须选择每个列表并 for/while 循环要匹配的值。您可以通过一些创造性的@Query 语句匹配使用TypeConverters 的特定对象,例如:@Query("Select article_entry Where media = :mediaItem) abstract void loadArticleByMediaItem(Media mediaItem); 在你道中只需使用查询即可。 @Query("SELECT * FROM topic where topic.topic LIKE :topic") List getSpecificTopicList(String topic); 我同意@AtifAbbAsi,这就是为什么我们首先使用TypeConverters,我还没有尝试过,但我认为它对你有用 刚刚阅读了另一个 *** 帖子。该查询有效,因为它在转换后的主题字符串中搜索相似主题。我将使用类型转换器。感谢您的帮助!【参考方案2】:

您可以对引用另一个类的相关 POJO 使用 @Embedded 注释。

你可以这样做:

Article.java

@Entity(tableName = "Article")
public class Article 
    @ColumnInfo (name = "article_id")
    public Long id;

    @Expose
    @SerializedName("section")
    public String section;

    @Expose
    @SerializedName("title")
    public String title;

    @Expose
    @SerializedName("topics")
    public List<String> topics;

    @Embedded // We need relation to Media table
    @Expose
    @SerializedName("media")
    public List<Media> media;

Media.java

public class Media 
    @ColumnInfo (name = "media_id")
    public Long id;

那么现在,您可以直接将这个 POJO 用作 ROOM 的实体。


请注意:

虽然我不确定你将如何处理这种关系(因为 Media obj 在 Article 类的列表中,你需要为此使用类型转换器)

来自here的参考

【讨论】:

【参考方案3】:

根据文档here“实体或道类的数量没有限制,但它们在数据库中必须是唯一的。”所以我认为您可以简单地在扩展RoomDatabase 的数据库类中声明不同的类。

您是否尝试过简单地将不同的 POJO 声明为不同的实体并将它们全部包含在同一个数据库类中?

例如:

  // Article, Topic and Media are classes annotated with @Entity.
  @Database(version = 1, entities = Article.class, Topic.class, Media.class)
  abstract class MyDatabase extends RoomDatabase 
  // ArticleDao is a class annotated with @Dao.
  abstract public ArticleDao articleDao();
  // TopicDao is a class annotated with @Dao.
  abstract public TopicDao topicDao();
  // MediaDao is a class annotated with @Dao.
  abstract public MediaDao mediaDao();

这可能对冗余没有帮助,但我最初的想法也是类型转换器。实际上,我什至使用TypeConverters 和单个Dao 成功地将parcelable 对象作为我的Room Database 中的列实现。

您是否尝试过在您的TypeConverter 课程中使用Gson?我相信this article 更直接地解决了您的问题。它是在房间数据库中存储对象的指南。同样,诀窍在于类型转换器并将您的对象声明为 Gson 的类型标记。例如:

public class Converters 
   @TypeConverter
   public static List<Media> fromStringToList(String mediaListString) 
      Type myType = new TypeToken<List<Media>>() .getType();
      return new Gson().fromJson(mediaListString, myType);
   
   @TypeConverter
   public static String fromMediaListToString(List<Media> mediaItems) 
      if (mediaItems== null || mediaItems.size() == 0) 
        return (null);
      
      Gson gson = new Gson();
      Type type = new TypeToken<List<VideoParcelable>>() 
      .getType();
      String json = gson.toJson(mediaItems, type);
      return json;
   

这解决了您尝试过的事情。现在开始您的陈述“我相信我需要将对象转换为与数据库实体模型匹配的对象。”其实,不一定。您可以将@Ignore 注解用于您的实体的不同创建实例或实现,只要至少有一个默认构造函数包含entryprimary key。在你的情况下:

@Entity(foreignKeys =  
      @ForeignKey(entity = Article.class, parentColumns = "id", childColumns = 
      "articleId"), 
      @ForeignKey(entity = Topic.class, parentColumns = "id", childColumns = 
      "topicId"),
      @ForeignKey(entity = Media.class, parentColumns = "id", childColumns = 
      "mediaId")


public class ArticlesEntry 

@PrimaryKey
public Long articleId; 
@ColumnInfo(name = "topic_id")
public Long topicId;
@ColumnInfo(name = "media_id")
public Long mediaId;

private Article articleObject;
private Media mediaObject;

//default constructor
public ArticlesEntry(int id) 
    this.articleId = id;


//You can call this anytime you add to the database with media object input
@Ignore
public ArticlesEntry(int id, Media inMedia) 
    this.articleId = id;
    this.mediaObject= inMedia;

//You can create many of these and insert as needed, the left out variables of the 
//are null, note that id has to be passed b/c your primary key isn't set to 
//autogenerate
@Ignore
public ArticlesEntry(int id, Article inArticle) 
    this.articleId = id;
    this.articleObject= articleObject;

//Or both objects:
@Ignore
public ArticlesEntry(int id, Media inMedia, Article inArticle) 
    this.articleId = id;
    this.mediaObject = inMedia;
    this.articleObject= articleObject;


//getters and setters here...


如果您像上面一样创建ArticlesEntry,则需要创建并包含不同的TypeConverters,它们都可以在同一个类中并使用@TypeConverters(MyConverters.class) 导入到特定的数据库中。希望这会有所帮助!

【讨论】:

以上是关于Android Room:将 json 结果转换为 db 对象的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

Android Room 不确定如何将光标转换为方法的返回类型问题

Android:将 Json 对象转换为 Strings.xml

在 Android 中将 MySql 查询转换为 SQL Room 查询 - kotlin

如何在 ROOM android 中修复“不确定如何将光标转换为此方法的返回类型”

将 TypeConverters 应用到 Dao Android Room

Android:如何使 Kotlin 中所有对象列表的类型转换器(用于 Room)通用