Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

Hibernate many to many бесконечно вложенный json

#java #hibernate #spring_mvc


Есть три таблицы user, group, user2group. Связь много ко многим.

Если прописать мапинг только в классе User, то контроллер выдает json который и нужен.

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user2group", 
    joinColumns = { @JoinColumn(name = "user_id") }, 
    inverseJoinColumns = { @JoinColumn(name = "group_id") })
private Set groups = new HashSet();


Но если мапим связанные данные в классе Group, то при попытке получить список групп
с привязанными к ним клиентами, контроллер начинает выдавать json, но при этом бесконечно
проваливается в связанные сущности.

Мапинг для класса Group сейчас у меня следующий:

@ManyToMany(mappedBy = "groups")
private Set users = new HashSet();


Записи я получаю следующим образом 

Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM User");
List users = query.list();


Также, на случай если это важно. По началу при обращении к соответствующему урлу,
вылетало исключение, которое починилось установкой следующего нагугленного свойства
при инициализации бина LocalSessionFactory:

properties.put("hibernate.enable_lazy_load_no_trans",  true);


Вопрос. Чтобы получать json единой вложенности, нужны какие-то особенные настройки
хибернейта? Или прописывать особый маппинг?

В ходе гугления вопроса мне начало казаться, что особо ни у кого не возникает подобной
сложности, может тут подход нужен иной?

Задача
Возможность получить всех юзеров, где к каждому юзеру будет прикреплен список групп,
в которых он числится.
Пример: 

[
  {id:1, name:"user1", groups:[{id:1, title:"group1"}, {id:5, title:"group5"}]},
  {id:2, name:"user2", groups:[]},
  {id:3, name:"user3", groups:[{id:3, title:"group3"}]},
]


Возможность получить все группы, где к каждой группе будет прикреплен список юзеров,
которые числятся в этой группе
Пример: 

[
  {id:1, title:"group1", users:[{id:1, name:"user1"}, {id:5, name:"user5"}]},
  {id:2, title:"group2", users:[]},
  {id:3, title:"group3", users:[{id:3, name:"user3"}]},
]

    


Ответы

Ответ 1



Не очень хорошая практика - отдавать ваши ORM-сущности непосредственно наружу. Во-первых, вы смешиваете разные задачи в одном классе. Во-вторых, вы отдаете наружу все его состояние, которое, возможно, не стоит отдавать. В третьих, когда-нибудь вам потребуется отдавать для разных задач разный набор полей. Обычно, эта проблема решается паттерном DTO (Data Transfer Object). Вы просто создаете классы UserDto и GroupDto, которые содержат только необходимые поля, геттеры-сеттеры, конструктор по-умолчанию и JSON-аннотации. При отдаче наружу в слое представления (контроллере) перекладываете поля из ORM-объектов в DTO-объекты. Можно это делать как вручную (завести static-метод в DTO), так и использовать какой-нибудь маппер вроде Dozer. Для получения групп с вложенными в нее пользователями, нужно завести отдельную DTO, например, как GroupWithUsersDto в примере ниже. public class GroupDto { @JsonProperty("id") private Long id; @JsonProperty("title") private String title; // сеттеры-геттеры public static GroupDto fromModel(Group group) { GroupDto dto = new GroupDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); return dto; } public class UserDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); return dto; } } Для выдачи пользователей с их группами: public class UserWithGroupsDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; @JsonProperty("groups") private List groups; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); List groupDtos = new ArrayList<>(); for (Group group : user.getGroups()) { groupDtos.add(GroupDto.fromModel(group)); } dto.setGroups(groupDtos); return dto; } } Для выдачи групп с пользователями: public class GroupWithUsersDto{ @JsonProperty("id") private Long id; @JsonProperty("title") private String title; @JsonProperty("users") private List // сеттеры-геттеры public static GroupWithUsersDto fromModel(Group group) { GroupWithUsersDto dto = new GroupWithUsersDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); List usersDtos = new ArrayList<>(); for (User user : group.getUsers()) { usersDtos.add(UserDto.fromModel(user)); } dto.setUsers(usersDtos); return dto; }

Ответ 2



Можно повесить в классе Group на поле @ManyToMany(mappedBy = "groups") private Set users = new HashSet(); аннотацию @JsonIgnore Или использовать слой DTO package my.company.model; import javax.persistence.*; import java.util.Date; import java.util.List; import java.util.Set; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private String surname; private String password; private String email; @Column(name="time_registration") private Date timeRegistration; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_roles", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "role_id")) private Set roles; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List albums; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_dialogs", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "dialog_id")) private List dialogs; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List messages; @OneToOne private Avatar avatar; public User() { } public User(String name, String surname, String password, String email, Date timeRegistration, Set roles) { this.name = name; this.surname = surname; this.password = password; this.email = email; this.timeRegistration = timeRegistration; this.roles = roles; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Set getRoles() { return roles; } public void setRoles(Set roles) { this.roles = roles; } public List getAlbums() { return albums; } public void setAlbums(List albums) { this.albums = albums; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public List getDialogs() { return dialogs; } public void setDialogs(List dialogs) { this.dialogs = dialogs; } public List getMessages() { return messages; } public void setMessages(List messages) { this.messages = messages; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Avatar getAvatar() { return avatar; } public void setAvatar(Avatar avatar) { this.avatar = avatar; } @Override public String toString() { return id + " " + name + " " + email; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return id == user.id; } @Override public int hashCode() { return (int) (id ^ (id >>> 32)); } } package my.company.DTO; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; import java.util.List; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY) @JsonInclude(JsonInclude.Include.NON_NULL) public class UserDTO { private Long id; private String name; private String surname; private String email; @JsonProperty("albums") private List albumDTOs; private Date timeRegistration; private Long avatarId; public UserDTO(Long id, String name, String surname, String email, Date timeRegistration, Long avatarId) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.timeRegistration = timeRegistration; this.avatarId = avatarId; } public UserDTO(Long id, String name, String surname, String email, List albumDTOs, Date timeRegistration) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.albumDTOs = albumDTOs; this.timeRegistration = timeRegistration; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List getAlbumDTOs() { return albumDTOs; } public void setAlbumDTOs(List albumDTOs) { this.albumDTOs = albumDTOs; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Long getAvatarId() { return avatarId; } public void setAvatarId(Long avatarId) { this.avatarId = avatarId; } }

Комментариев нет:

Отправить комментарий