我將開始使用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