使用 eclipselink 的 JPA Stackoverflowexception

JPA Stackoverflowexception using eclipselink

当我试图从我的应用程序中获取一个项目或项目列表时,我得到一个 java.lang.WhosebugError 异常。

我正在将 JPA 与 eclipselink 结合使用。

我得到以下堆栈跟踪:

java.lang.WhosebugError
java.lang.WhosebugError
    at java.util.concurrent.ConcurrentHashMap$BaseIterator.<init>(ConcurrentHashMap.java:3391)
    at java.util.concurrent.ConcurrentHashMap$ValueIterator.<init>(ConcurrentHashMap.java:3430)
    at java.util.concurrent.ConcurrentHashMap$ValuesView.iterator(ConcurrentHashMap.java:4683)
    at org.eclipse.yasson.internal.ComponentMatcher.searchComponentBinding(ComponentMatcher.java:187)
    at org.eclipse.yasson.internal.ComponentMatcher.getSerializerBinding(ComponentMatcher.java:143)
    at org.eclipse.yasson.internal.serializer.SerializerBuilder.build(SerializerBuilder.java:73)
    at org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:102)
    at org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:61)
    at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:63)
    at org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:95)

我尝试了在多个网站上找到的多种解决方案:

即使上面列出的所有内容都应该有效,none 其中似乎也有效。到目前为止,我发现的唯一 "solution" 是删除吸气剂,但这消除了建立双向关系的目的,因为您无法访问它们。

有人知道我做错了什么吗,或者有人有其他可行的解决方案吗?

下面是一些代码片段:

用户Class:

