【问题标题】:Reduce amount of data in hibernate while still making use of hibernate features减少休眠中的数据量,同时仍然使用休眠功能
【发布时间】:2020-12-16 10:31:23
【问题描述】:

假设我有一个这样的数据库:(不是实际的数据库,但足以说明问题)

Complete 和 total 字段只是 isComplete 和每个条件(每个任务列表或每个项目)的总任务记录的计数器。它们被重复(数据库反规范化?)以便于获取,因为在每个阶段我都想包含一个进度计数器。

我有这样的实体:

// other fields, getter and setters omitted for brevity

@Entity
@Table(name = "user_info")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "username")
    private String userName;
    
    @Column(name = "image")
    private String imageUrl;
    
    @ManyToMany
    @JoinTable(name = "project_user",
    joinColumns = @JoinColumn(name="user_id"),
    inverseJoinColumns = @JoinColumn(name="project_id"))
    private Set<Project> projects;
}

@Entity
@Table(name = "project")
public class Project {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "name")
    private String name;
    
    @OneToOne
    @JoinColumn(name = "creator_id")
    private User creator;
    
    @Column(name = "image")
    private String imageUrl;
    
    @Column(name = "creation_date")
    private Date createdAt;
    
    @Column(name = "total")
    private int total;
    
    @Column(name = "complete")
    private int complete;
    
    @ManyToMany
    @JoinTable(name = "project_user",
    joinColumns = @JoinColumn(name="project_id"),
    inverseJoinColumns = @JoinColumn(name="user_id"))
    @JsonIgnoreProperties(value = {"projects", "connected", "notifications"})
    private Set<User> members;
    
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "project_id")
    private Set<TaskList> taskLists;
}


@Entity
@Table(name = "task_list")
public class TaskList {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "name")
    private String name;
    
    @Column(name = "image")
    private String imageUrl;
    
    @Column(name = "creation_date")
    private Date createdAt;
    
    @Column(name = "complete")
    private int complete;
    
    @Column(name = "total")
    private int total;
    
    @OneToMany
    @JoinColumn(name = "task_list_id")
    private Set<Task> tasks;
}

@Entity
@Table(name = "task")
public class Task {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @Column(name = "description")
    private String description;
    
    @Column(name = "title")
    private String title;
    
    @ElementCollection
    @CollectionTable(name = "task_images", joinColumns = @JoinColumn(name="task_id"))
    @Column(name = "image")
    private Set<String> imageUrls;
    
    @OneToMany
    @JoinColumn(name = "task_id")
    private Set<TaskMember> handledBy;
    
    @Transient
    private Set<Note> notes;
    
    @Column(name = "complete")
    private boolean complete;
    
    @Column(name = "creation_date")
    private Date createdAt;
    
    @Column(name = "completion_date")
    private Date completeDate;
    
}


@Entity
@Table(name = "task_handler")
public class TaskMember {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    
    @OneToOne
    @JoinColumn(name = "id", referencedColumnName = "user_id")
    private User user;
    
    @Column(name = "assignmentType")
    private String assignmentType; //maybe change to enum?
}

问题是在添加了一些 json 忽略属性(以解决循环序列化问题)后,我得到一个这样的 json:

user: {
//fields
projects: [{
             //fields
             tasklist: [{
                         //fields
                         tasks: [{
                                 //fields
                                }]
                       }]
          }]
}

这不仅是一个巨大的响应(包含几乎所有的数据),而且还占用了服务器上的大量空间。 我想要的是这样的:

request to sign in:

user: {
//fields
}

request to endpoint1 with parameter user id

projects: [{
// fields
}]

request to endpoint2 with parameter project id
tasklist: [{
// fields
}]

request to endpoint3 with parameter tasklist id
tasks: [{
// fields
}]

这将使更新客户端的任何更改变得更加容易,同时使 api 尽可能无状态(我不希望服务器内存中的任何数据除了会话和安全性内容)。

我知道一种方法是删除实体并执行纯 sql 样式,但这意味着我失去了休眠功能,例如级联和数据完整性。

我在这里有什么选择?如果实体关系或数据库设计有错误请赐教。 在此先感谢:)

编辑:我没有使用任何自定义序列化程序类,只是基杰克逊。使用 @jsonIgnoreProperty 注释属性可以防止序列化不需要的字段,但我认为它不会首先阻止获取它们(这里可能是错误的)。

@RestController
public class HelloController {
    @Autowired
    private UserService userService;

    @GetMapping("/")
    public List<User> getAllData() {
        // just runs a basic "from User" query
        List<User> users = userService.findAll();
        return users;
    }
}

【问题讨论】:

  • 我想你想向用户展示他的任务,对吧?所以不要获取Projects,而是通过TaskHandler 和所有这些东西。
  • 好吧,提供查询/存储库方法来获取端点所需的数据应该不会太难。您面临什么问题?
  • @M.Prokhorov 不,我想要向用户展示他的项目。然后当他点击一个项目时,显示任务列表。单击任务列表会显示任务。 (我知道如何处理点击和请求)
  • @Movsac,那么具体的问题是什么?将 fetches 定义为惰性,并确保序列化程序不会拉取它们。如果你也展示一个序列化代码的例子会更简单。
  • 这可能是问题所在:您将内部模型(实体)直接暴露给外部世界(REST 客户端)。您可能想要做的是构建另一个模型,例如使用仅表示该端点所需数据的数据传输对象(使用映射库如 mapstruct 移动数据)或通过提供自定义序列化/反序列化(使用 Jackson 执行此操作的一种方法可能是混合 - 为每个混合提供具有不同注释的混合端点)。

标签: java mysql spring spring-boot hibernate


【解决方案1】:

显然,我误解了休眠的工作原理。在更多地了解它并玩弄它之后。我相信我终于明白了。事实证明,即使它是相关的,它也不会获取数据,除非它被要求。

一种选择是在隐藏属性上使用 jsonignore 来防止杰克逊访问 getter(这反过来又不会获取对象),但这意味着我以后不能自定义它。

更好的方法是使用 DTO。

感谢@Thomas 的 cmets

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 2013-11-17
    • 2012-04-18
    • 2012-08-15
    相关资源
    最近更新 更多