无法将自定义类型存储在数据库中-JSON字符串-Java.lang.illegalargumentException:无法将最终字段设置为java.util.linkedhashmap

发布于 2025-02-05 16:31:42 字数 13951 浏览 2 评论 0原文

我在JPA实体中使用自定义类型有问题,然后将其转换为JSON字符串中的数据库。

这是基本JPA实体:折扣(我们正在使用Lombok和构建器来创建对象实例。此问题发生在类型局部化文本的两个字段上):

@Value
@NoArgsConstructor(force = true)
@AllArgsConstructor
@Builder(builderMethodName = "internalBuilder")
@Entity
@Table(name="discount")
public class Discount {
    
    @Id
    @Column(name = "ID", nullable = false, unique = true)
    @Type(type="uuid-char")
    UUID id;
    
    @Column(name="NO", nullable = false, unique = true)
    String no;
    
    @Column(name="I_NO", nullable = false, unique = true)
    Integer iNo;
    
    @Convert(converter = JpaJsonConverter.class)
    @Column(name="DESIGNATION", nullable = false)
    LocalizedTexts designation;
    
    @Convert(converter = JpaJsonConverter.class)
    @Column(name="PRINT_TEXT")
    LocalizedTexts printText;
    
    ...

    /**
     * Builder with required parameters
     */
    public static Discount.DiscountBuilder builder(UUID id, String no, LocalizedTexts designation) {
        return internalBuilder()
                .id(id)
                .no(no)
                .designation(designation);
    }

    // Getters
    ...

}

我使用属性转换器来转换自定义类型(扩展Map<对象,字符串):

@Converter
public class JpaJsonConverter implements AttributeConverter<Object, String> {

    private final static ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Object localizedTexts) {
        String localizedTextsJson = null;
        try {
            localizedTextsJson = objectMapper.writeValueAsString(localizedTexts);
        } catch (JsonProcessingException ex) {
            //throw new RuntimeException();
        }
        return localizedTextsJson;
    }

    @Override
    public Object convertToEntityAttribute(String localizedTextsJSON) {
        Object localizedTexts = null;
        try {
            localizedTexts = objectMapper.readValue(localizedTextsJSON, Object.class);
        } catch (IOException ex) {
            //throw new RuntimeException();
        }
        return localizedTexts;
    }
}

这是自定义类型:本地化texxts(map&lt; enum,string&gt;):

public class LocalizedTexts extends HashMap<Language, String> implements EntityBase {
    public LocalizedTexts() {
    }

    public LocalizedTexts(Map map) {
        this.putAll(map);
    }

    public static LocalizedTextsBuilder internalBuilder() {
        return new LocalizedTextsBuilder();
    }

    public static class LocalizedTextsBuilder {
        LocalizedTextsBuilder() {
        }

        public LocalizedTexts build() {
            return new LocalizedTexts();
        }

        public String toString() {
            return "LocalizedTexts.LocalizedTextsBuilder()";
        }
    }
}

当我试图将折扣对象保存到数据库中时,问题发生了:

...
discountRepository.save(discount);
...

我已经检查了且属性转换器按预期工作(变换局部化teminizedtexts to JSON弦)。我得到的例外的完整堆栈痕迹如下:

org.springframework.orm.jpa.JpaSystemException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation; nested exception is org.hibernate.PropertyAccessException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation

    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:331)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
    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 jdk.proxy2/jdk.proxy2.$Proxy116.save(Unknown Source)
    at net.xxxxx.yyyyy.svc_voucher.messagehandler.DiscountHandler.handleCommand(DiscountHandler.java:58)
    at net.xxxxx.yyyyy.svc_voucher.handlers.TestDiscountHandler.Sending_script_to_messageHandler_handleCommand(TestDiscountHandler.java:109)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.hibernate.PropertyAccessException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:72)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:681)
    at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:144)
    at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:5252)
    at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:498)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:241)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:318)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:172)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:70)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:829)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:816)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
    at jdk.proxy2/jdk.proxy2.$Proxy113.merge(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:650)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    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:529)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
    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)
    ... 42 more
Caused by: java.lang.IllegalArgumentException: Can not set final net.xxxxx.yyyyy.entity.internal.LocalizedTexts field net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation to java.util.LinkedHashMap
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:83)
    at java.base/java.lang.reflect.Field.set(Field.java:799)
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:52)
    ... 81 more

I have a problem using a custom type into my JPA entity and then convert and store it into the database as JSON string.

Here is the base JPA entity: Discount (We are using Lombok and builders for creating object instances. The issue occurs on the two fields of type LocalizedTexts):

@Value
@NoArgsConstructor(force = true)
@AllArgsConstructor
@Builder(builderMethodName = "internalBuilder")
@Entity
@Table(name="discount")
public class Discount {
    
    @Id
    @Column(name = "ID", nullable = false, unique = true)
    @Type(type="uuid-char")
    UUID id;
    
    @Column(name="NO", nullable = false, unique = true)
    String no;
    
    @Column(name="I_NO", nullable = false, unique = true)
    Integer iNo;
    
    @Convert(converter = JpaJsonConverter.class)
    @Column(name="DESIGNATION", nullable = false)
    LocalizedTexts designation;
    
    @Convert(converter = JpaJsonConverter.class)
    @Column(name="PRINT_TEXT")
    LocalizedTexts printText;
    
    ...

    /**
     * Builder with required parameters
     */
    public static Discount.DiscountBuilder builder(UUID id, String no, LocalizedTexts designation) {
        return internalBuilder()
                .id(id)
                .no(no)
                .designation(designation);
    }

    // Getters
    ...

}

I am using an Attribute converter to transform the custom type (extending a Map<Object, String):

@Converter
public class JpaJsonConverter implements AttributeConverter<Object, String> {

    private final static ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Object localizedTexts) {
        String localizedTextsJson = null;
        try {
            localizedTextsJson = objectMapper.writeValueAsString(localizedTexts);
        } catch (JsonProcessingException ex) {
            //throw new RuntimeException();
        }
        return localizedTextsJson;
    }

    @Override
    public Object convertToEntityAttribute(String localizedTextsJSON) {
        Object localizedTexts = null;
        try {
            localizedTexts = objectMapper.readValue(localizedTextsJSON, Object.class);
        } catch (IOException ex) {
            //throw new RuntimeException();
        }
        return localizedTexts;
    }
}

And here is the custom type: LocalizedTexts (Map<Enum,String>):

public class LocalizedTexts extends HashMap<Language, String> implements EntityBase {
    public LocalizedTexts() {
    }

    public LocalizedTexts(Map map) {
        this.putAll(map);
    }

    public static LocalizedTextsBuilder internalBuilder() {
        return new LocalizedTextsBuilder();
    }

    public static class LocalizedTextsBuilder {
        LocalizedTextsBuilder() {
        }

        public LocalizedTexts build() {
            return new LocalizedTexts();
        }

        public String toString() {
            return "LocalizedTexts.LocalizedTextsBuilder()";
        }
    }
}

The issue occurs when I am trying to save a Discount object into the database:

...
discountRepository.save(discount);
...

I have checked and the attribute converter works as expected (transform LocalizedTexts into JSON strings). The full stack trace of the exception I get is the following:

org.springframework.orm.jpa.JpaSystemException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation; nested exception is org.hibernate.PropertyAccessException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation

    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:331)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
    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 jdk.proxy2/jdk.proxy2.$Proxy116.save(Unknown Source)
    at net.xxxxx.yyyyy.svc_voucher.messagehandler.DiscountHandler.handleCommand(DiscountHandler.java:58)
    at net.xxxxx.yyyyy.svc_voucher.handlers.TestDiscountHandler.Sending_script_to_messageHandler_handleCommand(TestDiscountHandler.java:109)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.hibernate.PropertyAccessException: Could not set field value [{de=Rabatt 1, en=Discount 1}] value by reflection : [class net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation] setter of net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:72)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:681)
    at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:144)
    at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:5252)
    at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:498)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:241)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:318)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:172)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:70)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:829)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:816)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
    at jdk.proxy2/jdk.proxy2.$Proxy113.merge(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:650)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    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:529)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
    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)
    ... 42 more
Caused by: java.lang.IllegalArgumentException: Can not set final net.xxxxx.yyyyy.entity.internal.LocalizedTexts field net.xxxxx.yyyyy.svc_voucher.entity.Discount.designation to java.util.LinkedHashMap
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:83)
    at java.base/java.lang.reflect.Field.set(Field.java:799)
    at org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:52)
    ... 81 more

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乖乖兔^ω^ 2025-02-12 16:31:43

起初,人们可以相信最终修饰符是造成问题的原因,但实际上不是。

让我们尝试以下简单的测试:

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.set(d, 1);
    }
}

我们得到:

java.lang.IllegalAccessException: class Test cannot access a member of class Discount with modifiers "private final"

这是不是您遇到的错误(您的stackTrace显示IllegalArgumentException,而不是Illegalacccessexception) 。

让我们更改代码以添加setAccessible(true)

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(d, 1);
    }
}

这次起作用,没有例外。

现在,让我们用1 “ 1”

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(d, "1");
    }
}

我们得到:

java.lang.IllegalArgumentException: Can not set final java.lang.Integer field Discount.id to java.lang.String

这次这与您的错误完全相同。 IllegalArgumentException表示我们正在尝试将不兼容的值分配(此处,String integer )。

您的stacktrace说,您正在尝试将linkedHashmap分配给指定字段,这确实是不兼容的(名称aboce> intaborized> localizedTexts)。

要了解linkedHashmap来自何处,让我们看一下您的jpajsonConverter。负责将JSON字符串转换为本地化texts对象的方法是:

public Object convertToEntityAttribute(String localizedTextsJSON) {
    Object localizedTexts = null;
    try {
        localizedTexts = objectMapper.readValue(localizedTextsJSON, Object.class);
    } catch (IOException ex) {
        //throw new RuntimeException();
    }
    return localizedTexts;
}