@Entity
    @Table(name = "users")
    @NamedEntityGraph(
            name = "user-entity-graph",
            attributeNodes = {
                    @NamedAttributeNode("id"),
                    @NamedAttributeNode("username"),
                    @NamedAttributeNode("email"),
                    @NamedAttributeNode("biography"),
                    @NamedAttributeNode("website"),
                    @NamedAttributeNode("longitude"),
                    @NamedAttributeNode("latitude"),
                    @NamedAttributeNode(value = "role", subgraph = "role-subgraph"),
                    @NamedAttributeNode(value = "followers", subgraph = "followers-subgraph"),
                    @NamedAttributeNode(value = "following", subgraph = "followers-subgraph"),
            },
            subgraphs = {
                    @NamedSubgraph(
                            name = "role-subgraph",
                            attributeNodes = {
                                    @NamedAttributeNode("id"),
                                    @NamedAttributeNode("name")
                            }
                    ),
                    @NamedSubgraph(
                            name = "followers-subgraph",
                            attributeNodes = {
                                    @NamedAttributeNode("id"),
                                    @NamedAttributeNode("username"),
                                    @NamedAttributeNode("email"),
                                    @NamedAttributeNode("biography"),
                                    @NamedAttributeNode("website"),
                                    @NamedAttributeNode("longitude"),
                                    @NamedAttributeNode("latitude"),
                            }
                    ),
            }
    )
    @NamedQueries({
            @NamedQuery(name = "user.getAllUsers", query = "SELECT u FROM User u"),
            @NamedQuery(name = "user.getUserById", query = "SELECT u FROM User u WHERE u.id = :id"),
            @NamedQuery(name = "user.getUserByUsername", query = "SELECT u FROM User u WHERE u.username = :username"),
            @NamedQuery(name = "user.getUserByEmail", query = "SELECT u FROM User u WHERE u.email = :email")
    })
    public class User implements Serializable {

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;

        @Column(unique = true, nullable = false)
        private String username;

        @Column(unique = true, nullable = false)
        private String email;

        @Column(nullable = false)
        @XmlTransient
        private String password;

        @Column(length = 160)
        private String biography;

        @Column
        private String website;
        private double longitude;
        private double latitude;

        @ManyToOne(fetch = FetchType.LAZY)
        private Role role;

        @ManyToMany(
                cascade = {
                        CascadeType.PERSIST,
                        CascadeType.MERGE
                }, fetch = FetchType.LAZY
        )
        @JoinTable(
                name = "followers",
                joinColumns = @JoinColumn(name = "follower"),
                inverseJoinColumns = @JoinColumn(name = "following")
        )
        private Set<User> followers;

        @ManyToMany(
                mappedBy = "followers",
                fetch = FetchType.LAZY
        )
        private Set<User> following;

        @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
        private Set<Tweet> tweets;

角色Class:

@Entity
@Table(name = "roles")
@NamedEntityGraph(
        name = "role-entity-graph",
        attributeNodes = {
                @NamedAttributeNode("id"),
                @NamedAttributeNode("name"),
                @NamedAttributeNode(value = "permissions", subgraph = "permission-subgraph"),
                @NamedAttributeNode(value = "users", subgraph = "user-subgraph"),
        },
        subgraphs = {
                @NamedSubgraph(
                        name = "permission-subgraph",
                        attributeNodes = {
                                @NamedAttributeNode("name"),
                        }
                ),
                @NamedSubgraph(
                        name = "roles-subgraph",
                        attributeNodes = {
                                @NamedAttributeNode("id"),
                                @NamedAttributeNode("name")
                        }
                ),
                @NamedSubgraph(
                        name = "user-subgraph",
                        attributeNodes = {
                                @NamedAttributeNode("id"),
                                @NamedAttributeNode("username"),
                                @NamedAttributeNode("email")
                        }
                ),
        }
)
@NamedQueries({
        @NamedQuery(name = "role.getAllRoles", query = "SELECT r FROM Role r"),
        @NamedQuery(name = "role.getRoleById", query = "SELECT r FROM Role r WHERE r.id = :id"),
        @NamedQuery(name = "role.getRoleByName", query = "SELECT r FROM Role r WHERE r.name = :name")
})
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(unique = true, nullable = false)
    private String name;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH}, fetch = FetchType.LAZY)
    @JoinTable(
            name = "role_permission",
            joinColumns = @JoinColumn(name = "role_id"),
            inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
    private Set<Permission> permissions;

    @OneToMany(mappedBy = "role", fetch = FetchType.LAZY)
    private Set<User> users;

实施片段:

@PersistenceContext(unitName = "kwetterDB")
    private EntityManager em;

    @Override
    public List<Role> all() {
        EntityGraph eg = em.getEntityGraph("role-entity-graph");
        return em.createNamedQuery("role.getAllRoles", Role.class).setHint("javax.persistence.fetchgraph", eg).getResultList();
    }

我的 RoleController 的片段:

@Stateless
@Path("/roles")
public class RoleController {
    @Inject
    private RoleService roleService;

    @Inject
    private PermissionService permissionService;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response all() {
        return Response.status(Response.Status.OK).entity( roleService.all()).build();
    }

pom.xml:

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>8.0.1-b5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>eclipselink</artifactId>
      <version>2.7.4</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>27.0.1-jre</version>
    </dependency>
      <dependency>
          <groupId>org.mockito</groupId>
          <artifactId>mockito-core</artifactId>
          <version>2.24.0</version>
          <scope>test</scope>
      </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.15</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.8</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.8</version>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <version>3.2.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-java</artifactId>
      <version>2.3.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.cucumber</groupId>
      <artifactId>cucumber-junit</artifactId>
      <version>2.3.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="kwetterDB" transaction-type="JTA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jta-data-source>jdbc/myapp</jta-data-source>
        <properties>
            <!--<property name="javax.persistence.schema-generation.database.action" value="create"/>-->
            <property name="eclipselink.logging.logger" value="DefaultLogger"/>
            <property name="eclipselink.logging.level" value="FINE"/>
        </properties>
    </persistence-unit>
</persistence>

搜索了一段时间后,我发现我必须使用 ObjectMapper 来触发 Json 注释。 资料来源:

适用于 @JsonIgnore@JsonBackReference@JsonManagedReference@JsonIgnoreProperties@JsonIdentityInfo

在我的控制器中更改以下行:

return Response.status(Response.Status.OK).entity( roleService.all()).build();

致:

return Response.status(Response.Status.OK).entity(new ObjectMapper().writeValueAsString(roleService.all())).build();