JHipster生成一对多关联引发异常的原因排查与修复咨询
问题描述
编写的JDL代码
entity A { name String required } entity B { name String unique required, } relationship OneToMany { B{children} to A{owner} } application { config { applicationType monolith databaseType sql } entities * dto * with mapstruct service * with serviceClass }
遇到的问题
1. MapStruct编译警告
/src/main/java/foo/service/mapper/A.java:13: 警告: 未映射目标属性: "children, removeChildren"。从属性"BDTO owner"映射到"B owner"。发生在'EntityMapper'的'E toEntity(D dto)'方法中。
public interface A extends EntityMapper<ADTO, A> {
^
/src/main/java/foo/service/mapper/AMapper.java:13: 警告: 未映射目标属性: "children, removeChildren"。从属性"BDTO owner"映射到"B owner"。发生在'EntityMapper'的'void partialUpdate(E entity, D dto)'方法中。
public interface AMapper extends EntityMapper<ADTO, A> {
^
/src/main/java/foo/service/mapper/BMapper.java:13: 警告: 未映射目标属性: "children, removeChildren"。发生在'EntityMapper'的'E toEntity(D dto)'方法中。
public interface BMapper extends EntityMapper<BDTO, B> {
^
/src/main/java/foo/service/mapper/BMapper.java:13: 警告: 未映射目标属性: "children, removeChildren"。发生在'EntityMapper'的'void partialUpdate(E entity, D dto)'方法中。
public interface ModelMapper extends EntityMapper<BDTO, B> {
^
4 条警告
2. Hibernate运行时异常
ERROR 30510 --- [ XNIO-1 task-3] foo.service.AService : findAll()方法中出现异常,原因='org.hibernate.exception.SQLGrammarException: could not prepare statement',异常信息='could not prepare statement; SQL [select a0_.id as id1_1_, a0_.name as name2_1_, a0_.owner_id as owner_id4_1_, a0_.value as value3_1_ from a a0_]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement'
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select a0_.id as id1_1_, a0_.name as name2_1_, a0_.owner_id as owner_id4_1_, a0_.value as value3_1_ from a a0_]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)
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.proxy3/jdk.proxy3.$Proxy200.findAll(Unknown Source)
at foo.service.AService.findAll(AService.java:88)
at foo.service.AService$$FastClassBySpringCGLIB$$4afc8e16.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
....
修复方案
一、MapStruct警告修复
警告根源是JDL默认生成的DTO未包含关联集合属性的映射规则。实体B的children是关联集合,MapStruct无法自动识别映射,需手动在JDL中指定:
修改JDL的DTO配置
将原有的dto * with mapstruct替换为更精确的配置,明确包含关联字段:dto A with mapstruct { name, owner } dto B with mapstruct { name, children }或者如果需要保留批量生成,单独为B补充字段:
dto * with mapstruct dto B with mapstruct { children }重新生成代码
运行jhipster import-jdl <你的JDL文件名>.jh,重新生成后MapStruct会自动生成children和removeChildren的映射逻辑,警告即可消除。
二、Hibernate SQL异常修复
从SQL语句select ... a0_.value ...可以看出,Hibernate在查询实体A时尝试访问value字段,但你的JDL中实体A并没有定义这个属性,说明数据库表结构与当前实体类不匹配:
排查原因
大概率是之前生成过带value属性的实体A,后来删除该属性但数据库表未同步更新,导致Hibernate映射出错。修复方式
- 开发环境快速修复:清空数据库,重新运行代码生成,让JHipster重建正确的表结构。
- 生产/测试环境修复:执行SQL删除冗余字段:
ALTER TABLE a DROP COLUMN value; - 标准JHipster流程:使用Liquibase创建数据库迁移脚本,在
src/main/resources/config/liquibase/changelog目录下新增变更日志文件,添加删除字段的配置,启动应用时自动执行迁移。
三、JDL代码优化
实体B的定义末尾多了一个冗余逗号,虽然不影响生成,但建议修正:
entity B { name String unique required // 去掉末尾的逗号 }
内容的提问来源于stack exchange,提问作者juwi

