- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我使用 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<Post> posts;
}
我设置了一个简单的 SqlLite 模式来根据模型的类型和 ID 缓存和索引这些模型。
我的问题是,如果模型包含其他模型的字段,我不想将 field 模型作为一个整体存储在数据库中。 ArrayList Venue.posts 中的每个帖子都应该单独保存。
最好的方法是什么?
最佳答案
在使用 JSON 创建自己的数据库 -> POJO 实现时,我遇到了类似的问题。这就是我解决问题的方法,对我来说效果很好。
让我们以您的 Post
对象为例。它需要很容易地表示为 JSON 对象并从 JSON 字符串创建。此外,它需要能够保存到数据库中。我已经根据这两个条件分解了我使用的类的继承关系:
Post
-> DatabaseObject
-> JsonObject
-> LinkedHashMap
从最基本的表示开始,一个JsonObject
,它是一个扩展的LinkedHashMap
。 Maps
由于其键值映射而非常适合表示 JSON 对象。这是 JsonObject
类:
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A <code>JsonObject</code> represents a JSON object, which begins and ends
* with curly braces '{' '}' and contains key-value pairs separated by a
* colon ':'.
* <p>
* In implementation, this is simply an extended <code>LinkedHashMap</code> to
* represent key-value pairs and to preserve insert order (which may be
* required by some JSON implementations, though is not a standard).
* <p>
* Additionally, calling <code>toString()</code> on the <code>JsonObject</code>
* will return a properly formatted <code>String</code> which can be posted as
* a value JSON HTTP request or response.
* @author Andrew
* @param <V> the value class to use. Note that all keys for a
* <code>JsonObject</code> are <code>Strings</code>
*/
public class JsonObject<V> extends LinkedHashMap<String, V> {
/**
* Creates a new empty <code>JsonObject</code>.
*/
public JsonObject() {
}
/**
* Creates a new <code>JsonObject</code> from the given HTTP response
* <code>String</code>.
* @param source HTTP response JSON object
* @throws IllegalArgumentException if the given <code>String</code> 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 new <code>JsonObject</code> from the given <code>Map</code>.
* @param map a <code>Map</code> of key-value pairs to create the
* <code>JsonObject</code> from
*/
public JsonObject(Map<? extends String, ? extends V> map) {
putAll(map);
}
/**
* Returns a JSON formatted <code>String</code> that properly represents
* this JSON object.
* <p>
* This <code>String</code> may be used in an HTTP request or response.
* @return JSON formatted JSON object <code>String</code>
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
Iterator<Map.Entry<String, V>> iter = entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, V> 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
与数据库通信的功能。在我的实现中,Database
对象只是一个抽象类。我指定 Database
如何将 DatabaseObjects
保存到别处,无论是通过 JDBC 还是 JSON over HTTP。
请记住,我们使用 Map
来表示我们的对象。对于您的帖子
,这意味着您拥有三个“属性”(我在文档中称其为关键值):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 <code>DatabaseObject</code> represents a single row of data from a
* specific table within a database.
* <p>
* 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.
* <p>
* The <code>DatabaseObject</code> works with key-value pairs as an
* extended <code>LinkedHashMap</code>. It defines one property,
* <code>DatabaseObject.ROW_ID</code> 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
* <code>Integer.MAX_VALUE</code> is well suited for most databases as a maximum
* row count per table).
* <p>
* The key and value pairs should be accessed by implementing getter and
* setter methods, not by the get and put methods provided by the
* <code>LinkedHashMap</code>. This is to ensure proper <code>Class</code>
* type casting for each value.
* <p>
* A <code>DatabaseObject</code> itself is also an extension of a
* <code>JsonObject</code>, and <code>toString()</code> may be called on
* it to provide a JSON notated <code>DatabaseObject</code>.
* <p>
* 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 <code>DatabaseObject</code> should be converted back into its
* implementing object form and saved when using web services.
* <p>
* The parameter <code>T</code> should be set to the class of the implementing
* <code>DatabaseObject</code>. This will allow proper class casting when
* returning instances of the implementation, such as in the <code>load()</code>
* methods.
* @param <T> the type of <code>DatabaseObject</code>
* @author Andrew
*/
public abstract class DatabaseObject<T extends DatabaseObject>
extends JsonObject<Object> implements Cloneable{
/**The property for the row ID*/
public final static String ROW_ID = "rowId";
/**
* Creates a new empty <code>DatabaseObject</code>.
*/
public DatabaseObject() {
}
/**
* {@inheritDoc }
* <p>
* This get method will additionally check the <code>Class</code> of
* the returned value and cast it if it is a <code>String</code> but
* matches another <code>Class</code> type such as a number.
* @see #doGet(java.lang.String, boolean)
*/
@Override
public Object get(Object key) {
//From here you can specify additional requirements before retrieving a value, such as class checking
//This is optional of course, and doGet() calls super.get()
return doGet(String.valueOf(key), true);
}
/**
* {@inheritDoc }
* <p>
* This get method will additionally check the <code>Class</code> of
* the given value and cast it if it is a <code>String</code> but
* matches another <code>Class</code> type such as a number.
* @see #doPut(java.lang.String, java.lang.Object, boolean)
*/
@Override
public Object put(String key, Object value) {
//Like doGet(), doPut() goes through additional checks before returning a value
return doPut(key, value, true);
}
//Here are some example getter/setter methods
//DatabaseObject provides an implementation for the row ID column by default
/**
* Retrieves the row ID of this <code>DatabaseObject</code>.
* <p>
* If the row ID could not be found, -1 will be returned. Note that
* a -1 <i>may</i> indicate a new <code>DatabaseObject</code>, but it
* does not always, since not all <code>Databases</code> support
* retrieving the last inserted ID.
* <p>
* While the column name might not correspond to "rowId", this
* method uses the <code>DatabaseObject.ROW_ID</code> property. All
* objects must have a unique identifier. The name of the column
* should be registered in the constructor of the object.
* @return the <code>DatabaseObject's</code> row ID, or -1 if it is not set
*/
public int getRowId() {
//getProperty(), while not included, simply returns a default specified value
//if a value has not been set
return getProperty(ROW_ID, -1);
}
/**
* Sets the row ID of this <code>DatabaseObject</code>.
* <p>
* <b>Note: this method should rarely be used in implementation!</b>
* <p>
* The <code>DatabaseObject</code> will automatically set its row ID when
* retrieving information from a <code>Database</code>. If the row ID
* is forcibly overriden, this could overwrite another existing row entry
* in the database table.
* @param rowId the row ID of the <code>DatabaseObject</code>
*/
public void setRowId(int rowId) {
//And again, setProperty() specifies some additional conditions before
//setting a key-value pair, but its not needed for this example
setProperty(ROW_ID, rowId);
}
//This is where your question will be answered!!
//Since everything in a DatabaseObject is set as a key-value pair in a
//Map, we don't have to use reflection to look up values for a
//specific object. We can just iterate over the key-value entries
public synchronized void save(Database database) throws SaveException {
StringBuilder sql = new StringBuilder();
//You would need to check how you want to save this, let's assume it's
//an UPDATE
sql.append("UPDATE ").append(getTableName()).append(" SET ");
Iterator<String, Object> iter = entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Object> entry = iter.next();
String property = entry.getKey();
Object value = entry.getValue();
if (value instanceof DatabaseObject) {
((DatabaseObject)value).save();
}
else if (value instanceof Collection) {
for (Object v : (Collection)value) {
((DatabaseObject)value).save();
}
}
//However many other checks you want, such as Maps, Arrays, etc
else {
sql.append(property); //Assuming the property equals the column name
sql.append("='").append(value).append("'");
}
if (iter.hasNext()) {
sql.append(", ");
}
}
//getIdColumn() would retrieve which column is being used as the identifier
sql.append("WHERE ").append(getIdColumn()).append("=").append(getRowId());
//Finally, take our SQL string and save the value to the Database
//For me, I could simply call database.update(sql), and
//the abstracted Database object would determine how to
//send that information via HTTP as a JSON object
//Of course, your save() method would vary greatly, but this is just a quick
//and dirty example of how you can iterate over a Map's
//key-value pairs and take into account values that are
//DatabaseObjects themselves that need to be saved, or
//a Collection of DatabaseObjects that need to be saved
}
/**
* Retrieves the name of the database table that this
* <code>DatabaseObject</code> pulls its information from.
* <p>
* It is recommended to declare a final and static class variable that
* signifies the table name to reduce resource usage.
* @return name of the database table
*/
public abstract String getTableName();
}
对于 TL;DR 版本:
Post
是一个 DatabaseObject
。
DatabaseObject
是一个JsonObject
,它是一个LinkedHashMap
。
LinkedHashMap
设定了存储键值对的标准。键 = 列名,值 = 列值。
JsonObject
什么都不做,只是提供了一种将 LinkedHashMap
打印为 JSON 字符串的方法。
DatabaseObject
指定有关如何保存到数据库的逻辑,包括在值是另一个 DatabaseObject
的情况下,或者如果值包含 DatabaseObject
,例如集合。
^ -- 这意味着您只需编写一次“保存”逻辑。当您调用 Post.save()
时,它会保存帖子。当您调用 Venue.save()
时,它会保存地点列(如果有的话),以及 ArrayList
中的每个单独的 Post
。
为了额外的乐趣,下面是您的 Post
和 Venue
对象的样子:
public class Post extends DatabaseObject<Post> {
//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<Venue> {
//This is just a key property, not a column
public final static String POSTS = "Posts";
public Venue() {
setPosts(new ArrayList());
}
public List<Post> getPosts() {
return (List<Post>)get(POSTS);
}
public void setPosts(List<Post> 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
值并将列值对保存到数据库,同时考虑集合或可保存的值数据库对象
。
现在您所要做的就是调用 Venue.save()
,您所有的 Post
对象也将被适本地保存。
关于java - 将嵌套的 Pojo 对象作为单独的对象存储在数据库中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19726894/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!