Java 将jpa(Hibernate)中的本机(join)查询转换为json

Java 将jpa(Hibernate)中的本机(join)查询转换为json,java,json,hibernate,spring-mvc,jpa,Java,Json,Hibernate,Spring Mvc,Jpa,我正在从事一个SpringBoot项目,并使用jpa进行数据持久化。现在我有两个相互关联的表,用户和项目。一个用户可以拥有任意数量的项目,而一个项目只能由一个用户拥有。 这是我的POJO: 用户 @Entity @Table(name="users", uniqueConstraints = { @UniqueConstraint(columnNames = { "email" }) }) @EntityLi

我正在从事一个SpringBoot项目,并使用jpa进行数据持久化。现在我有两个相互关联的表,用户和项目。一个用户可以拥有任意数量的项目,而一个项目只能由一个用户拥有。
这是我的POJO:
用户

@Entity
@Table(name="users", uniqueConstraints = {
            @UniqueConstraint(columnNames = {
                "email"
            })
    })
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @NaturalId
    @Column(unique = true)
    private String email;

    @NotBlank
    @JsonIgnore
    private String password;

    @NotBlank
    private String first_name;

    @NotBlank
    private String last_name;

    @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
    @JsonIgnore
    private Set<Item> items;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();


    @Column(nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdAt;

    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date updatedAt;
@Entity
@Table(name="items")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = {"createdAt", "updatedAt"}, allowGetters = true)
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @NotNull
    private String name;

    private String description;

    @NotNull 
    private Date purchase_date;

    private double price;

    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JoinColumn(name = "owner", nullable = true)
    private User owner;

    @Column(nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreatedDate
    private Date createdAt;

    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @LastModifiedDate
    private Date updatedAt;
现在我想将所有项目都作为restfulljson获取。我需要使用项目加入用户,以便获得以下JSON:

{
    "item_id":1,
    "name":"item1",
    "price":120.00,

    etc .....

    "owner_id":1,
    "owner_name":"Jon"
    etc ....

}
因此,我使用的是一个自定义本机查询

SELECT i.id, i.name, i.description ...... u.id, u.name ..... from items i , users u where i.owner = u.id
然后我返回
query.getResultList()
,但是这会返回字符串的数组,而不是像这样的json

[

    [ 1 , "item1" , 120.00 , ..... , 1 , "Jon" , ....]
    [ 2 , "item2" , 420.00 ....   ]
etc...
]

如何将返回的对象直接转换为JSON,或转换为将列名映射到值的映射列表,然后将其转换为JSON

可以使用构造函数表达式创建包含所需数据的DTO(数据传输对象)

package dto;

public class ItemDTO {

   private final Long item_id;
   private final String name;
   private final Long owner_id;
   private final String owner_name;

   public ItemDTO(Long item_id, String name, Long owner_id, String owner_name) {
      // set the fields
   }

   // getters
}
然后在构造函数表达式查询中使用此DTO(重要说明:这仅适用于JPQL查询,而不是本机查询)

此DTO可用于序列化为JSON

请在此处阅读有关Constructor表达式的更多信息:

您可以使用此功能,无需在每次请求时都使用新的DTO

@SuppressWarnings("unchecked")
public static List<ObjectNode> getQueryResult(EntityManager entityManager, String nativeQuery, Object... parameters) {

    Query localQuery = entityManager.createNativeQuery(nativeQuery,Tuple.class);

    for(int i = 0; i < parameters.length; i++)
        localQuery.setParameter(i+1, parameters[i]);

    return toJson(localQuery.getResultList());
}

private static List<ObjectNode> toJson(List<Tuple> results) {

    List<ObjectNode> json = new ArrayList<>();

    ObjectMapper mapper = new ObjectMapper();

    for (Tuple tuple : results)
    {
        List<TupleElement<?>> cols = tuple.getElements();

        ObjectNode node = mapper.createObjectNode();

        for (TupleElement col : cols)
            node.put(col.getAlias(), tuple.get(col.getAlias()).toString());

        json.add(node);
    }
    return json;
}
@SuppressWarnings(“未选中”)
公共静态列表getQueryResult(EntityManager EntityManager、字符串nativeQuery、对象…参数){
Query localQuery=entityManager.createNativeQuery(nativeQuery,Tuple.class);
对于(int i=0;i列表他还必须删除“nativeQuery=true”因为您给出的示例是一个JPQL表达式。另外,我会检索整个对象,然后使用MapStruct…@dbl之类的映射器来构造DTO。从性能角度来看,构造函数表达式比读取实体和映射到DTO要好得多,因为它只会执行一条SQL语句,而您不会有n+1炸弹。这是真的,特别是对于最终甚至需要分页的大型列表…但是如果列表像几个简单对象一样计数,并且请求不太频繁,那么我看不到任何好处。但是这是否意味着我需要为我所做的每个查询编写一个新的DTO类?
@SuppressWarnings("unchecked")
public static List<ObjectNode> getQueryResult(EntityManager entityManager, String nativeQuery, Object... parameters) {

    Query localQuery = entityManager.createNativeQuery(nativeQuery,Tuple.class);

    for(int i = 0; i < parameters.length; i++)
        localQuery.setParameter(i+1, parameters[i]);

    return toJson(localQuery.getResultList());
}

private static List<ObjectNode> toJson(List<Tuple> results) {

    List<ObjectNode> json = new ArrayList<>();

    ObjectMapper mapper = new ObjectMapper();

    for (Tuple tuple : results)
    {
        List<TupleElement<?>> cols = tuple.getElements();

        ObjectNode node = mapper.createObjectNode();

        for (TupleElement col : cols)
            node.put(col.getAlias(), tuple.get(col.getAlias()).toString());

        json.add(node);
    }
    return json;
}