Spring-data не может обновить объект с идентификатором 0 - PullRequest
0 голосов
/ 09 мая 2019

У меня есть следующий класс сущностей:

@Entity
public class Item {
    @Id
    private long id;
    private String name;
    //getters & setters
}

Стандартный репо:

public interface Repo extends CrudRepository<Item, Long> {}

Простой restController:

@RestController
public class Controller {
    @Autowired
    private Repo repo;

    @GetMapping("1")
    public Iterable<Item> getItems1() {
        Item item = new Item();
        item.setId(1);
        item.setName("name1");
        repo.save(item);

        item = new Item();
        item.setId(1);
        item.setName("name11");
        repo.save(item);

        return repo.findAll();
    }

    @GetMapping("0")
    public Iterable<Item> getItems0() {
        Item item = new Item();
        item.setId(0);
        item.setName("name0");
        repo.save(item);

        item = new Item();
        item.setId(0);
        item.setName("name00");
        repo.save(item);

        return repo.findAll();
    }
}

Зависимости: spring-boot-starter-web, spring-boot-starter-data-jpa, & com.h2database: h2.

Тестирование:

curl -si http://localhost:8080/1
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked

[{"id":1,"name":"name11"}]

curl -si http://localhost:8080/0
HTTP/1.1 500
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: close

{"timestamp":"2019-05-09T16:45:01.734+0000","status":500,"error":"Internal Server Error","message":"A different object with the same identifier value was already associated with the session : [test.Item#0]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [test.Item#0]","path":"/0"}

Почему хранение с идентификатором 1 (или любым другим идентификатором) работает и корректно обновляет объект, в то время как сохранение с идентификатором 0 всегда приводит к сбою во втором хранилище?

Я погуглил исключение и понял, что это, вероятно, связано с гибернацией, думая, что это неинициализированный идентификатор?Но не смог понять точную логику того, как он попал в это исключение.

Трассировка стека:

javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [test.Item#0]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:118) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:790) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:768) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_202]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_202]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_202]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_202]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350) ~[spring-orm-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at com.sun.proxy.$Proxy74.persist(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_202]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_202]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_202]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_202]
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305) ~[spring-orm-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at com.sun.proxy.$Proxy74.persist(Unknown Source) ~[na:na]
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:490) ~[spring-data-jpa-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_202]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_202]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_202]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_202]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:641) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:590) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135) ~[spring-data-jpa-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.0.10.RELEASE.jar:2.0.10.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at com.sun.proxy.$Proxy76.save(Unknown Source) ~[na:na]
at test.Controller.getItems0(Controller.java:37) ~[classes/:na] <-- when saving (0, "item00")

1 Ответ

1 голос
/ 09 мая 2019

Посмотрите на реализацию SimpleJpaRepository.save

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

Чем проверка AbstractEntityInformation.isNew.В нем делается вывод, что сущность является новой, только если она имеет нулевое значение (или 0 для числовых типов).

У вас есть 2 варианта для решения этой ситуации:

  • использование entityManager.persistили entityManager.merge вместо хранилища данных Spring
  • происходит от org.springframework.data.domain.Persistable и реализует isNew()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...