@OneToManyで相互参照したEntityをThymeleafを使ってJavaScript内で呼び出すとStackOverFlowする件
こんにちはさのです。
今回業務で4つのSpringFrameworkに触れることができました。
Spring Boot
Spring Security
Spring Data Jpa (hibernate)
Spring MVC
まだまだ「理解した!」とは言い難いですが、せっかく使ったんでばんばん書いていこうと思います。
いきなり濃いめですが今回はJpaの@OneToManyで相互参照したEntityにJavaScript内からThymelafを使ってアクセスしたら無限ループが発生してStackOverFlowしてしまった件についてです。
Entityクラスは以下のとおりです。
会社Entityクラス
@Entity public class Company { @Id private Integer id; private String name; @OneToMany @JoinColumn(name="companyId", referencedColumnName="id") private List<Employee> employees; // setter getter constructorは省略 }
社員Entityクラス
@Entity public class Employee { @Id private id; private companyId; private name; @ManyToOne @JoinColumn(name="companyId") private Company company; // getter setter constructorは省略 }
2つのEntityクラスは1対多の関係で相互にgetterで参照することができます。
これをモデルにつっこんで、Thymeleafを使ってHTML内に挿入します。
<html> <script th:inline="javascript"> $(function(){ /* エラーが発生する */ var company = /*[[${company}]]*/ null; }); </script> <body> /* script外から参照する場合はエラーは発生しない */ <div th:text="${company.name}"></div> /* 以下略 */
${company}はThymeleafの記法で、Model内の"company"と名付けられた要素を参照します。
しかし、今回は以下のエラーが発生しました。
java.lang.StackOverflowError: null at java.beans.WeakIdentityMap.get(WeakIdentityMap.java:56) at java.beans.ThreadGroupContext.getContext(ThreadGroupContext.java:57) at java.beans.Introspector.getBeanInfo(Introspector.java:167) at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:354) at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184) at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347) at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338) at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366) at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184) at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347) at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338) at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366) at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184) at org.thymeleaf.util.JavaScriptUtils.printCollection(JavaScriptUtils.java:323) at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:173) at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347) at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338) at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366) at org.thymeleaf.util.JavaScriptUtils.print(JavaScriptUtils.java:184) at org.thymeleaf.util.JavaScriptUtils.printKeyValue(JavaScriptUtils.java:347) at org.thymeleaf.util.JavaScriptUtils.printMap(JavaScriptUtils.java:338) at org.thymeleaf.util.JavaScriptUtils.printObject(JavaScriptUtils.java:366) =================以下ループ===================
これは${company}で参照する際、すべてのgetterメソッドを呼び出す仕様のためです。
CompanyクラスのgetEmployees()を呼び、更にEmployeeクラスがgetCompany()を呼び、またそのCompanyが・・・と、無限ループしてしまいます。
対策は相互参照をやめるかThymeleaf用にオブジェクトを作るしかないかと思われます。
ちなみに、script外では問題なく呼び出せます。
script内では参照されたオブジェクトが操作されるため、先にオブジェクト内部をすべて把握しておく必要があるのでしょう。
そう考えると改善は難しいかもしれません。