2

I have two Entities: 1. Post. 2. Tag. They are implemented as follows:

@Entity(name = "Post")
@Table(name = "post")
data class Post(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var title: String,
@ManytToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
@Fetch(value= FetchMode.SELECT)
@JoinTable(name = "post_tag",
joinColumns = [JoinColumn(name = "post_id")],
inverseJoinColumns = [JoinColumn(name = "tag_id")],)
@JsonIgnoreProperties("posts")
var tags: Set<Tag> = mutableSetOf()
)


@Entity(name = "Tag")
@Table(name = "tag")
data class Tag(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val name: String,
@ManyToMany(mappedBy = "tags")
@JsonIgnoreProperties("tags")
var posts: Set<Post> = mutableSetOf(),
)

I can fill them with data, so I have data in every of the three tables.

But when I call for example postReository.getAll the below error is thrown: The warning line over 500 times, so it seems to be something recursive and after that the error and than the following StackOverflowError.

The intresting thing is when I use var tags: List<Tag> = mutableListOf() and var posts: List<Post> = mutableListOf(), instead of set, it is working. But I read using a set instead of a list is more performant.

So is it possible to make it running using set? Or what is the best practice to implement this in kotlin?

2022-02-01 20:51:03.585  WARN 44603 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts      : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@6cda6288<rs=HikariProxyResultSet@2079611130 wrapping org.postgresql.jdbc.PgResultSet@2f8e2b05>
2022-02-01 20:51:03.585  WARN 44603 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts      : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@2777867f<rs=HikariProxyResultSet@1686136316 wrapping org.postgresql.jdbc.PgResultSet@570a7adb>
2022-02-01 20:51:03.585  WARN 44603 --- [nio-8080-exec-2] o.h.e.loading.internal.LoadContexts      : HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@68fb8c90<rs=HikariProxyResultSet@1899379583 wrapping org.postgresql.jdbc.PgResultSet@36f34489>

2022-02-01 20:51:03.592 ERROR 44603 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause

java.lang.StackOverflowError: null
at java.base/java.nio.ByteBuffer.limit(ByteBuffer.java:1529) ~[na:na]
at java.base/java.nio.MappedByteBuffer.limit(MappedByteBuffer.java:330) ~[na:na]
at java.base/java.nio.MappedByteBuffer.limit(MappedByteBuffer.java:73) ~[na:na]
at java.base/sun.nio.ch.Util$BufferCache.get(Util.java:172) ~[na:na]
at java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Util.java:232) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.tryWrite(NioSocketImpl.java:394) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:413) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826) ~[na:na]
at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1035) ~[na:na]
at java.base/java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:81) ~[na:na]
at java.base/java.io.BufferedOutputStream.flush(BufferedOutputStream.java:142) ~[na:na]
at org.postgresql.core.PGStream.flush(PGStream.java:665) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.sendSync(QueryExecutorImpl.java:1482) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:320) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114) ~[postgresql-42.2.24.jre7.jar:42.2.24.jre7]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.getResultSet(AbstractLoadPlanBasedLoader.java:390) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:104) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at com.bla.blabla.model.Post.hashCode(Post.kt) ~[main/:na]
at java.base/java.util.HashMap.hash(HashMap.java:338) ~[na:na]
at java.base/java.util.HashMap.put(HashMap.java:610) ~[na:na]
at java.base/java.util.HashSet.add(HashSet.java:221) ~[na:na]
at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:336) ~[na:na]
at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:336) ~[na:na]
at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:253) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:211) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:710) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2163) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
at com.bla.blabla.model.Tag.hashCode(Tag.kt) ~[main/:na]
at java.base/java.util.HashMap.hash(HashMap.java:338) ~[na:na]
at java.base/java.util.HashMap.put(HashMap.java:610) ~[na:na]
at java.base/java.util.HashSet.add(HashSet.java:221) ~[na:na]

Adding the following override function to each of the class bodies did the trick:

@Entity(name = "Post")
@Table(name = "post")
data class Post(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
var title: String,
@ManytToMany(cascade = [CascadeType.ALL], fetch = 
FetchType.EAGER)
@Fetch(value= FetchMode.SELECT)
@JoinTable(name = "post_tag",
joinColumns = [JoinColumn(name = "post_id")],
inverseJoinColumns = [JoinColumn(name = "tag_id")],)
@JsonIgnoreProperties("posts")
var tags: Set<Tag> = mutableSetOf()
) {
override fun hashCode(): Int {
    return id.hashCode()
}
}


@Entity(name = "Tag")
@Table(name = "tag")
data class Tag(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val name: String,
@ManyToMany(mappedBy = "tags")
@JsonIgnoreProperties("tags")
var posts: Set<Post> = mutableSetOf(),
) {
override fun hashCode(): Int {
    return id.hashCode()
}
}
midi
  • 3,128
  • 5
  • 30
  • 47

1 Answers1

1

The problem here is that determining the hashCode of the data class instance is leading to a stackoverflow error due to the bidirectional association (mutual references).

To avoid this you need to exclude the @ManyToMany properties from the hashCode method (e.g. by explicitly declaring hashCode on your own).

Property include/exclude on Kotlin data classes

fladdimir
  • 1,230
  • 1
  • 5
  • 12