您正在使用包含对象的JSON字符串和object.class。作为第二个参数。
在这种情况下,杰克逊返回linkedhashmap。它无法知道您想要entainizedTexts

这是修复该方法的可能方法:

@SuppressWarnings("unchecked")
public Object convertToEntityAttribute(String localizedTextsJSON) {
    LocalizedTexts localizedTexts = new LocalizedTexts();
    try {
        Map<String,String> map = (Map<String,String>)objectMapper.readValue(localizedTextsJSON, Object.class);
        for(Map.Entry<String,String> e : map.entrySet())
            localizedTexts.put(Language.valueOf(e.getKey()), e.getValue());
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
    return localizedTexts;
}

At first it's tempting to believe that the final modifier is the cause of the issue, but in fact it's not.

Let's try this simple test:

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.set(d, 1);
    }
}

We get:

java.lang.IllegalAccessException: class Test cannot access a member of class Discount with modifiers "private final"

This is not the error that you are getting (your stacktrace shows an IllegalArgumentException, not an IllegalAccessException).

Let's change the code to add a setAccessible(true):

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(d, 1);
    }
}

This time it works, no exception is thrown.

Now let's replace the 1 with "1":

import java.lang.reflect.Field;

class Discount
{
    private final Integer id = 0;
}

public class Test
{
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException
    {
        Discount d = new Discount();
        Field f = Discount.class.getDeclaredField("id");
        f.setAccessible(true);
        f.set(d, "1");
    }
}

We get:

java.lang.IllegalArgumentException: Can not set final java.lang.Integer field Discount.id to java.lang.String

This time this is exactly the same error as yours. The IllegalArgumentException means that we're trying to assign an incompatible value (here, a String to an Integer).

Your stacktrace says that you're trying to assign a LinkedHashMap to the designation field, which is incompatible indeed (designation being a LocalizedTexts).

To understand where the LinkedHashMap comes from, let's take a look at your JpaJsonConverter. The method responsible for converting a JSON string to a LocalizedTexts object is:

public Object convertToEntityAttribute(String localizedTextsJSON) {
    Object localizedTexts = null;
    try {
        localizedTexts = objectMapper.readValue(localizedTextsJSON, Object.class);
    } catch (IOException ex) {
        //throw new RuntimeException();
    }
    return localizedTexts;
}

You're calling readValue() with a JSON string containing an object, and Object.class as second argument.
In such a case, Jackson returns a LinkedHashMap. It has no way to know that you want a LocalizedTexts.

Here is a possible way to fix the method:

@SuppressWarnings("unchecked")
public Object convertToEntityAttribute(String localizedTextsJSON) {
    LocalizedTexts localizedTexts = new LocalizedTexts();
    try {
        Map<String,String> map = (Map<String,String>)objectMapper.readValue(localizedTextsJSON, Object.class);
        for(Map.Entry<String,String> e : map.entrySet())
            localizedTexts.put(Language.valueOf(e.getKey()), e.getValue());
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
    return localizedTexts;
}
悲欢浪云 2025-02-12 16:31:43

我遵循并复制了您的代码,因此让我们通过了解根本原因来谈论问题。

首先,您需要阅读stacktrace/登录我们读到我们无法设置final final the final of finalararymumentException向我们展示的字段。

Caused by: java.lang.IllegalArgumentException: Can not set final

第二个例外,第一个例外说明了第一个的东西,但更通用。

org.springframework.orm.jpa.JpaSystemException: Could not set field value 

第二,我们必须知道我们在entity上使用的注释和库在这种情况下是折扣,而不是通过添加来添加,而是在通过阅读官方文档来使用。因此,您确保使用什么。

通过使用@value 折扣的每个字段都是私人和最终的,因此指定指定 nes final不会修改我们无法修改我们无法进行的设置任何字段,以确保我发布的内容,您可以阅读以下文档。

@value lombok文档说:

@value@data的不变变体;所有领域都是私人的
默认情况下,最终未生成设置器。班级本身
默认情况下也是最终的,因为不变性不是
可以被迫到子类

通过删除@Value解决问题,这是不必要的。

I followed and reproduced your code, so let's talk about the issue by understanding the root cause.

First, you need to have a reading of stacktrace/logging we read that we can't set final a field which IllegalArumentException shows us.

Caused by: java.lang.IllegalArgumentException: Can not set final

and second exception wrapped by the first one says something seemed to the first one but more generic.

org.springframework.orm.jpa.JpaSystemException: Could not set field value 

Second, we must know what annotations and library we use on our Entity in this case is Discount, not adding by adding, but knowing it before to use by reading the official documentation. so you make sure what you use.

By using @Value each field of Discount is made private and final, so pointing designation as final won't be modified which we can't set any field, to ensure what I am posting you can read the documentation below.

The @Value lombok documentation says:

@Value is the immutable variant of @Data; all fields are made private
and final by default, and setters are not generated. The class itself
is also made final by default, because immutability is not something
that can be forced onto a subclass

The issue was resolved by removing @Value which is not necessary have it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文