Двунаправленная ассоциация Hibernate и TomEE+ вызывает рекурсивность JSON
Я использую TomEE 9.0.12 с Hibernate 5.3.7 и в настоящее время получаю исключение stackru из-за рекурсивности json. Обратите внимание, что я не использую Джексона, и в идеале не хочу, но стандарт, который поставляется с TomEE+: org.apache.johnzon.
Исключение:
Caused by: java.lang.StackruError
at org.apache.johnzon.mapper.Mappings.findOrCreateClassMapping(Mappings.java:340)
at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObjectBody(MappingGeneratorImpl.java:240)
Вот мой класс User.java:
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
/**
* A user
*/
@Entity
@Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = -9012412584251217L;
// TODO: 16/12/2018 they keep nesting each other: { user { posts { user { posts } } } }
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
@Column(name = "salt")
private String salt;
@OneToMany(mappedBy = "user")
private Set<Post> posts = new HashSet<>();
/**
* Constructs an empty user (for persistence purposes)
*/
public User() {}
// Setters and getters removed for convenience
}
А вот и мой Post.java
import javax.persistence.*;
import java.io.Serializable;
/**
* Project created by ExpDev
*/
@Entity
@Table(name = "posts")
public class Post implements Serializable {
private static final long serialVersionUID = -9887234238952234L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Column(name = "data")
private String data;
/**
* Constructs an empty post (for persistence purposes)
*/
public Post() {}
// Setters and getters removed for convenience
}
Проблема
Проблема в том, что они будут продолжать ссылаться друг на друга. Пользователь будет ссылаться на сообщения, которые будут ссылаться на пользователя, который снова будет ссылаться на сообщения - и поэтому злой круг продолжается.
Я знаю, что могу использовать аннотацию org.apache.johnzon @JsonbTransient для "user" в User.java, чтобы не сериализовать ее, но чтобы не показывать (что я хочу, но условно). Почему я хочу это так?
Поскольку, скажем, к GET /users/1 обращаются, 1 является идентификатором пользователя, я хочу показать все сообщения пользователя (или, по крайней мере, их идентификатор), но не хочу, чтобы он ссылался на пользователя (вложенный пользователь внутри поста внутри пользователя).
Однако, если GET /api/posts/3, 3 является идентификатором, я хочу, чтобы он показывал пользователя, который сделал сообщение, но не сообщения снова внутри пользователя.
Таким образом, аннотации должны быть условными и конкретными. В настоящее время именно так я получаю и показываю пользователя (фреймворк автоматически преобразует возвращаемый объект в JSON при обращении к методу).
@GET
@Path("/{id}")
public User get(@PathParam("id") Long id) {
// Find the user and return them
return HiberUtil.getSession().get(User.class, id);
}
Заранее спасибо! Я искал 5 часов, но никакие решения или материалы для чтения не решили мою проблему.
1 ответ
Я исправил это, просто переместившись из TomEE+ в Spring Framework (со встроенным сервером Tomcat), который использует Джексона по умолчанию вместо "org.apache.johnzon. Благодаря этому я смог использовать полезные аннотации:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
@JsonIdentityReference(alwaysAsId=true)
private List<Post> posts;
Это сделало так, чтобы JSON выглядел так:
{
"username":"test",
"posts": [2, 5, 6, 8, 2]
}
То же самое относится и к Пользователю внутри Post, если он также помечен этими аннотациями. Это именно то, что я хотел, и то, как я использую интерфейсную веб-среду, EmberJS и многие другие, ожидают, что данные будут возвращены. Так что это идеально подходит для меня. Кроме того, после небольшой игры с Spring на встроенном сервере Tomcat он мне тоже понравился (я тоже прошел через Glassfish, но он мне не понравился).
Важно отметить, что исправление было не из-за того, что я переехал в Spring, а из-за того, что вместо этого я использовал Джексона. Если вы все еще хотите использовать TomEE+, вы можете просто посмотреть /questions/16367322/kak-ispolzovat-dzheksona-v-kachestve-json-provajdera-dlya-jax-rs-client-vmesto-johnzon-v-tomee-7/16367340#16367340 чтобы узнать, как использовать Джексона вместо johnzon с TomEE.
Для справки: johnzon поддерживает обработку ссылок с помощью опции deduplicateObjects в mapper (том 7) и свойства johnzon.deduplicateObjects (tomee 8, meecrowave и т. Д.). Это решает эту проблему, заменяя уже просмотренные ссылки на jsonpointer.