将嵌套的Pojo对象存储为数据库中的单个对象
我使用jackson将json字符串映射到我的HTModel类,这基本上是一个简单的Pojo。
class HTModel{} public class Post extends HTModel { public String id; public String content; public String author; }
即使这些类嵌套在一起,这也很有效。
public class Venue extends HTModel { public ArrayList posts; }
我设置了一个简单的SqlLite模式来缓存和索引这些模型的类型和ID。
我的问题是,如果模型包含其他模型的字段,我不想将整个数据库中的Venue模型存储起来。 ArrayList Venue.posts中的每个post都应单独保存。
什么是最好的方法呢?
在使用JSON创建自己的数据库 – > POJO实现时,我遇到了类似的问题。 这就是我解决这个问题的方法,对我来说效果很好。
我们以Post
对象为例。 它需要很容易地表示为JSON对象,并从JSON字符串创建。 此外,它需要能够保存到数据库。 我已根据这两个条件分解了我使用的类的heirachy:
Post -> DatabaseObject -> JsonObject -> LinkedHashMap
从最基本的表示开始,一个JsonObject
,它是一个扩展的LinkedHashMap
。 由于其键值映射, Maps
可以很好地表示JSON对象。 这是JsonObject
类:
import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * A
JsonObject
represents a JSON object, which begins and ends * with curly braces '{' '}' and contains key-value pairs separated by a * colon ':'. ** In implementation, this is simply an extended
LinkedHashMap
to * represent key-value pairs and to preserve insert order (which may be * required by some JSON implementations, though is not a standard). ** Additionally, calling
toString()
on theJsonObject
* will return a properly formattedString
which can be posted as * a value JSON HTTP request or response. * @author Andrew * @paramthe value class to use. Note that all keys for a * JsonObject
areStrings
*/ public class JsonObjectextends LinkedHashMap { /** * Creates a new empty JsonObject
. */ public JsonObject() { } /** * Creates a newJsonObject
from the given HTTP response *String
. * @param source HTTP response JSON object * @throws IllegalArgumentException if the givenString
is not * a JSON object, or if it is improperly formatted * @see JsonParser#getJsonObject(java.lang.String) */ public JsonObject(String source) throws IllegalArgumentException { this(JsonParser.getJsonObject(source)); } /** * Creates a newJsonObject
from the givenMap
. * @param map aMap
of key-value pairs to create the *JsonObject
from */ public JsonObject(Map extends String, ? extends V> map) { putAll(map); } /** * Returns a JSON formattedString
that properly represents * this JSON object. ** This
String
may be used in an HTTP request or response. * @return JSON formatted JSON objectString
*/ @Override public String toString() { StringBuilder sb = new StringBuilder("{"); Iterator> iter = entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); sb.append(JsonParser.toJson(entry.getKey())); sb.append(':'); V value = entry.getValue(); sb.append(JsonParser.toJson(value)); if (iter.hasNext()) { sb.append(','); } } sb.append("}"); return sb.toString(); } }
简单来说,它只是一个表示JSON对象的LinkedHashMap
,它可以通过调用toString()
快速转换为JSON字符串,也可以使用我创建的JsonParser
类从JSON字符串创建。
可能如果您已经使用像Jackson这样的JSON解析器,您可以重做一些事情来使用该API。
接下来是Post
, DatabaseObject
,它提供Post
function以与数据库通信。 在我的实现中, Database
对象只是一个抽象类。 我指定Database
如何在其他地方保存DatabaseObjects
,无论是通过JDBC还是通过HTTP的JSON。
请记住,我们使用Map
来表示我们的对象。 对于你的Post
,这意味着你有三个“属性”(我称之为文档中的键值):ID,内容和作者。
这是DatabaseObject
(剪裁下来)的样子。 请注意save()
方法,这是我将回答你的问题的地方。
import java.text.ParseException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * The
DatabaseObject
represents a single row of data from a * specific table within a database. ** The object should implement getters and setters for each column, and is * responsible for registering the correct table name and column names, as * well as default values for those columns, in case a default value is * not supported by the database table. *
* The
DatabaseObject
works with key-value pairs as an * extendedLinkedHashMap
. It defines one property, *DatabaseObject.ROW_ID
which represents the unique * identifier column for a table row. This column should always be an * integer value. (Future implementations may allow for long values, but *Integer.MAX_VALUE
is well suited for most databases as a maximum * row count per table). ** The key and value pairs should be accessed by implementing getter and * setter methods, not by the get and put methods provided by the *
LinkedHashMap
. This is to ensure properClass
* type casting for each value. ** A
DatabaseObject
itself is also an extension of a *JsonObject
, andtoString()
may be called on * it to provide a JSON notatedDatabaseObject
. ** When using JSON however, keep in mind that the keys may not correspond * exactly with the table column names, even though that is the recommendation. * The
DatabaseObject
should be converted back into its * implementing object form and saved when using web services. ** The parameter
T
should be set to the class of the implementing *DatabaseObject
. This will allow proper class casting when * returning instances of the implementation, such as in theload()
* methods. * @paramthe type of DatabaseObject
* @author Andrew */ public abstract class DatabaseObjectextends JsonObject
对于TL; DR版本:
Post
是一个DatabaseObject
。
DatabaseObject
是一个JsonObject
,它是一个LinkedHashMap
。
LinkedHashMap
设置了存储键值对的标准。 Key =列名,值=列值。
JsonObject
除了提供一种方法将LinkedHashMap
打印为JSON字符串之外什么都不做。
DatabaseObject
指定有关如何保存到数据库的逻辑,包括值是另一个DatabaseObject
,或者值是否包含DatabaseObject
,例如使用Collections。
^ – 这意味着您只需编写一次“保存”逻辑。 当你调用Post.save()
它会保存post。 当你调用Venue.save()
,它会保存场地列(如果有的话),以及ArrayList
中的每个Post
。
为了获得额外的乐趣,这里是Post
和Venue
对象的样子:
public class Post extends DatabaseObject { //The column names public final static String POST_ID = "PostID"; public final static String CONTENT = "Content"; public final static String AUTHOR = "Author"; public Post() { //Define default values } public int getPostId() { return (Integer)get(POST_ID); } public void setPostId(int id) { put(POST_ID, id); } public String getContent() { return (String)get(CONTENT); } public void setContent(String content) { put(CONTENT, content); } public String getAuthor() { return (String)getAuthor(); } public void setAuthor(String author) { put(AUTHOR, author); } @Override public String getTableName() { return "myschema.posts"; } }
请注意,我们不再声明类变量,只是存储值的列名。 我们在getter / setter方法中定义变量的类。
import java.util.ArrayList; import java.util.List; public class Venue extends DatabaseObject { //This is just a key property, not a column public final static String POSTS = "Posts"; public Venue() { setPosts(new ArrayList()); } public List getPosts() { return (List )get(POSTS); } public void setPosts(List posts) { put(POSTS, posts); } public void addPost(Post post) { getPosts().add(post); } @Override public String getTableName() { //Venue doesn't have a table name, it's just a container //In your DatabaseObject.save() method, you can specify logic to //account for this condition return ""; } }
额外终极TL; DR版本:
使用Map
存储变量,而不是在类中定义它们。
创建一个save()
方法逻辑,该逻辑迭代Map
值并将列值对保存到数据库,同时考虑作为Collections或可保存DatabaseObjects
。
现在你所要做的就是调用Venue.save()
,你的所有Post
对象也会被妥善保存。
使用@JsonIdentityInfo
/ @JsonIdentityReference
(将post序列化为id)和自定义反序列化器(可以通过id读取DB中的post)的可能解决方案:
public class SerializationTest { static class HTModel{} @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id") public static class Post extends HTModel { public int id; public String content; public String author; } static class Venue extends HTModel { @JsonIdentityReference(alwaysAsId=true) @JsonDeserialize(using = VenuePostsDeserializer.class) public ArrayList posts; } static class VenuePostsDeserializer extends JsonDeserializer> { @Override public ArrayList deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { ArrayList posts = new ArrayList (); if (parser.getCurrentToken() != JsonToken.START_ARRAY) { return posts; } parser.nextValue(); try { do { int postId = parser.getIntValue(); // FIXME: fetch the corresponding post from DB instead of creating a stub Post post = new Post(); post.id = postId; post.content = String.format("Post #%d content", postId); post.author = String.format("Post #%d author", postId); posts.add(post); parser.nextValue(); } while(parser.getCurrentToken() != JsonToken.END_ARRAY); } catch (Exception exception) { System.out.println(exception.getMessage()); } return posts; } } public static void main(String[] args) { ObjectMapper mapper = new ObjectMapper(); Venue venue = new Venue() {{ posts = new ArrayList () {{ add(new Post() {{ id = 2; }}); add(new Post() {{ id = 42; }}); }}; }}; try { String serializedVenue = mapper.writeValueAsString(venue); System.out.println("Serialized venue: " + serializedVenue); Venue deserializedVenue = mapper.readValue(serializedVenue, Venue.class); System.out.println("Deserialized venue:"); for (Post post : deserializedVenue.posts) { System.out.println(String.format("Post: id=%d, content=%s, author=%s", post.id, post.content, post.author)); } } catch (Exception exception) { System.out.println(exception.getMessage()); } } }
输出:
Serialized venue: {"posts":[2,42]} Deserialized venue: Post: id=2, content=Post #2 content, author=Post #2 author Post: id=42, content=Post #42 content, author=Post #42 author
编辑 :我将Post.id
更改为int
(来自String
),以便在示例中使用更简单的反序列化器。
- jackson的readValue和readTree:何时使用哪个?
- 用spring反序列化JSON:未解析的前向引用Jackson Exception
- java.lang.ClassNotFoundException:org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
- jackson,序列化参考的一个属性
- 使用Jackson将Json反序列化为另一个类层次结构
- jackson的只写属性
- 使用Spring Data REST时如何更改Jacksons Configuration?
- SpringMVC Jackson2HttpMessageConverter定制不起作用
- Jackson ObjectMapper – 未映射“_”的属性