Skip to content

Instantly share code, notes, and snippets.

@nobeans
Last active August 29, 2015 14:20
Show Gist options
  • Save nobeans/fbe4d3684ed44e0aab0e to your computer and use it in GitHub Desktop.
Save nobeans/fbe4d3684ed44e0aab0e to your computer and use it in GitHub Desktop.
Grails3で双方向1対1のドメインクラスの保存がrun-appしたときだけNPEになる事象
{org.grails.orm.hibernate.GrailsHibernateTemplate$7@17518}
OK
2
org.grails.orm.hibernate.AbstractHibernateGormInstanceApi$_performSave_closure3@27ba4c03
3
org.grails.orm.hibernate.validation.UniqueConstraint$2@40259e2
OK
----------------------------------------------------------
EntityIdentityInsertAction#preInsert()
size = 3
{org.hibernate.cfg.beanvalidation.BeanValidationEventListener@12956}
{org.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor@12966}
→拒否権発動!
{org.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor$NullabilityCheckerPreInsertEventListener@12967}
{org.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor@12966}
org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@7048535f: startup date [Tue Apr 28 14:14:19 JST 2015]; root of context hierarchy
size = 1
"{org.grails.orm.hibernate.SessionFactoryProxy@13079}" -> "{org.grails.orm.hibernate.HibernateDatastore@13080}"
----------------------------------------------------------
preInsertでの拒否権の発動が原因かと思ったが、それは二次災害であり、本来はpreInsert自体が呼ばれてはいけないことが分かった。
それ以前のvalidateでNGになるので、preInsertが呼ばれる前に終わるのがただしい。
Java8では、validateがなぜかスルーされてしまい、preInsertまでたどり着いてしまっている。
----------------------------------------------------------
起動時にAbstractMappingContext#addEntityValidatorで、各ドメインクラスのバリデータ情報を登録するが、ここでChannel, IrcbotState, Summaryがのvalidatorがnullで、結局登録されていないのが問題のようだ。
Java7だと登録される。Java8だと登録されない。
起動時にaddEntityValidatorは同一ドメインクラスに対して、2〜3回呼ばれている。Java7の場合も1回目の呼び出しは各ドメインクラスに対してすべてvalidatorはnullだが、2度目できちんと非null値が登録されている。Java8の場合は、2度目でChannel, Summary, IrcbotStateだけnull、それ以外は非nullになっている。
未確認だったが、IrcbotStateもだめっぽい気がする。→なぜか問題ない...
----------------------------------------------------------
実際にaddEntityValidatorを呼んでるところ
Java8
GrailsDomainClassPersistentEntity
一通りドメインクラスを回るもvalidatorはすべてnull
AbstractHibernateGormEnhancer
Channel, IrcbotState, Summary
null
Irclog, Person, Role
HibernateDomainClassValidator
Java7
GrailsDomainClassPersistentEntity
一通りドメインクラスを回るもvalidatorはすべてnull
AbstractHibernateGormEnhancer
Channel, IrcbotState, Summary, Irclog, Person, Role
HibernateDomainClassValidator
----------------------------------------------------------
ドメインクラスのgetValidator()でnullになってる
Channel
DefaultGrailsDomainClass#getValidator()
これはsetValidator()で設定した値を返してるだけ
DefaultGrailsDomainClass#setValidator()
Java7
↓から呼ばれてる
GrailsDomainClassValidator#setDomainClass()
↓sun.reflect.NativeMethodAcessImplを経由して呼ばれてる。怪しい?
Java8
setValidator自体はすべて呼ばれている
つまりvalidatorフィールドはnullじゃないはずなのに...
Role, Irclog, PersonのsetValidator呼び出しと、Channel, Summary, IrcbotStateの呼び出しに少し時間のギャップがある
怪しい?
AbstractHibernateGormEnhancerを初期化した後に見つけてる?間に合ってない?
それっぽい。確かに間に合ってない。1回目にnullになった後、2回目でvalidator実装を見つける前に、AbstractHibernateGormEnhancerでdomainClass.validatorを参照
----------------------------------------------------------
ビーンの初期化順序が怪しいことが分かってきたのでもう一度確認してみる
Java8
ビーンの初期化: grailsDomainClassMappingContext
GrailsDomainClassPersistentEntity
一通りドメインクラスを回るもvalidatorはすべてnull
ビーンの初期化: irclog.IrclogValidator
ビーンの初期化: irclog.PersonValidator
ビーンの初期化: irclog.RoleValidator
ビーンの初期化: org.grails.gorm.hibernate.internal.POST_INIT_BEAN-DEFAULT
AbstractHibernateGormEnhancer
Channel, IrcbotState, Summary
null
Irclog, Person, Role
HibernateDomainClassValidator
ビーンの初期化: irclog.IrcbotStateValidator
ビーンの初期化: irclog.SummaryValidator
ビーンの初期化: irclog.ChannelValidator
Java7
ビーンの初期化: grailsDomainClassMappingContext
GrailsDomainClassPersistentEntity
一通りドメインクラスを回るもvalidatorはすべてnull
ビーンの初期化: irclog.ChannelValidator
ビーンの初期化: irclog.SummaryValidator
ビーンの初期化: irclog.RoleValidator
ビーンの初期化: irclog.IrcbotStateValidator
ビーンの初期化: org.grails.gorm.hibernate.internal.POST_INIT_BEAN-DEFAULT
AbstractHibernateGormEnhancer
Channel, IrcbotState, Summary, Role
HibernateDomainClassValidator
Irclog, Person
null
ビーンの初期化: irclog.PersonValidator
ビーンの初期化: irclog.IrclogValidator
えっ
これ、逆にJava7だとPersonやIrclogのsaveでぬるぽになるっていうこと?
やってみたら...ぬるぽ!
つまり、Java7と8で出方は違うけど、両方ともアウトなバグがある、ということ?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment