【问题标题】:How to get just an entity id instead of the entire entity in JSON response如何在 JSON 响应中仅获取实体 ID 而不是整个实体
【发布时间】:2018-02-16 12:15:23
【问题描述】:

我看到了几个类似的问题,但没有一个对我有用的答案。 我正在将现有项目转换为使用 Hibernate。我正在使用 Mysql 作为数据库。我有这样的课:

@Entity
@Table(name = "user")
public class User {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "user_id")
   private Long userId;

   @ManyToOne
   @JoinColumn(name = "group_id_user")
   private Group group;

   @Column(name = "name")
   private String name;

   ...
   // getters and setters....   
}

还有这样的课程:

@Entity
@Table(name = "group")
public class Group {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "group_id")
   private Long groupId;

   @Column(name="group_name")
   private String groupName;

   ...
   // getters and setters....   
}

当我从数据库中获取 User 项目时,我将其转换为 JSON 并最终得到以下格式的结果:

{
    "user": {
        "userId": 1,
        "group": {
            "groupId": 3,
            "groupName": "Group Three",

        },
        "name": "John Doe",
     }
}

在我的例子中,Group 对象实际上比这个例子中的要大很多,并且还包含对另一个表的引用(即Group 有一个SomeOtherClass 对象)。我想最终得到如下所示的内容:

{
    "user": {
        "userId": 1,
        "groupId": 3,
        "name": "John Doe",
     }
}

有没有办法只在我的 JSON 响应中获取 groupId 而不是整个 Group 对象?

我尝试将 FetchType.Lazy 添加到我的 User 类中

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id_user")
private Group group;

但是当我尝试将 User 对象转换为 Json 时,我收到以下错误:

error: Errorjava.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?

我发现 this answer 消除了错误,但 JSON 对象与我添加 FetchType.LAZY 之前完全相同。看起来以某种方式转换为 JSON 添加了 FetchType.LAZY 忽略的值。

我正在使用以下方式转换为 JSON:

GsonBuilder b = new GsonBuilder();
Gson gson = b.create();
//b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY);  

com.google.gson.JsonArray jsonArray = gson.toJsonTree(out).getAsJsonArray();

【问题讨论】:

  • 答案与您提出的另一个相同:将您作为对 REST API 的响应发送的输出与实体分离,一切都会变得更简单。 IE。不要序列化您的实体。序列化表示您的 API 旨在作为响应发送的对象。
  • 打扰一下,你说的是 Http Response 吗?
  • @JBNizet 如果我理解正确,您建议不要使用 Gson 将实体转换为 Json,而应该只创建一个新的空 Json 对象并将实体中我需要的所有数据添加到Json 对象并省略我不需要的部分(在此示例中,省略 Group Object 并添加 userId、groupId 和 name)。这可能适用于获取单个小实体,但对于获取更复杂实体的列表,此过程可能会变得更加复杂。似乎应该有一种方法可以按原样转换实体,但用它们的 id 替换子实体。
  • 没有。 Gson 是一个对象映射器。创建代表 API 输出的类 (DTO),将实体转换为这些 DTO 类的实例,并使用 Gson 将 DTO 序列化为 JSON。好的 REST 框架会自动为您完成最后一部分。

标签: java json hibernate annotations


【解决方案1】:

如果我错了,请纠正我,因为我是休眠新手。根据我目前的经验,是的,可以只发送对象 ID 而不是发送整个对象详细信息作为响应。通过使用@JsonIdentityReference@JsonIdentityInfo 找到documentation here

下面是为我工作的示例代码:

团体课

@Entity
@Table(name = "group")
public class Group {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "group_id")
   private Long groupId;

   @Column(name="group_name")
   private String groupName;

   ...
   // getters and setters....   
}

用户类

@Entity
@Table(name = "user")
public class User {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "user_id")
   private Long userId;

   @ManyToOne
   @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "group_id")
   @JsonIdentityReference(alwaysAsId = true)
   @JoinColumn(name = "group_id_user")
   private Group group; 
   ...
   // getters and setters....   
}

结果是

{ “用户ID”:1, “组”:1, "name": "示例用户" }

我使用以下代码转换 JSON

ObjectMapper mapper = new ObjectMapper();
try {
      json = mapper.writeValue(user);
} catch (JsonProcessingException e) {
      e.printStackTrace();
}

我找到了解决问题的方法here

【讨论】:

    【解决方案2】:

    选项 1:(来自@JBNizet 的评论)

    创建代表 API 输出的类 (DTO),将实体转换为这些 DTO 类的实例,并使用 Gson 将 DTO 序列化为 JSON。好的 REST 框架会自动为您完成最后一部分。

    对于此示例,您可以创建一个如下所示的 UserItem 类:

    public class UserItem {
    
        private Long userId;
        private Long groupId;
        private String name;
       ...
       // getters and setters....   
    }
    

    那么你需要一个将 User 实体转换为 UserItem 的方法:

    public UserItem UserToUserItem(User user) {
        UserItem userItem = new UserItem();
        userItem.setUserId(user.getUserId());
        userItem.setGroupId(user.getGroup().getGroupId());
        userItem.setName(user.getName());
    
        return userItem;
    }
    

    在您的响应中使用 userItem。


    选项 2:

    更新:将选项 2 更新为可行的方法

    如果您不想为每个实体类编写 DTO,因为(如此示例)唯一需要更改的字段是 groupId 的 Group 对象,您可以创建一个 ExclusionStrategy 并使用 @Expose 注释作为如下:

    首先创建一个实现ExclusionStrategy的类

    public class SerializeExclusionStrat implements ExclusionStrategy {
    
        @Override
        public boolean shouldSkipField(FieldAttributes fieldAttributes) {
            final Expose expose = fieldAttributes.getAnnotation(Expose.class);
            return expose != null && !expose.serialize();
        }
    
        @Override
        public boolean shouldSkipClass(Class<?> aClass) {
            return false;
        }
    }
    
    
    @Entity
    @Table(name = "user")
    public class User {
    

    然后将@Expose注解添加到User类。添加附加字段 groupId 将允许在您的查询和 json 响应中返回 groupId。只要确保用updatable=falseinsertable=false 标记groupId。

    @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       @Column(name = "user_id")
       private Long userId;
    
       @ManyToOne
       @JoinColumn(name = "group_id_user")
       @Expose(serialize = false)
       private Group group;
    
       @Column(name = "group_id_user", updatable = false, insertable = false)
       private Long groupId;     
    
       @Column(name = "name")
       private String name;
    
       ...
       // getters and setters....   
    }
    

    然后在创建Gson的时候设置Exclusion策略:

    GsonBuilder b = new GsonBuilder();
    b.serializeNulls();
    b.setExclusionStrategies(new SerializeExclusionStrat());
    Gson gson = b.create();
    

    【讨论】:

    • JPA 规范说:所有未使用 Transient 注解的非瞬态实例变量都是持久的。。因此,将字段标记为瞬态也会阻止其保存在数据库中。
    • @JBNizet 嗯...看起来你是对的。我能够正确地从数据库中获取数据,但现在我检查添加它看起来像“group_id_user”值设置为空。必须有一种方法可以做到这一点,而不必为每个具有外键的实体创建 DTO...
    • 我已经编辑了答案,以展示一种不同的方式,该方式适用于添加到数据库和从数据库中提取。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2021-07-27
    • 2016-07-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多