2014-03-29 237 views
5

我将开始使用Spring和Hibernate为我的模型管理的REST应用程序的项目。将JSON对象映射到Hibernate实体

我知道,Spring允许你从HTTP请求(与@Consumes(JSON)注释)获得的Java对象。如果这个Java对象也是一个Hibernate实体,是否有冲突?并且是嵌套对象工作(如@ManyToOne关系)?

回答

1

我们正在使用这种方法来简化设计并摆脱许多dtos(我们滥用它们太多)。基本上,它为我们工作。

然而,在我们的REST模型中,我们试图不公开其他公关的对象,你总是可以创建其他REST资源来访问它们。

因此,我们只是将@JsonIgnore注释映射到@OneToMany@ManyToOne这些关系映射中,使它们变成暂时的。

另一个问题,我看到的是,如果你仍然想退回这些关系,你将不得不使用Join.FETCH策略为他们或移动交易管理提出了更高,所以当一个响应序列化到JSON(打开会话视图模式),该交易仍然存在。 在我看来,这两种解决方案不太好。

+0

如果我添加使用'@ JsonIgnore'注释但仍然需要两个不同的对象,我可以从我的HTTP请求中获得两个参数吗? (就像这样一种方法:'public void createPeople(@RequestBody AnEntity entityOne,AnOtherEntity entityTwo){...}') – Fractaliste

+0

据我所知,您可以将请求主体仅映射到一个对象。所以我不太确定这是可能的。但是,请注意,如果需要,可以将@ JsonIgnore仅用于序列化/反序列化,方法是分别将它放在getter/setter上。此外,我仍然认为在一个请求中创建对象及其关系不是很RESTful。我会创建另一个REST端点来保存对象关系并使用它。 – oceansize

3

是的,这不会是一个问题,实际上是一个相当普遍的做法。

近年来我已经认识到,有时,然而,这是不是一个好主意基础上直接您的域名始终建立你的看法。你可以在这个帖子看看:

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注释,但是然后如果您在其他视图中需要该注释,则可以使用

一个非常简单的例子,但你应该得到,有时你的域模型不能立即使用返回给表示层,由于这两个代码的可维护性和性能问题的想法。

7

正如我在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; 

这就是它!

+0

而不是'Location',是否可以存储通用的JSON对象?例如'@Type(type =“jsonb”) @Column(columnDefinition =“json”) private JSONObject document;' – Sriram

+0

当然可以。这更容易。您可以改编此示例来代替存储/检索JsonNode。 –

+0

谢谢,它工作 – Sriram