我遇到了许多双向JPA映射问题,导致堆栈溢出错误 虽然我的应用程序中总共有5个实体,但我认为该问题仅与其中的2个实体有关,为简单起见,这些实体将在下面进行描述。
实体:
申请
用户
应用程序可以有很多开发人员
用户可以开发许多应用程序
以下是它们的映射方式:
//应用程序实体:
@ManyToMany
private List<Users> users
//用户实体
@ManyToMany(mappedBy = "users", fetch = FetchType.LAZY)
private List <Applications> applications;
这导致在数据库中创建了3个表:
申请&lt; ID APPNAME
APPLICATION_USERS&lt;连接表包含USERS_ID APPLICATIONS_ID
USERS&lt; ID USER NAME
这是一个循环引用,一直运行直到堆栈溢出。
首次部署空表时,应用程序正常工作。 用户注册应用程序,这会在APPLICATIONS表中创建一行(如果应用程序不存在) 还会在USERS表中创建一行(如果用户不存在)和连接表APPLICATION_USERS 使用名为APPLICATIONS_ID和的APPLICATION表中的ID填充 来自USERS表的ID称为USERS_ID。
您可以根据需要添加任意数量的应用程序或用户,并且应用程序可以完美运行。 我已经验证数据正在加载并完全按预期保存到3个表中 以下是用户注册应用程序后表中数据的示例:
申请
ID 51
APPLICATION_USERS
USERS_ID APPLICATIONS_ID
1 51
USERS
ID
1
现在,当服务器停止并重新启动或使用create-tables重新部署应用程序时 (vs drop-and-create-tables)(并且数据存在于表中)然后我在每个实体toString()函数中得到堆栈溢出。 我已经在Applications toString()函数和Users toString()函数的调试中使用断点运行了这个,我可以单击resume并观察每个toString()函数被反复调用直到 堆栈溢出结果。
这是控制台日志:
(正在执行的实体查询)
[EL Fine]:2014-01-21 14:48:44.383 - 的ServerSession(1615948530) - 连接(49767657) - 线程(线程[HTTP-BIO-8080-EXEC-9,5,主]) - 选择 t1.ID,t1.APPIDENTIFIER,t1.DATECREATED,t1.DATEMODIFIED, t1.DEVICETYPE FROM APPLICATIONS_Users t0,APPLICATIONS t1 WHERE ((t0.users_ID =?)AND(t1.ID = t0.applications_ID))
(调用第二个实体查询)
[EL Fine]:2014-01-21 14:50:02.444 - 的ServerSession(1615948530) - 连接(1871047709) - 线程(线程[HTTP-BIO-8080-EXEC-9,5,主]) - 选择 t1.ID,t1.DATECREATED,t1.DATEMODIFIED,t1.EMAIL,t1.FIRSTNAME, t1.FULLNAME,t1.LASTLOGINDATE,t1.LASTNAME,t1.USERNAME FROM APPLICATIONS_Users t0,用户t1 WHERE((t0.applications_ID =?)AND (t1.ID = t0.users_ID))
[EL最佳]:2014-01-21 14:50:02.471 - 的ServerSession(1615948530) - 连接(1601422824) - 线程(线程[HTTP-BIO-8080-EXEC-9,5,主]) - 连接 发布到连接池[read]。
java.lang.StackOverflowError的
at java.util.Vector.get(Vector.java:693) at java.util.AbstractList$Itr.next(AbstractList.java:345) at java.util.AbstractCollection.toString(AbstractCollection.java:421) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Applications.toString(Applications.java:150) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Users.toString(Users.java:168) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Applications.toString(Applications.java:150) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940)
根据我读过的一些帖子,我尝试过: 1.反转映射, 2.将@JsonIgnore添加到某些实体字段中 3.使用fetch = FetchType.LAZY
和许多其他配置调整但没有一个解决了这个问题。 一些建议,如使用瞬态字段。我不确定我的eclipselink的JPA实现是否支持。
我还读了一个线程,建议我实现gson.ExclusionStrategy。 还没有尝试过。
这就是故事。我是Java和JPA的新手。 这对我来说是一个非常困难的问题。 如果您有任何建议可以帮我解决,我们将不胜感激。
答案 0 :(得分:1)
也许这对某人有帮助。
我遇到了同样的问题。我使用带有自定义@InvisibleJson
的注释ExclusionStrategy
来隐藏这些字段
public class SafeDataExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipClass(Class<?> c) {
return false;
}
@Override
public boolean shouldSkipField(FieldAttributes c) {
return c.getAnnotation(InvisibleJson.class) != null;
}
}
和
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface InvisibleJson {}
像
@InvisibleJson
@ManyToMany(mappedBy="roles")
public List<Staff> staff;
所以GSON并没有挖掘员工的内心。
答案 1 :(得分:0)
您的对象toString方法在整个模型上递归调用toString。堆栈最初没有显示它被调用的原因,但可能是由于某些日志记录设置。修复你的toString方法并关闭日志记录,
答案 2 :(得分:0)
非常简单的解决方案,对您希望允许GSON序列化/反序列化的所有变量和列表使用注释@Expose
。
一个基本上只允许GSON使用名称的简单示例:
@Expose
private String name;
@ManyToMany
private List<Users> users
然后,在使用GsonBuilder的地方,执行以下操作:
GsonBuilder builder = new GsonBuilder().disableHtmlEscaping();
builder.excludeFieldsWithoutExposeAnnotation(); //<-- This tells GSON look for @Expose
gson = builder.create();
此外,您可以将@Expose放在列表中,但是您需要进入用户实体并确保将@Expose放在该实体中的东西上,以便GSON深入其中。