我将开始使用Spring和Hibernate为我的模型管理的REST应用程序的项目。将JSON对象映射到Hibernate实体
我知道,Spring允许你从HTTP请求(与@Consumes(JSON)
注释)获得的Java对象。如果这个Java对象也是一个Hibernate实体,是否有冲突?并且是嵌套对象工作(如@ManyToOne
关系)?
我将开始使用Spring和Hibernate为我的模型管理的REST应用程序的项目。将JSON对象映射到Hibernate实体
我知道,Spring允许你从HTTP请求(与@Consumes(JSON)
注释)获得的Java对象。如果这个Java对象也是一个Hibernate实体,是否有冲突?并且是嵌套对象工作(如@ManyToOne
关系)?
我们正在使用这种方法来简化设计并摆脱许多dtos(我们滥用它们太多)。基本上,它为我们工作。
然而,在我们的REST模型中,我们试图不公开其他公关的对象,你总是可以创建其他REST资源来访问它们。
因此,我们只是将@JsonIgnore
注释映射到@OneToMany
或@ManyToOne
这些关系映射中,使它们变成暂时的。
另一个问题,我看到的是,如果你仍然想退回这些关系,你将不得不使用Join.FETCH
策略为他们或移动交易管理提出了更高,所以当一个响应序列化到JSON(打开会话视图模式),该交易仍然存在。 在我看来,这两种解决方案不太好。
是的,这不会是一个问题,实际上是一个相当普遍的做法。
近年来我已经认识到,有时,然而,这是不是一个好主意基础上直接您的域名始终建立你的看法。你可以在这个帖子看看:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
它也被称为 “演示模式”:
http://martinfowler.com/eaaDev/PresentationModel.html
背后的想法基本上是以下几点:
试想一下,你的域条目的用户,谁看起来像这样:
@Entity
@Data
public class User {
@Id private UUID userId;
private String username;
@OneToMany private List<Permission> permissions;
}
现在让我们想象一下,你有,你想显示该用户的名称的视图,你完全不关心的权限。如果你使用你的方法立即返回用户到视图,Hibernate会从Permissions表中添加一个连接,因为事件虽然默认情况下是延迟加载的,但没有简单的方法发信号给jackson序列化器或者你是什么在这个特殊的场合你不关心他们,所以杰克逊会试着去处理它们(如果你的事务在你的对象被用于json序列化时仍然存在,否则你会得到一个令人讨厌的异常)。是的,您可以在权限字段上添加@JsonIgnore
注释,但是然后如果您在其他视图中需要该注释,则可以使用。
一个非常简单的例子,但你应该得到,有时你的域模型不能立即使用返回给表示层,由于这两个代码的可维护性和性能问题的想法。
既然你刚开始,也许你可以使用Spring Data REST?
这是项目:http://projects.spring.io/spring-data-rest/
这里有一些简单的例子:
正如你在例子中看到,有没有多余的超越@Entity的DTO注释了POJO。
正如我在this article中所解释的,使用Hibernate持久化JSON对象非常容易。
您不必手动创建所有这些类型的,你可以通过Maven的中央使用以下依赖简单地得到 他们:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency>
欲了解更多信息,请查看hibernate-types open-source project。
我写了an article关于如何在PostgreSQL和MySQL上映射JSON对象。
对于PostgreSQL,你需要发送的JSON对象以二进制形式:
public class JsonBinaryType
extends AbstractSingleColumnStandardBasicType<Object>
implements DynamicParameterizedType {
public JsonBinaryType() {
super(
JsonBinarySqlTypeDescriptor.INSTANCE,
new JsonTypeDescriptor()
);
}
public String getName() {
return "jsonb";
}
@Override
public void setParameterValues(Properties parameters) {
((JsonTypeDescriptor) getJavaTypeDescriptor())
.setParameterValues(parameters);
}
}
的JsonBinarySqlTypeDescriptor
看起来是这样的:
public class JsonBinarySqlTypeDescriptor
extends AbstractJsonSqlTypeDescriptor {
public static final JsonBinarySqlTypeDescriptor INSTANCE =
new JsonBinarySqlTypeDescriptor();
@Override
public <X> ValueBinder<X> getBinder(
final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
@Override
protected void doBind(
PreparedStatement st,
X value,
int index,
WrapperOptions options) throws SQLException {
st.setObject(index,
javaTypeDescriptor.unwrap(
value, JsonNode.class, options), getSqlType()
);
}
@Override
protected void doBind(
CallableStatement st,
X value,
String name,
WrapperOptions options)
throws SQLException {
st.setObject(name,
javaTypeDescriptor.unwrap(
value, JsonNode.class, options), getSqlType()
);
}
};
}
}
和JsonTypeDescriptor
这样的:
public class JsonTypeDescriptor
extends AbstractTypeDescriptor<Object>
implements DynamicParameterizedType {
private Class<?> jsonObjectClass;
@Override
public void setParameterValues(Properties parameters) {
jsonObjectClass = ((ParameterType) parameters.get(PARAMETER_TYPE))
.getReturnedClass();
}
public JsonTypeDescriptor() {
super(Object.class, new MutableMutabilityPlan<Object>() {
@Override
protected Object deepCopyNotNull(Object value) {
return JacksonUtil.clone(value);
}
});
}
@Override
public boolean areEqual(Object one, Object another) {
if (one == another) {
return true;
}
if (one == null || another == null) {
return false;
}
return JacksonUtil.toJsonNode(JacksonUtil.toString(one)).equals(
JacksonUtil.toJsonNode(JacksonUtil.toString(another)));
}
@Override
public String toString(Object value) {
return JacksonUtil.toString(value);
}
@Override
public Object fromString(String string) {
return JacksonUtil.fromString(string, jsonObjectClass);
}
@SuppressWarnings({ "unchecked" })
@Override
public <X> X unwrap(Object value, Class<X> type, WrapperOptions options) {
if (value == null) {
return null;
}
if (String.class.isAssignableFrom(type)) {
return (X) toString(value);
}
if (Object.class.isAssignableFrom(type)) {
return (X) JacksonUtil.toJsonNode(toString(value));
}
throw unknownUnwrap(type);
}
@Override
public <X> Object wrap(X value, WrapperOptions options) {
if (value == null) {
return null;
}
return fromString(value.toString());
}
}
现在,您需要在任何一个类上声明新类型VEL或在package-info.java包级descriptior:
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
与实体映射将是这样的:
@Type(type = "jsonb")
@Column(columnDefinition = "json")
private Location location;
这就是它!
如果我添加使用'@ JsonIgnore'注释但仍然需要两个不同的对象,我可以从我的HTTP请求中获得两个参数吗? (就像这样一种方法:'public void createPeople(@RequestBody AnEntity entityOne,AnOtherEntity entityTwo){...}') – Fractaliste
据我所知,您可以将请求主体仅映射到一个对象。所以我不太确定这是可能的。但是,请注意,如果需要,可以将@ JsonIgnore仅用于序列化/反序列化,方法是分别将它放在getter/setter上。此外,我仍然认为在一个请求中创建对象及其关系不是很RESTful。我会创建另一个REST端点来保存对象关系并使用它。 – oceansize