michle sheejo
michle sheejo

Reputation: 5

Save does not work with @OneToMany - having a composite Id embedded in child element

I am trying to save entity with OnetoMany relation. Am expecting that, the child items attached to the parents will automatically saved on saving parent. But it fails with following exception. I doubt that composite key that embedded at child element is making trouble. Can somebody help me..

Execption

org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.spring.oe.entity.OrderItem#OrderItemId [order=Order [orderId=116, customer=Customer [CustomerId=20, name=Dollar General, address=113 Washington Sq N, Lansing, MI, website=http://www.dollargeneral.com, creditLimit=200.0], status=PENDING, saleman=null, orderDate=null], itemId=null]]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.spring.oe.entity.OrderItem#OrderItemId [order=Order [orderId=116, customer=Customer [CustomerId=20, name=Dollar General, address=113 Washington Sq N, Lansing, MI, website=http://www.dollargeneral.com, creditLimit=200.0], status=PENDING, saleman=null, orderDate=null], itemId=null]]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:400)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at com.sun.proxy.$Proxy95.save(Unknown Source)
at com.spring.oe.service.OrderService.placeRandomOrder(OrderService.java:54)
at com.spring.oe.service.OrderService$$FastClassBySpringCGLIB$$b1e9fc84.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.spring.oe.service.OrderService$$EnhancerBySpringCGLIB$$6d281358.placeRandomOrder(<generated>)
at com.spring.oe.OrderTest.testPlaceRandomOrder(OrderTest.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.spring.oe.entity.OrderItem#OrderItemId [order=Order [orderId=116, customer=Customer [CustomerId=20, name=Dollar General, address=113 Washington Sq N, Lansing, MI, website=http://www.dollargeneral.com, creditLimit=200.0], status=PENDING, saleman=null, orderDate=null], itemId=null]]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:750)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:712)
at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:532)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:463)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:426)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:459)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:293)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:93)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:720)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:706)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
at com.sun.proxy.$Proxy83.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:557)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:524)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:531)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:156)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:131)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
... 86 more

Order.java / Parent class

@Entity
@Table(name = "ORDERS")
public class Order implements Serializable{

@Id
@SequenceGenerator(name = "ordSeq", sequenceName = "ORDER_SEQ", allocationSize = 1)
@GeneratedValue(generator = "ordSeq")
private Long orderId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "CUSTOMER_ID")
private Customer customer;
private String status;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "SALESMAN_ID")
private Employee saleman;
@CreationTimestamp
private Date orderDate;

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "ORDER_ID")
private List<OrderItem> orderItems;
.
.
.
.
public void addItem(OrderItem item) {
    if(CollectionUtils.isEmpty(getOrderItems()))
        this.orderItems = new ArrayList<OrderItem>();
    this.orderItems.add(item);
    item.setOrderItemId(new OrderItemId());
    item.getOrderItemId().setOrder(this);
}
}

OrderItem.java /child.class

@Entity
@Table(name = "ORDER_ITEMS")
public class OrderItem {

@EmbeddedId
OrderItemId orderItemId;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "PRODUCT_ID")
private Product product;
private Double quantity;
private Double unitPrice;
....}

**OrderItemId.java /composite Key @ child referring to parent

@Embeddable
public class OrderItemId implements Serializable {

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "ORDER_ID", referencedColumnName = "orderId")
private Order order;
@SequenceGenerator(name = "ordItmSeq", sequenceName = "ORDER_ITEM_SEQ", allocationSize = 1)
@GeneratedValue(generator = "ordItmSeq")
private Long itemId;
....
....
}

My Service class

In my service class, I am placing an order by fetching random products and saleman and customer.Then I create order item from the random products and link them to order and saving it.

@Service
public class OrderService {

@Autowired
ProductService productService;

@Autowired
EmployeeService employeeService;

@Autowired
CustomerRepo customerRepo;

@Autowired
OrderRepo orderRepo;

@Transactional
public Order placeRandomOrder(int noOfProducts, int maxItemPerProduct) {
    Order order = new Order();
    Employee employee = employeeService.findRandomEmployeeByTitle("Sales Representative");
    List<Product> products = productService.getRandomProducts(noOfProducts);
    Random random = new Random();

    order.setSaleman(employee);
    order.setStatus(OrderStatus.PENDING.toString());
    order.setCustomer(CollectionUtils.firstElement(customerRepo.fetchRandomCustomers(1)));

    for (Product product : products) {
        OrderItem orderItem = new OrderItem();
        orderItem.setProduct(product);
        orderItem.setQuantity(random.nextInt(maxItemPerProduct) + random.nextDouble());
        orderItem.setUnitPrice(product.getListPrice());
        order.addItem(orderItem);
    }

    orderRepo.save(order);
    return order;
}

}

Upvotes: 0

Views: 829

Answers (1)

fladdimir
fladdimir

Reputation: 1365

Infact, the problem here is that the automatic generation of the itemId does not work when it is specified inside the composite EmbeddedId (which is "assignment"-based).
(Trying to just save an order with only one item would get you a clearer error message revealing the root problem.)

The automatic generation of ID values inside of embedded IDs does not seem to work as one would expect, as various (old) discussions of this topic show, e.g.:

Hibernate composite key id generator

How can I use generated value within composite keys?

Also see Chapter 6.2. Composite identifiers of the official documentation.


A common workaround involves moving the generated field directly into the entity and using an @IdClass: https://vladmihalcea.com/how-to-map-a-composite-identifier-using-an-automatically-generatedvalue-with-jpa-and-hibernate/

E.g.:

@IdClass(OrderItemId.class)
public class OrderItem {

  @Id
  @ManyToOne(fetch = FetchType.EAGER)
  private Order order;

  @Id
  @SequenceGenerator(name = "ordItmSeq", sequenceName = "ORDER_ITEM_SEQ", allocationSize = 1)
  @GeneratedValue(generator = "ordItmSeq")
  private Long itemId;

}

and

public class OrderItemId implements Serializable {

  private Order order;

  private Long itemId;
}

Apart from that, your itemId sequence is already generating unique values for all items independently of the referenced order, so that you would not need a composite PK here.

Upvotes: 1

Related Questions