我将开始一个使用Spring管理的REST应用程序的项目,并为我的模型启用Hibernate。
我知道Spring允许您从HTTP请求中获取Java对象(带有@Consumes(JSON)
注释)。如果这个Java对象也是一个Hibernate实体,有没有冲突?嵌套对象是否正常工作(如@ManyToOne
关系)?
答案 0 :(得分:7)
正如我在this article中解释的那样,使用Hibernate持久化JSON对象非常容易。
您不必手动创建所有这些类型,您可以简单地获取 它们通过Maven Central使用以下依赖项:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency>
有关详细信息,请查看hibernate-types open-source project。
我写了关于如何在PostgreSQL和MySQL上映射JSON对象的an article。
对于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());
}
}
现在,您需要在类级别或 package-info.java 包级别描述符中声明新类型:
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
实体映射将如下所示:
@Type(type = "jsonb")
@Column(columnDefinition = "json")
private Location location;
就是这样!
答案 1 :(得分:3)
是的,这不是一个问题,实际上是一种相当普遍的做法。
近年来,我逐渐意识到,有时候,总是直接根据您的域构建您的视图并不是一个好主意。你可以看一下这篇文章:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
它也被称为&#34;演示模型&#34;:
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序列化器发送信号或者无论您是什么使用,你在这个特殊的场合不关心它们,所以jackson会尝试解除它们(如果你的对象在json序列化时你的事务仍处于活动状态,否则你会得到一个令人讨厌的异常)。是的,您可以在权限字段中添加@JsonIgnore
注释,但是然后如果您在其他视图中需要它,则会被搞砸。
这是一个非常基本的示例,但您应该知道,由于代码可维护性和性能问题,有时您的域模型无法立即返回到表示层。
答案 2 :(得分:3)
您可以映射JSON请求,而无需在REST Web服务上使用任何库(Jersy)
此代码示例:
此休眠实体称为book:
@Entity
@Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setter and getters
}
此网络服务功能
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
这是示例json请求:
{
"bookName" : "Head First Java"
,
"author" : {
"id" : 1
}
}
答案 3 :(得分:2)
我们使用这种方法来简化设计并摆脱许多dtos(我们滥用它们太多)。基本上,它对我们有用。
但是,在我们的REST模型中,我们试图不公开对象的其他关系,因为您总是可以创建另一个REST资源来访问它们。
因此,我们只需将@JsonIgnore
注释添加到@OneToMany
或@ManyToOne
等关系映射中,使其成为瞬态。
我发现另一个问题是,如果您仍然希望返回这些关系,则必须对它们使用Join.FETCH
策略或更高的事务管理,以便在将响应序列化为JSON时事务仍然存在(Open Session In查看模式)。
在我看来,这两种解决方案并不是那么好。
答案 4 :(得分:1)
既然您刚刚开始,也许您可以使用Spring Data REST?
这是项目:http://projects.spring.io/spring-data-rest/
以下是一些简单的例子:
正如您在示例中所看到的,除了@Entity带注释的POJO之外,没有额外的DTO。