Grails 3.3.x中mapWith与hasMany关联报错,3.2.11运行正常
这个问题我在升级Grails版本的时候遇到过,是3.3.x版本对GORM关联逻辑的调整导致的,我来给你拆解一下问题和解决办法:
你在Grails 3.2.11里的代码逻辑是这样的:
首先定义了一个非持久化域类DomainA,通过mapWith="none"让它不生成数据库表:
class DomainA { String someString static mapWith = "none" }
然后在持久化域类DomainB里用hasMany关联这个非持久化类:
class DomainB { String anotherString static hasMany = [domainAs: DomainA] }
这段代码在3.2.11里运行没问题,但升级到Grails 3.3.x后启动就抛出了依赖错误:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'methodValidationPostProces...
Grails 3.3.x开始,GORM(Grails对象关系映射)对关联的校验和初始化逻辑做了严格升级。当持久化域类用hasMany关联一个非持久化类时,GORM会默认尝试为这个关联创建对应的数据库映射(比如中间表),但mapWith="none"的类没有对应的数据库表,这就导致Spring容器在初始化Bean时找不到对应的依赖,抛出异常。
根据你的业务需求,有几个可行的解决方向:
方案1:改用普通集合属性(推荐)
如果DomainA和DomainB只是业务逻辑上的集合关系,不需要数据库层面的关联,直接把hasMany替换成普通的集合属性就行:
class DomainB { String anotherString // 直接初始化一个空List,不用hasMany语法 List<DomainA> domainAs = [] }
这样GORM不会把这个集合当成数据库关联处理,完全避开了ORM层面的校验,启动就不会报错了。
方案2:将DomainA改为持久化类(如果业务允许)
如果业务上DomainA其实需要持久化,只是之前误设置了mapWith="none",那直接移除这个配置,让它成为普通的GORM域类:
class DomainA { String someString // 删掉static mapWith = "none" }
这样DomainB的hasMany关联就能被GORM正常处理,自动生成对应的数据库中间表,问题也就解决了。
方案3:标记关联为瞬态属性(不推荐,有副作用)
如果必须保留hasMany的语法,可以把关联属性标记为transient,让GORM忽略它的数据库映射:
class DomainB { String anotherString static hasMany = [domainAs: DomainA] // 把关联属性加入瞬态列表,GORM不会处理它的数据库映射 static transients = ['domainAs'] }
不过这个方案要注意:标记为transient后,GORM的一些自动功能(比如级联保存、查询关联数据)会失效,只适合特殊场景下临时用。
内容的提问来源于stack exchange,提问作者MKB

