You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot更新产品触发java.lang.UnsupportedOperationException问题求助

Spring Boot产品更新时UnsupportedOperationException异常排查与解决

问题场景

Spring Boot开发中,新增产品功能正常,但调用save方法执行更新操作时抛出java.lang.UnsupportedOperationException,已确认产品ID存在,使用环境为Java 17、Spring Framework 5.3.18。

相关代码

1. ProductService的save方法实现

@Override
public ProductDTO save(ProductDTO product) {
    ProductEntity entity= null;
    if(product.getId()!=null)
    {
        ProductEntity oldProduct= productrepository.findById(product.getId()).get();
        entity=converter.toEntity(product, oldProduct);
    }else {
        entity= converter.modelMapper().map(product, ProductEntity.class);
    }
    entity.setCategory(categoryRepository.findById(product.getCategoryId()).get());
    entity.setOrigins(toList(originRepository.findById(product.getOriginId())));
    entity.setMaterials(toList(materialRepository.findById(product.getMaterialId())));
    entity.setType(typeRepository.findById(product.getTypeId()).get());
    productrepository.save(entity);
    return converter.modelMapper().map(entity, ProductDTO.class);
}

2. 新增与更新产品API接口

@PostMapping(value="/product")
public ProductDTO addProduct(@ModelAttribute ProductDTO model)
{
    return iProductService.save(model);
}
@PutMapping(value="/product")
public ProductDTO editProduct(@ModelAttribute ProductDTO model)
{
    return iProductService.save(model);
}

异常栈信息

2022-09-08 20:19:40.892 ERROR 14524 --- [nio-8081-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.UnsupportedOperationException] with root cause

java.lang.UnsupportedOperationException: null
    at java.base/java.util.AbstractList.remove(AbstractList.java:167) ~[na:na]
    at java.base/java.util.AbstractList$Itr.remove(AbstractList.java:387) ~[na:na]
    at java.base/java.util.AbstractList.removeRange(AbstractList.java:598) ~[na:na]
    at java.base/java.util.AbstractList.clear(AbstractList.java:243) ~[na:na]
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:580) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.type.CollectionType.replace(CollectionType.java:757) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.type.TypeHelper.replace(TypeHelper.java:167) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:450) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:205) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:178) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:70) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:829) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:816) ~[hibernate-core-5.6.7.Final.jar:5.6.7.Final]
    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.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:362) ~[spring-orm-5.3.18.jar:5.3.18]
    at jdk.proxy25/jdk.proxy25.$Proxy348.merge(Unknown Source) ~[na: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.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311) ~[spring-orm-5.3.18.jar:5.3.18]
    at jdk.proxy25/jdk.proxy25.$Proxy348.merge(Unknown Source) ~[na:na]
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:640) ~[spring-data-jpa-2.6.3.jar:2.6.3]
    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.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:529) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.6.3.jar:2.6.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.18.jar:5.3.18]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.18.jar:5.3.18]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.18.jar:5.3.18]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.18.jar:5.3.18]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174) ~[spring-data-jpa-2.6.3.jar:2.6.3]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.18.jar:5.3.18]
    at jdk.proxy25/jdk.proxy25.$Proxy350.save(Unknown Source) ~[na:na]
    at com.lifood.stuneed.service.impl.ProductService.save(ProductService.java:81) ~[classes/:na]
    at com.lifood.stuneed.api.ProductAPI.addProduct(ProductAPI.java:46) ~[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]

问题排查与解决方案

核心原因

异常栈显示Hibernate在清理集合时调用AbstractList.clear()抛出异常,说明**entity.setOrigins()或entity.setMaterials()设置的集合是不可修改的**。

大概率是toList方法返回了Arrays.asList()生成的集合,或是Spring Data JPA查询返回的不可修改集合,这类集合不支持增删改操作;新增时实体是瞬时态,Hibernate会直接创建新集合所以无问题,更新时实体是持久态,Hibernate需要修改现有集合,因此触发异常。

解决方案

方案1:修改toList方法返回可修改集合

private <T> List<T> toList(Optional<T> optional) {
    List<T> list = new ArrayList<>();
    optional.ifPresent(list::add);
    return list;
}

方案2:在调用处直接转换为可修改集合

如果不想修改toList方法,可在设置集合时转换:

entity.setOrigins(new ArrayList<>(toList(originRepository.findById(product.getOriginId()))));
entity.setMaterials(new ArrayList<>(toList(materialRepository.findById(product.getMaterialId()))));

内容的提问来源于stack exchange,提问作者stupidlifood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:25:30