2014-03-29 16 views
5

Ich starte ein Projekt einer REST-Anwendung, die mit Spring und mit Hibernate für mein Modell verwaltet wird.JSON-Objekt zu Hibernate-Entity zuordnen

Ich weiß, dass Spring ermöglicht Ihnen, Java-Objekt von der HTTP-Anforderung (mit @Consumes(JSON) Annotation) zu bekommen. Gibt es einen Konflikt, wenn dieses Java-Objekt auch Hibernate-Entitäten ist? Und funktioniert geschachteltes Objekt (wie @ManyToOne Relation)?

Antwort

1

Wir verwendeten einen solchen Ansatz, um das Design zu vereinfachen und viele dtos loszuwerden (wir haben sie zu sehr missbraucht). Im Grunde hat es für uns funktioniert.

In unserem REST-Modell haben wir jedoch versucht, keine anderen Beziehungen für ein Objekt verfügbar zu machen, da Sie jederzeit weitere REST-Ressourcen für den Zugriff darauf erstellen können.

Also setzen wir einfach @JsonIgnore Annotationen in Beziehungszuordnungen wie @OneToMany oder @ManyToOne, die sie vorübergehend machen.

Ein weiteres Problem, das ich sehe, dass, wenn Sie immer noch diese Beziehungen zurückkehren möchten Sie Join.FETCH Strategie für sie verwenden oder das Transaktionsmanagement bewegen höher, so dass Transaktion noch existiert, wenn eine Antwort auf JSON (Open Session in View Pattern) serialisiert . Meiner Meinung nach sind diese beiden Lösungen nicht so gut.

+0

Wenn ich hinzufügen '@ JsonIgnore' Annotation, aber ich brauche immer noch von beiden differents Objekte, kann ich zwei Parameter von meiner HTTP-Anfrage bekommen? (wie eine Methode dieser Art: 'public void createPeople (@RequestBody AnEntity entityOne, AnOtherEntity entityTwo) {...}') – Fractaliste

+0

Soweit ich weiß, können Sie den Anfragetext nur einem Objekt zuordnen. Ich bin mir also nicht sicher, ob das möglich ist. Beachten Sie jedoch, dass Sie '@ JsonIgnore' bei Bedarf entweder für die Serialisierung/Deserialisierung verwenden können, indem Sie sie auf Getter/Setter setzen. Ich glaube auch immer noch, dass das Erstellen eines Objekts und seiner Beziehung in einer Anfrage nicht so RESTful ist. Ich würde einen anderen REST-Endpunkt zum Speichern der Objektbeziehung erstellen und verwenden. – oceansize

3

Ja, das wäre kein Problem und ist eigentlich eine ziemlich übliche Praxis.

In den letzten Jahren habe ich festgestellt, dass es manchmal keine gute Idee ist, Ihre Ansichten basierend auf Ihrer Domain direkt zu erstellen. Sie können zu diesem Beitrag einen Blick:

http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/

Es wird auch als "Presentation Model" bekannt ist:

http://martinfowler.com/eaaDev/PresentationModel.html

Die Idee dahinter ist im Grunde die folgenden:

Stellen Sie sich vor, Sie haben den Domäneneintrag Benutzer, der so aussieht:

@Entity 
@Data 
public class User { 
    @Id private UUID userId; 
    private String username; 
    @OneToMany private List<Permission> permissions; 
} 

Lassen Sie uns jetzt vorstellen, Sie haben eine Ansicht, wo Sie den Namen dieses Benutzers anzeigen möchten, und Sie völlig kümmern sich nicht um die Berechtigungen. Wenn Sie Ihren Ansatz verwenden, den Benutzer sofort zur Ansicht zurückzubringen, führt Hibernate einen zusätzlichen Join aus der Permissions-Tabelle aus, da das Ereignis, obwohl die Berechtigungen standardmäßig geladen sind, keine einfache Möglichkeit gibt, dem Jacksons Serializer oder was auch immer Sie sind, zu signalisieren mit, dass du dich in diesem speziellen Fall nicht um sie kümmerst, also wird Jackson versuchen, sie zu entschärfen (wenn deine Transaktion noch am Leben ist, wenn dein Objekt für die JS Serialisierung verwendet wird, sonst bekommst du eine unangenehme Ausnahme). Ja, Sie können eine @JsonIgnore Annotation im Feld Berechtigungen hinzufügen, aber dann, wenn Sie es in einer anderen Ansicht benötigen, sind Sie geschraubt.

Das ist ein sehr einfaches Beispiel, aber Sie sollten die Idee haben, dass Ihr Domänenmodell nicht sofort für die Darstellungsschicht verwendet werden kann, aufgrund von Wartbarkeit und Leistungsprobleme.

7

Wie ich in this article erklärt habe, ist es sehr einfach, JSON-Objekt mit Hibernate persistent zu halten.

Sie müssen all diese Typen nicht manuell erstellen, geben Sie einfach sie über Maven Zentrale mit der folgenden Abhängigkeit erhalten können:

<dependency> 
    <groupId>com.vladmihalcea</groupId> 
    <artifactId>hibernate-types-52</artifactId> 
    <version>${hibernate-types.version}</version> 
</dependency> 

Für weitere Informationen, besuchen Sie die hibernate-types open-source project.

Ich schrieb an article darüber, wie Sie JSON-Objekte auf PostgreSQL und MySQL zuordnen können.

Für PostgreSQL, müssen Sie die JSON-Objekt in eine binäre Form senden:

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); 
    } 

} 

Die JsonBinarySqlTypeDescriptor sieht wie folgt aus:

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() 
       ); 
      } 
     }; 
    } 
} 

und die JsonTypeDescriptor wie folgt aus:

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()); 
    } 

} 

Jetzt müssen Sie den neuen Typ für jede Klasse le deklarieren vel oder in einer package-info.java Paketebene descriptior:

@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) 

Und die Entitätszuordnung wird wie folgt aussehen:

@Type(type = "jsonb") 
@Column(columnDefinition = "json") 
private Location location; 

Das ist es!

+0

Kann anstelle von 'Location' ein generisches JSON-Objekt gespeichert werden? Zum Beispiel "@Type (type =" jsonb ") @Column (columnDefinition =" json ") privates JSONObject-Dokument;' – Sriram

+0

Sicher. Das ist noch einfacher. Sie können dieses Beispiel anpassen, um stattdessen JsonNode zu speichern/abrufen. –

+0

Danke, es hat funktioniert – Sriram

Verwandte Themen