Android Room:将 json 结果转换为 db 对象的有效方法
Android Room: Efficient way to transform json result into db object
问题
我有一个从 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 接口中的一个方法内实现转换。但随后我将不得不创建一个新对象并手动设置所有值。
- 我最初认为类型转换器可以工作,但它们似乎可以转换单个列。
您可以为您的相关 POJO 使用 @Embedded 注释,它引用另一个 class.
你可以这样做:
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 在文章 class 的列表中,您需要为此使用类型转换器)
引用自here
您所要做的就是为您的 POJO(模型 Class)使用 @Embedded
注释,它将引用另一个 class。然后创建一个类型转换器 class。
@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;
}
根据文档 here "There is no limit on the number of Entity or Dao classes but they must be unique within the Database." 所以我认为您可以简单地在扩展 RoomDatabase
的数据库 class 中声明不同的 classes。
您是否尝试过简单地将不同的 POJO 声明为不同的实体并将它们全部包含在同一个数据库中class?
例如:
// 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
class 中使用 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;
}
}
这解决了您尝试过的问题。现在开始你的陈述 "I believe I need to transform the object to one which matches the database entity model." 实际上,不一定。您可以对实体的不同创建实例或实现使用 @Ignore
注释,只要至少有一个默认构造函数包含 entry
的 primary 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
,它们可以全部在同一个 class 中并导入到特定的数据库 @TypeConverters(MyConverters.class)
。希望这对您有所帮助!
问题
我有一个从 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 接口中的一个方法内实现转换。但随后我将不得不创建一个新对象并手动设置所有值。
- 我最初认为类型转换器可以工作,但它们似乎可以转换单个列。
您可以为您的相关 POJO 使用 @Embedded 注释,它引用另一个 class.
你可以这样做:
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 在文章 class 的列表中,您需要为此使用类型转换器)
引用自here
您所要做的就是为您的 POJO(模型 Class)使用 @Embedded
注释,它将引用另一个 class。然后创建一个类型转换器 class。
@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;
}
根据文档 here "There is no limit on the number of Entity or Dao classes but they must be unique within the Database." 所以我认为您可以简单地在扩展 RoomDatabase
的数据库 class 中声明不同的 classes。
您是否尝试过简单地将不同的 POJO 声明为不同的实体并将它们全部包含在同一个数据库中class?
例如:
// 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
class 中使用 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;
}
}
这解决了您尝试过的问题。现在开始你的陈述 "I believe I need to transform the object to one which matches the database entity model." 实际上,不一定。您可以对实体的不同创建实例或实现使用 @Ignore
注释,只要至少有一个默认构造函数包含 entry
的 primary 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
,它们可以全部在同一个 class 中并导入到特定的数据库 @TypeConverters(MyConverters.class)
。希望这对您有所帮助!