Двунаправленная ассоциация 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.

Другие вопросы по тегам