我们正在创建基于 Play 框架 2.1.x 的 RESTFul API,它以 JSON 格式传输/接受数据。创建、读取和删除操作很容易实现,但我们却被更新操作卡住了。
以下是我们拥有的实体:
事件:
@Entity
public class Event extends Model {
@Id
public Long id;
@NotEmpty
public String title;
@OneToOne(cascade = CascadeType.ALL)
public Location location;
@OneToMany(cascade = CascadeType.ALL)
public List<Stage> stages = new LinkedList<Stage>();
...
}
地点:
@Entity
public class Location extends Model {
@Id
public Long id;
@NotEmpty
public String title;
public String address;
...
}
阶段:
@Entity
public class Stage extends Model {
@Id
public Long id;
@NotEmpty
public String title;
public int capacity;
...
}
在我们的路由器中,我们有以下条目:
PUT /events/:id controllers.Event.updateEvent(id: Long)
控制器中的updateEvent方法如下所示(注意:我们使用 Jackson 库将对象映射到 JSON 并返回):
@BodyParser.Of(BodyParser.Json.class)
public static Result updateEvent(Long id) {
Event event = Event.find.byId(id);
Http.RequestBody requestBody = request().body();
JsonNode jsonNode = requestBody.asJson();
try {
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.readerForUpdating(event);
event = reader.readValue(jsonNode);
event.save();
} catch (IOException e) {
e.printStackTrace();
}
return ok();
}
在我们从数据库中获取事件后,通过使用 ObjectReader 从 JSON 读取来更新其值,我们尝试保存更新的事件并获取异常(我们在尝试更新阶段列表时得到类似的异常):
org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_9F ON PUBLIC.LOCATION(ID)"; SQL statement: insert into location (id, title, address) values (?,?,?) [23505-168]
根据 H2 日志框架尝试对位置执行插入操作,但由于指定 id 的位置已经存在而失败。我们已经进一步调查了 ant,当我们从 DB 获取 Event 时,由于延迟获取,位置未加入。看起来问题是在保存与我们的事件有关系的其他实体时发生的。我们尝试通过执行以下操作来强制获取位置操作:
Event event = Ebean.find(Event.class).fetch("location").where().eq("id", id).findUnique();
但是当我们使用 ObjectReader 的 readValue 方法更新此事件并保存事件时,我们仍然会得到相同的异常。
我们还尝试从 JSON 中创建单独的 Event 对象,并逐个字段地从 DB 中更新 Event(我们自己实现了合并操作)并且它有效,但看起来很奇怪,框架没有提供任何方法来合并和更新传递数据的实体从客户。
有人可以建议如何解决这个问题吗?任何展示如何将实体与来自客户端的 JSON 数据合并并在存储中更新它的示例都将受到高度赞赏。