代码之家  ›  专栏  ›  技术社区  ›  hamdi

以空值传递给验证的嵌套关系实体

  •  0
  • hamdi  · 技术社区  · 1 年前

    我有一个单向关系 Country Governorate 第一个是所有者,下面是它们的实现:

    Country.java

    @RequiredArgsConstructor
    @NoArgsConstructor
    @Getter
    @EqualsAndHashCode
    
    @Entity
    public class Country {
    
        
        @Id
        @NotBlank
        @Size(max = 3)
        @NonNull
        @Setter
        private String isoCode;
        
        @Column(unique = true, nullable = false)
        @NotBlank
        @Size(max = 50)
        @NonNull
        @Setter
        private String name;
    
        @OneToMany(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
        @EqualsAndHashCode.Exclude
        private List<Governorate> governorates = new ArrayList<>();
    
    }
    

    Governorate.java

    @NoArgsConstructor
    @RequiredArgsConstructor
    @Getter
    @EqualsAndHashCode
    
    @Entity
    public class Governorate {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long id;
    
        @Column(nullable = false)
        @NotBlank
        @Size(max = 50)
        @Setter
        @NonNull
        private String name;
    }
    

    我试图在启动时填充数据库,所以我实现了一个 CommandLineRunner 从a阅读的类 json 文件填充为 countries 以及它们各自的 governorates 并映射到以下列表 国家 像这样:

    CountriesInitializer.java

    @Component
    public class CountriesInitilizer extends Initializer<Country>{
    
        private static final Logger logger = LoggerFactory.getLogger(CountriesInitilizer.class);
        
        private static final String JSON_FILE = "com/evalia/backend/countries.json";
        
        @Autowired
        CountryRepository repository;
    
        @Override
        void preLoad(Class<Country> clazz, String filePath) {
            try (InputStream stream = ResourceUtils.loadResource(filePath)) {
                ObjectMapper mapper = new ObjectMapper();
                TypeReference<List<Country>> recordTypes = new TypeReference<List<Country>>() {};
                List<Country> countries = mapper.readValue(stream, recordTypes);
                for(Country c: countries) {
                    repository.save(c);
                }
            } catch (IOException e) {
                InitializationException ex = InitializationException.build(clazz.getName(), e);
                logger.error("Could not pre-load entity!", ex);
                throw ex;
            }
        }
        
        @Override
        public void run(String... args) throws Exception {
            
            if (!InitializersUtils.isLoaded(repository)) {
                preLoad(Country.class, JSON_FILE);
            }
        }
    }
    

    这里的问题是,每当我启动应用程序,这个初始化器开始保存它抛出的数据时 ConstraintsViolationException 具有以下堆栈跟踪:

    java.lang.IllegalStateException: Failed to execute CommandLineRunner
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:771) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at com.evalia.backend.EvaliaApplication.main(EvaliaApplication.java:10) ~[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) ~[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:566) ~[na:na]
        at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
    Caused by: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:571) ~[spring-orm-5.3.29.jar:5.3.29]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.29.jar:5.3.29]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174) ~[spring-data-jpa-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.29.jar:5.3.29]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.29.jar:5.3.29]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:241) ~[spring-aop-5.3.29.jar:5.3.29]
        at com.sun.proxy.$Proxy143.save(Unknown Source) ~[na:na]
        at com.evalia.backend.initializers.CountriesInitilizer.preLoad(CountriesInitilizer.java:41) ~[classes/:na]
        at com.evalia.backend.initializers.CountriesInitilizer.run(CountriesInitilizer.java:55) ~[classes/:na]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.7.15-SNAPSHOT.jar:2.7.15-SNAPSHOT]
        ... 10 common frames omitted
    Caused by: javax.persistence.RollbackException: Error while committing the transaction
        at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:81) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562) ~[spring-orm-5.3.29.jar:5.3.29]
        ... 27 common frames omitted
    Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [com.evalia.backend.models.Governorate] during persist time for groups [javax.validation.groups.Default, ]
    List of constraint violations:[
        ConstraintViolationImpl{interpolatedMessage='must not be blank', propertyPath=name, rootBeanClass=class com.evalia.backend.models.Governorate, messageTemplate='must not be blank'}
    ]
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.action.internal.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:188) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:78) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:329) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:286) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:122) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:801) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.spi.CascadingActions$8.cascade(CascadingActions.java:341) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:510) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:434) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:543) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:474) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:437) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:159) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:149) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:82) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1407) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:489) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3303) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2438) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:449) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101) ~[hibernate-core-5.6.15.Final.jar:5.6.15.Final]
        ... 28 common frames omitted
    

    有什么想法吗?

    环境 : Java 11 , 弹簧靴: 2.7.15-SNAPSHOT

    我试着在 BeanValidationEventListener 如堆栈中所述 Exception 被抛出后,我发现了一些有趣的东西,第一个被传递进行验证的实体是 国家 实例附带了它的值,但当它是 省份 轮到时,它们将被传递给验证,其值设置为 null ,这是bug还是我做错了什么?

    非常感谢。

    0 回复  |  直到 1 年前