将JSON对象映射到Hibernate实体

时间:2014-03-29 10:17:10

标签: java json spring hibernate rest

我将开始一个使用Spring管理的REST应用程序的项目,并为我的模型启用Hibernate。

我知道Spring允许您从HTTP请求中获取Java对象(带有@Consumes(JSON)注释)。如果这个Java对象也是一个Hibernate实体,有没有冲突?嵌套对象是否正常工作(如@ManyToOne关系)?

5 个答案:

答案 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。