Tonny Tc
Tonny Tc

Reputation: 930

Spring Data cannot persist OptaPlanner's Score object with *ScoreHibernateType

I want to use Spring-boot-data to persist OptaPlanner's domain object in DB, and according to the guide, I've done the hibernate mapping task and everything looks fine except for Score object.

The score object can be persisted in DB as BLOB column if without any special configuration, but if based on the guide (17.2.1.1. JPA and Hibernate: Persisting a Score), it always throws an exception as follows:

java.lang.AbstractMethodError: org.optaplanner.persistence.jpa.impl.score.buildin.hardmediumsoft.HardMediumSoftScoreHibernateType.replace(Ljava/lang/Object;Ljava/lang/Object;Lorg/hibernate/engine/spi/SharedSessionContractImplementor;Ljava/lang/Object;)Ljava/lang/Object;
at org.hibernate.type.CompositeCustomType.replace(CompositeCustomType.java:178)
at org.hibernate.type.AbstractType.replace(AbstractType.java:144)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:194)
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:431)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:233)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:301)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:170)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:884)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:870)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)
at com.sun.proxy.$Proxy86.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:493)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:377)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:641)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:590)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy93.save(Unknown Source)
at cc.tonny.optaplanner.exercise.springbootcloudbalance.CommandLineAppStartupRunner.run(CommandLineAppStartupRunner.java:64)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:818)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:802)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:341)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1277)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1265)
at cc.tonny.optaplanner.exercise.springbootcloudbalance.SpringbootCloudbalanceApplication.main(SpringbootCloudbalanceApplication.java:10)

Please check the code at https://github.com/tonny1983/springboot-cloudbalance

Upvotes: 0

Views: 117

Answers (2)

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27312

Use OptaPlanner 7.18.0.Final or later, that supports Hibernate 5.3+

Upvotes: 0

Tonny Tc
Tonny Tc

Reputation: 930

Finally, I got it works in spring-boot-jpa v2.x with hibernate v5.2.x, and I'll state the patch as follows:

  1. Create a new class which extends org.optaplanner.persistence.jpa.impl.score.AbstractScoreHibernateType and implement all methods defined in the org.hibernate.usertype.CompositeUserType (you can copy all the code from AbstractScoreHibernateType but just change the parameter's type from org.hibernate.engine.spi.SessionImplementor to org.hibernate.engine.spi.SharedSessionContractImplementor)

  2. Create a new *SoftScoreHibernateType class which extends the one created in step 1, and add its construction method just like origin *SoftScoreHibernateType class

  3. In my case, getPropertyValue method throws java.lang.ArrayIndexOutOfBoundsException because levelNumbers array only holds three items -- hard score, medium score and soft score, but the propertyIndex is from 1 to 3. So, I overrides the method just change the last line to return levelNumbers[propertyIndex - 1];

    That's all for my case.

Upvotes: 1

Related Questions