Let’s say you have an entity with a property of type List
and you’re in the middle of your own save()
mehtod in your service layer. You’re provided by DTO that came from admin frontend and preparing an entity to persist to the repository.
Original code:
final List<Article> relatedArticles =
dto.getRelatedArticlesSorted().stream()
.map( id -> this.articleRepository.findById( id ).orElse( null ) )
.filter( Objects::nonNull )
.collect( Collectors.toList() ); // NOSONAR
entity.setRelatedArticles( relatedArticles );
Code that Sonar Lint proposes:
final List<Article> relatedArticles =
dto.getRelatedArticlesSorted().stream()
.map( id -> this.articleRepository.findById( id ).orElse( null ) )
.filter( Objects::nonNull )
.toList(); // generated list is immutable
entity.setRelatedArticles( relatedArticles );
The code which persists is simple:
this.articleRepository.save( entity );
While original code works perfectly, code proposed by SonarLint causes exception:
[ERROR] (CustomResponseEntityExceptionHandler.java:122) CustomResponseEntityExceptionHandler Exception: UnsupportedOperationException -> null
java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.clear(ImmutableCollections.java:149)
at org.hibernate.collection.spi.PersistentList.clear(PersistentList.java:294)
at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:506)
at org.hibernate.type.CollectionType.replace(CollectionType.java:719)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:117)
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:596)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:286)
at org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:220)
at org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:152)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:136)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:89)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:854)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:840)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:320)
at jdk.proxy4/jdk.proxy4.$Proxy201.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:639)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
...
Is there a way to use immutable list or is it just an overhead? If you ask me, I like things to be immutable in Java but then again, I would like to see what you guys think about this.
BR,
Hrvoje