My research suggests that this error occurs when something like item1.setOptions(item2.getOptions()) occurs. Solutions generally suggest searching for and removing anything like that. I am never calling Item.setOptions anywhere in my own code, so it seems to me that whatever is causing this must be within Spring GraphQL or JPA code or somewhere else. How do I stop it from causing this error?
I am trying to apply GraphQL to a particular data model with a Many to Many mapping. I have two classes, simplified below:
@Entity
@Data
@NoArgsConstructor
@Table(name="items_table")
public class Item implements Serializable {
private static final long serialVersionUID = -2084220931830648548L;
@Id
@Column(nullable = false)
String id;
@Basic(optional = false)
@Column(nullable = false)
String type;
@ManyToMany
@JoinTable(name="options_table",
joinColumns = @JoinColumn(name="type", referencedColumnName="type"),
inverseJoinColumns = @JoinColumn(name="id", referencedColumnName="id"))
List<Option> options;
}
@Entity
@Data
@NoArgsConstructor
@Table(name="options_table")
public class Option implements Serializable {
private static final long serialVersionUID = 7050760122397397513L;
@Id
@Column(nullable = false)
Long id;
@Basic(optional = false)
@Column(nullable = false)
String type;
}
The GraphQL Schema, again simplified:
type Query {
items: [Item]
}
type Item{
id : Int
type: String
options: [Option]
}
type Option {
id : Int
type: String
}
The Controller class and Service. If I use @SchemaMapping rather than @BatchMapping I do not get this error, but this is a large dataset so that is inefficient.:
@Controller
public class GraphQLController {
@Autowired
private GraphQLService graphqlService;
@QueryMapping
List<Item> items()
{
return graphqlService.getItems();
}
@BatchMapping
Map<Item, List<Option>> options(List<Item> items) {
return graphqlService.getOptionsByTypes(items);
}
}
@Service
public class GraphService {
@Autowired private ItemRepository itemRepo;
@Autowired private OptionRepository optionRepo;
public GraphService() {
super();
}
@Transactional(noRollbackFor=Exception.class)
public List<Item> getItems() {
return StreamSupport.stream(itemRepo.findAll().spliterator(), false).toList();
}
@Transactional(readOnly=true, noRollbackFor=Exception.class)
public Map<Item, List<Option>> getOptionsByTypes(List<Item> items) {
List<String> types = items.stream().map(Item::getType).collect(Collectors.toList());
Map<String, List<Option>> optionsByType= StreamSupport.stream(optionRepo.findByTypeIn(types).spliterator(), false).collect(Collectors.groupingBy(Option::getType));
Map<Item, List<Option>> itemOptionsMap = new HashMap<>();
for (Item item : items) {
List<Option> newList = new ArrayList<>();
newList.addAll(optionsByType.get(item.getType()));
itemOptionsMap.put(item, newList);
}
return itemOptionsMap;
}
}
The repositories only extend JpaRepository and include findByTypeIn(List<String> types) where necessary.
Whenever I do a graphiql query that includes 'items', even if I do not ask for 'options', I get this error:
org.springframework.orm.jpa.JpaSystemException: Found shared references to a collection: my.package.Item.options; nested exception is org.hibernate.HibernateException: Found shared references to a collection: my.package.Item.options
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:331) ~[spring-orm-5.3.21.jar:5.3.21]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233) ~[spring-orm-5.3.21.jar:5.3.21]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:566) ~[spring-orm-5.3.21.jar:5.3.21]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-5.3.21.jar:5.3.21]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.3.21.jar:5.3.21]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654) ~[spring-tx-5.3.21.jar:5.3.21]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407) ~[spring-tx-5.3.21.jar:5.3.21]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.21.jar:5.3.21]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.21.jar:5.3.21]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.21.jar:5.3.21]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.21.jar:5.3.21]
at my.package.GraphQLService$$EnhancerBySpringCGLIB$$1b574e58.getItems(<generated>) ~[classes/:na]
at my.package.GraphController.items(GraphController.java:76) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.doInvoke(InvocableHandlerMethodSupport.java:87) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.validateAndInvoke(DataFetcherHandlerMethod.java:191) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:122) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:497) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:64) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.execution.ReactorContextManager.invokeCallable(ReactorContextManager.java:104) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:63) ~[spring-graphql-1.0.0.jar:1.0.0]
at org.springframework.boot.actuate.metrics.graphql.GraphQlMetricsInstrumentation.lambda$instrumentDataFetcher$1(GraphQlMetricsInstrumentation.java:98) ~[spring-boot-actuator-2.7.1.jar:2.7.1]
at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:279) ~[graphql-java-18.1.jar:na]
at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:210) ~[graphql-java-18.1.jar:na]
at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:60) ~[graphql-java-18.1.jar:na]
at graphql.execution.Execution.executeOperation(Execution.java:160) ~[graphql-java-18.1.jar:na]
at graphql.execution.Execution.execute(Execution.java:106) ~[graphql-java-18.1.jar:na]
at graphql.GraphQL.execute(GraphQL.java:641) ~[graphql-java-18.1.jar:na]
at graphql.GraphQL.lambda$parseValidateAndExecute$11(GraphQL.java:561) ~[graphql-java-18.1.jar:na]
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) ~[na:na]
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309) ~[na:na]
at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:556) ~[graphql-java-18.1.jar:na]
at graphql.GraphQL.executeAsync(GraphQL.java:524) ~[graphql-java-18.1.jar:na]
at org.springframework.graphql.execution.DefaultExecutionGraphQlService.lambda$execute$2(DefaultExecutionGraphQlService.java:81) ~[spring-graphql-1.0.0.jar:1.0.0]
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:47) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:157) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:160) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.netty.channel.FluxReceive.terminateReceiver(FluxReceive.java:468) ~[reactor-netty-core-1.0.20.jar:1.0.20]
at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:260) ~[reactor-netty-core-1.0.20.jar:1.0.20]
at reactor.netty.channel.FluxReceive.request(FluxReceive.java:129) ~[reactor-netty-core-1.0.20.jar:1.0.20]
at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxPeek$PeekSubscriber.request(FluxPeek.java:138) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.MonoCollect$CollectSubscriber.onSubscribe(MonoCollect.java:104) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onSubscribe(FluxPeek.java:171) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.core.publisher.FluxMap$MapSubscriber.onSubscribe(FluxMap.java:92) ~[reactor-core-3.4.19.jar:3.4.19]
at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:167) ~[reactor-netty-core-1.0.20.jar:1.0.20]
at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:146) ~[reactor-netty-core-1.0.20.jar:1.0.20]
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503) ~[netty-transport-4.1.78.Final.jar:4.1.78.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.78.Final.jar:4.1.78.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: org.hibernate.HibernateException: Found shared references to a collection: my.package.Item.options
at org.hibernate.engine.internal.Collections.processReachableCollection(Collections.java:188) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.FlushVisitor.processCollection(FlushVisitor.java:53) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:104) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:59) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:183) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:229) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:93) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1407) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:489) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3290) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2425) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:449) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-5.6.9.Final.jar:5.6.9.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562) ~[spring-orm-5.3.21.jar:5.3.21]
... 69 common frames omitted