【问题标题】:Android Room: Efficient way to transform json result into db objectAndroid Room:将 json 结果转换为 db 对象的有效方法
【发布时间】: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 接口中的方法内实现转换。但是我必须创建一个新对象并手动设置所有值。
  • 我最初认为类型转换器可以工作,但它们似乎可以转换单个列。

【问题讨论】:

  • 只需创建一个类型转换器类。

标签: android type-conversion normalization android-room pojo


【解决方案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,我还没有尝试过,但我认为它对你有用
  • 刚刚阅读了另一个 stackoverflow 帖子。该查询有效,因为它在转换后的主题字符串中搜索相似主题。我将使用类型转换器。感谢您的帮助!
【解决方案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) 导入到特定的数据库中。希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-24
      • 2019-12-23
      • 2018-05-27
      • 1970-01-01
      相关资源
      最近更新 更多