【发布时间】: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