I use SpringBoot with JPA to perform create and update requests using the following service method. As the create requests work like a charm, the update request will result in an IllegalArgumentException from within the framework if any of the two references to PaymentReason are changed in any way (That is if I try to set paymentReasonWriteOff to null or to set it to an actual instance of PaymentReason if it was null before ).
The service method throwing the error:
public Long createOrUpdateRemindLevelConfig(RemindLevelConfig remindLevelConfig) {
var paymReasonWriteOff = paymentReasonRepository.findById(
remindLevelConfig.getPaymentReasonWriteOff().getPaymentReasonId());
var paymReasonWriteOffFees = paymentReasonRepository.findById(
remindLevelConfig.getPaymentReasonWriteOffFees().getPaymentReasonId());
if (paymReasonWriteOff.isPresent()) {
remindLevelConfig.setPaymentReasonWriteOff(paymReasonWriteOff.get());
} else {
remindLevelConfig.setPaymentReasonWriteOff(null);
}
if (paymReasonWriteOffFees.isPresent()) {
remindLevelConfig.setPaymentReasonWriteOffFees(paymReasonWriteOffFees.get());
} else {
remindLevelConfig.setPaymentReasonWriteOffFees(null);
}
// will fail if any reference to PaymentReason was changed
return remindLevelConfigRepository.save(remindLevelConfig).getId();
}
The entity I want to update (simplified):
@Entity
@Table(name = "C_REMINDPARA")
@NoArgsConstructor
@Getter
@Setter
public class RemindLevelConfig {
@Column(name = "ID")
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@JoinColumn(name = "P_PAYMREASON_ID_WRITE_OFF", referencedColumnName = "ID")
@ManyToOne
@NotFound(action = NotFoundAction.IGNORE)
private PaymentReason paymentReasonWriteOff;
@JoinColumn(name = "P_PAYMREASON_ID_WRITE_OFF_FEES", referencedColumnName = "ID")
@ManyToOne
@NotFound(action = NotFoundAction.IGNORE)
private PaymentReason paymentReasonWriteOffFees;
}
The class whose reference I cannot change: Note that the primary Key is an Embedded Id but in RemindLevelConfig the natural Id is used to reference this entity.
@Entity
@Getter
@Setter
@Table(name = "p_paymreason")
public class PaymentReason implements Serializable {
@EmbeddedId
private PaymentReasonId paymentReasonId;
@OneToMany(mappedBy = "paymentReasonWriteOff")
private Collection<RemindLevelConfig> remindLevelConfigsWriteOff;
@OneToMany(mappedBy = "paymentReasonWriteOffFees")
private Collection<RemindLevelConfig> remindLevelConfigsWriteOffFees;
@Column(name = "ID")
@NaturalId
private Long id;
}
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Embeddable
@EqualsAndHashCode
public class PaymentReasonId implements Serializable {
@Column(name = "I_COMPANY")
private Long companyId;
@Column(name = "I_CUST_COMPANY")
private Long customerCompanyId;
@Column(name = "I_REASONCODE")
private Integer reasonCode;
}
When I try to update a RemindLevelConfig changing either paymentReasonWriteOff or paymentReasonWriteOffFees using the mentioned service function I get the following exception:
java.lang.IllegalArgumentException: Can not set java.lang.Long field backend.models.composite_keys.PaymentReasonId.companyId to backend.models.PaymentReason
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36) ~[na:na]
at java.base/java.lang.reflect.Field.get(Field.java:416) ~[na:na]
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:59) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ComponentType.getPropertyValue(ComponentType.java:419) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ComponentType.isDirty(ComponentType.java:262) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:335) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:345) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.type.TypeHelper.findDirty(TypeHelper.java:316) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:4586) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:585) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:242) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:169) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:232) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:92) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1352) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:443) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3202) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2370) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:534) ~[spring-orm-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:631) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178) ~[spring-data-jpa-2.2.6.RELEASE.jar:2.2.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at com.sun.proxy.$Proxy154.save(Unknown Source) ~[na:na]
at de.osp.paymentbackend.services.RemindLevelConfigService.createOrUpdateRemindLevelConfig(RemindLevelConfigService.java:83) ~[classes/:na]
at de.osp.paymentbackend.controllers.RemindLevelConfigController.updateRemindLevelConfig(RemindLevelConfigController.java:55) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:920) ~[spring-webmvc-5.2.5.RELEASE.jar:5.2.5.RELEASE]
I have no idea why it even would think of trying to modify the primary key in this case since I did not intend to create a new instance of PaymentReason. I just want to change the reference to it. I guess the reason of the exception is due to some mapping issue which I could not figure out so far. Also the service function does work when I create a new RemindLevelConfig.
Any help and suggestions would be appreciated!