Java JPA&x2B;GSON:只有在服务器重新启动后,查询才会导致堆栈溢出

Java JPA&x2B;GSON:只有在服务器重新启动后,查询才会导致堆栈溢出,java,jpa,gson,Java,Jpa,Gson,我们最近遇到了一个JPA问题,查询@ManyToOne/@OneToMany关系导致堆栈溢出。但是,只有在创建实体后重新启动应用程序服务器时才会发生这种情况 问题是: 当我通常在部署war文件的情况下启动应用程序服务器时,我可以用一些内容填充数据库并进行查询。 但是,当我在不清除数据库的情况下重新启动服务器并尝试执行相同的查询时,我会出现奇怪的行为: 我得到一个Stackoverflow异常: java.lang.StackOverflower错误 at java.lang.StringBuff

我们最近遇到了一个JPA问题,查询@ManyToOne/@OneToMany关系导致堆栈溢出。但是,只有在创建实体后重新启动应用程序服务器时才会发生这种情况

问题是: 当我通常在部署war文件的情况下启动应用程序服务器时,我可以用一些内容填充数据库并进行查询。 但是,当我在不清除数据库的情况下重新启动服务器并尝试执行相同的查询时,我会出现奇怪的行为:

我得到一个Stackoverflow异常:

java.lang.StackOverflower错误

at java.lang.StringBuffer.append(StringBuffer.java:224)

at java.io.StringWriter.write(StringWriter.java:84)

at java.io.StringWriter.append(StringWriter.java:126)

at java.io.StringWriter.append(StringWriter.java:24)

at com.google.gson.stream.JsonWriter.beforeValue(JsonWriter.java:610)

at com.google.gson.stream.JsonWriter.open(JsonWriter.java:317)

at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:300)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:190)

at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:879)

at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
[……]

完整性:我的实体具有以下关系:

特派团实体:

这意味着,父母了解他们的孩子,反之亦然,但至少应该通过使用保留政策避免典型的GSON堆栈溢出,因为父母被排除在外

我不知道整个问题是否与JPA或GSON有关,但真正让我疑惑的是,为什么只有在服务器重新启动后才会发生这种情况。它表明了某种会话问题,我无法理解,而且我还没有发现关于这个特定问题的任何其他线程或问题,所以我在这里提问

我知道有懒惰和急切的获取类型,但使用它们并不能解释为什么这个问题只在服务器重新启动时出现

非常感谢,
Kay

GSON不支持循环,所以这很可能是您的原因。我认为在GSON中避免循环的正常方法是使变量暂时化,我还没有听说@Retention(RetentionPolicy.RUNTIME)起作用,也许它真的起作用了

您可能没有维护双向关系(这是非常错误的),因此在清除共享缓存之前不要有循环

您还可以尝试禁用缓存,或禁用编织以缩小问题范围

您可能希望尝试其他JSON序列化程序,例如EclipseLink Moxy,它使用JAXB注释并支持循环


这里有一个例子,

好的,我终于找到了答案,找到答案很痛苦:

在特派团实体,我收集了儿童和父母。然而,GSON只对其中一个进行序列化,这个“父项”被排除在外(@GsonExclude=@Retention(RetentionPolicy.RUNTIME)和@Target(ElementType.FIELD),为了澄清,我刚才明确提到了它们)

以下代码是特派团实体的“错误”版本:

@OneToMany(mappedBy = "mission", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@XmlTransient 
private Collection<Mission2Mission> children;

@OneToMany(mappedBy = "parent", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@GsonExclude@XmlTransient
private Collection<Mission2Mission> parents;
@OneToMany(mappedBy=“mission”,fetch=FetchType.LAZY,cascade=CascadeType.ALL)@xmltransive
私人收藏儿童;
@OneToMany(mappedBy=“parent”,fetch=FetchType.LAZY,cascade=CascadeType.ALL)@GsonExclude@XmlTransient
私人收藏父母;
还有人看到问题吗?“mappedBy”映射了错误的引用。当然,当试图检索任务的子项时,我需要将其映射到“父项”字段,以便查找所有条目,其中给定任务是父项。这同样适用于“父母”。检索所有需要查找的父任务2任务实体的地图,这里是“任务”(子任务)

上面的代码反之亦然,这会导致可能的inf循环,因为Mission2Mission实体(见上文)的做法正好相反(它实际上排除了父对象而不是子对象)

交换地图区域(任务和家长)就像一个符咒。花了足够长的时间才弄明白


无论如何,谢谢你的意见。缓存提示在这里大大提高了调试速度:)

通常,“通过线路”发送JPA实体不是一个好主意,原因有很多(性能、安全性、上述循环引用问题等)。因此,最好使用DTO对象。关于这一点,已经有很多线程,例如这里:

为了安全起见,添加了fetchType.LAZY太多关系:不起作用:(禁用缓存会导致与重新启动相同的行为(哦,奇怪)-我真的很想知道,我们在哪里没有维护我们的关系,因为我们真的试图在每个地方都这样做。然而,这并不能解决问题,只能更早地揭示它。我想知道为什么GSON仍然会进入一个循环。我现在也将@XmlTransient添加到关系中,但这仍然没有帮助。
@ManyToOne  
@Retention(RetentionPolicy.RUNTIME)   
@Target(ElementType.FIELD)  
private Mission parent;  

@ManyToOne   
private Mission mission;
@OneToMany(mappedBy = "mission", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@XmlTransient 
private Collection<Mission2Mission> children;

@OneToMany(mappedBy = "parent", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@GsonExclude@XmlTransient
private Collection<Mission2Mission> parents;