#java #mysql #spring #spring-boot #hibernate
#java #mysql #spring #весенняя загрузка #спящий режим
Вопрос:
предположим, у меня есть база данных, подобная этой: (не фактическая БД, но достаточная для иллюстрации проблемы)
Поля 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, но это означает, что я теряю функции гибернации, такие как каскадирование и целостность данных.
какие у меня есть варианты здесь? Если есть ошибка в отношениях сущностей или дизайне базы данных, пожалуйста, просветите меня. Заранее спасибо 🙂
Редактировать: я не использую какой-либо пользовательский класс сериализатора, просто базовый jackson. Аннотирование свойств с помощью @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;
}
}
Комментарии:
1. Я думаю, вы хотите показать пользователю его задачи, верно? Так что не извлекайте
Project
s, а вместо этого просматривайтеTaskHandler
и все такое прочее.2. Что ж, не должно быть слишком сложно предоставить методы запросов / репозитория, чтобы просто извлекать данные, необходимые для ваших конечных точек. С какой проблемой вы столкнулись?
3. @М.Прохоров нет, я хочу показать пользователю его проекты. Затем, когда он нажимает на проект, чтобы показать список задач. При нажатии на список задач отображаются задачи. (я знаю, как обрабатывать нажатия и запросы)
4. @Movsac, так в чем же тогда конкретная проблема? Определите выборки как отложенные и убедитесь, что сериализатор не использует их. Было бы проще, если бы вы также показали пример сериализации кода.
5. Это может быть проблемой: вы напрямую предоставляете свою внутреннюю модель (сущности) внешнему миру (клиентам REST). Вместо этого вы можете захотеть создать другую модель, например, используя объекты передачи данных, которые просто представляют данные, необходимые для этой конечной точки (перемещать данные с помощью библиотеки сопоставления, такой как mapstruct) или путем предоставления пользовательской сериализации / десериализации (одним из способов сделать это с помощью Jackson может быть mixins — предоставлять mixins с разными аннотациями для каждой конечной точки).
Ответ №1:
По-видимому, я неправильно понял, как работает режим гибернации. Узнав больше об этом и поиграв с ним. Я полагаю, что наконец понял. Оказывается, даже если это связано, оно не извлекает данные, если их не запрашивать.
Один из вариантов — использовать jsonignore для скрытых свойств, чтобы предотвратить доступ Джексона к получателю (который, в свою очередь, не будет извлекать объект), но это означает, что я не смогу настроить его позже.
Лучшим подходом является использование DTO.
Спасибо @Thomas за ваши комментарии