2014-03-29 245 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