java - Usage of @Transactional with Spring Data custom methods -
i using spring data (via spring boot 1.3.3). repositories have custom method primary key. example:
@transactional(readonly=true) @repository public interface userrepository extends crudrepository<user, userid>, userrepositorycustom { user findbyusername(string username); } public interface userrepositorycustom { userid nextid(); } public class userrepositoryimpl implements userrepositorycustom { public userid nextid() { return new userid( uuid.randomuuid() ); } }
is use of @transactional
correct here? or need add @transactional
userrepositoryimpl
(possibly readonly set or not)?
the reason asking because unexplainable objectoptimisticlockingfailureexception
org.springframework.orm.objectoptimisticlockingfailureexception: object of class [com.company.project.domain.game] identifier [gameid{id=7968c30b-838f-424c-bfef-838de7028def}]: optimistic locking failed; nested exception org.hibernate.staleobjectstateexception: row updated or deleted transaction (or unsaved-value mapping incorrect) : [com.company.project.domain.game#gameid{id=7968c30b-838f-424c-bfef-838de7028def}]
this happens during jmeter testing. altough methods called not change game
entity in way.
i have added game
entity debugging:
@preupdate public void preupdate() { system.out.println("game updated!! version = " + version); thread.dumpstack(); }
this gives few times stack trace similar this:
java.lang.exception: stack trace @ java.lang.thread.dumpstack(thread.java:1329) @ com.company.project.domain.game.preupdate(game.java:85) @ sun.reflect.nativemethodaccessorimpl.invoke0(native method) @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) @ java.lang.reflect.method.invoke(method.java:497) @ org.hibernate.jpa.event.internal.jpa.entitycallback.performcallback(entitycallback.java:47) @ org.hibernate.jpa.event.internal.jpa.callbackregistryimpl.callback(callbackregistryimpl.java:112) @ org.hibernate.jpa.event.internal.jpa.callbackregistryimpl.preupdate(callbackregistryimpl.java:76) @ org.hibernate.jpa.event.internal.core.jpaflushentityeventlistener.invokeinterceptor(jpaflushentityeventlistener.java:68) @ org.hibernate.event.internal.defaultflushentityeventlistener.handleinterception(defaultflushentityeventlistener.java:342) @ org.hibernate.event.internal.defaultflushentityeventlistener.scheduleupdate(defaultflushentityeventlistener.java:293) @ org.hibernate.event.internal.defaultflushentityeventlistener.onflushentity(defaultflushentityeventlistener.java:160) @ org.hibernate.event.internal.abstractflushingeventlistener.flushentities(abstractflushingeventlistener.java:231) @ org.hibernate.event.internal.abstractflushingeventlistener.flusheverythingtoexecutions(abstractflushingeventlistener.java:102) @ org.hibernate.event.internal.defaultautoflusheventlistener.onautoflush(defaultautoflusheventlistener.java:61) @ org.hibernate.internal.sessionimpl.autoflushifrequired(sessionimpl.java:1227) @ org.hibernate.internal.sessionimpl.list(sessionimpl.java:1293) @ org.hibernate.internal.queryimpl.list(queryimpl.java:103) @ org.hibernate.jpa.internal.queryimpl.list(queryimpl.java:573) @ org.hibernate.jpa.internal.queryimpl.getsingleresult(queryimpl.java:495) @ org.hibernate.jpa.criteria.compile.criteriaquerytypequeryadapter.getsingleresult(criteriaquerytypequeryadapter.java:71) @ org.springframework.data.jpa.repository.query.jpaqueryexecution$singleentityexecution.doexecute(jpaqueryexecution.java:206) @ org.springframework.data.jpa.repository.query.jpaqueryexecution.execute(jpaqueryexecution.java:78) @ org.springframework.data.jpa.repository.query.abstractjpaquery.doexecute(abstractjpaquery.java:100) @ org.springframework.data.jpa.repository.query.abstractjpaquery.execute(abstractjpaquery.java:91) @ org.springframework.data.repository.core.support.repositoryfactorysupport$queryexecutormethodinterceptor.doinvoke(repositoryfactorysupport.java:462) @ org.springframework.data.repository.core.support.repositoryfactorysupport$queryexecutormethodinterceptor.invoke(repositoryfactorysupport.java:440) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.data.projection.defaultmethodinvokingmethodinterceptor.invoke(defaultmethodinvokingmethodinterceptor.java:61) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99) @ org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281) @ org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.dao.support.persistenceexceptiontranslationinterceptor.invoke(persistenceexceptiontranslationinterceptor.java:136) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.data.jpa.repository.support.crudmethodmetadatapostprocessor$crudmethodmetadatapopulatingmethodinterceptor.invoke(crudmethodmetadatapostprocessor.java:131) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.aop.interceptor.exposeinvocationinterceptor.invoke(exposeinvocationinterceptor.java:92) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208) @ com.sun.proxy.$proxy141.findbyusername(unknown source) @ com.company.project.service.userserviceimpl.findbyusername(userserviceimpl.java:117) @ com.company.project.service.userserviceimpl.subtractcredits(userserviceimpl.java:143) @ sun.reflect.nativemethodaccessorimpl.invoke0(native method) @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) @ java.lang.reflect.method.invoke(method.java:497) @ org.springframework.aop.support.aoputils.invokejoinpointusingreflection(aoputils.java:302) @ org.springframework.aop.framework.reflectivemethodinvocation.invokejoinpoint(reflectivemethodinvocation.java:190) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:157) @ org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99) @ org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281) @ org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208) @ com.sun.proxy.$proxy154.subtractcredits(unknown source) @ com.company.project.service.gameserviceimpl.subtractcreditsforplacedshotsandsave(gameserviceimpl.java:703) @ com.company.project.service.gameserviceimpl.placeshotsongamewhengameisopen(gameserviceimpl.java:641) @ com.company.project.service.gameserviceimpl.placeshotsongame(gameserviceimpl.java:629) @ com.company.project.service.gameserviceimpl.placeshots(gameserviceimpl.java:281) @ sun.reflect.nativemethodaccessorimpl.invoke0(native method) @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) @ java.lang.reflect.method.invoke(method.java:497) @ org.springframework.aop.support.aoputils.invokejoinpointusingreflection(aoputils.java:302) @ org.springframework.aop.framework.reflectivemethodinvocation.invokejoinpoint(reflectivemethodinvocation.java:190) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:157) @ org.springframework.transaction.interceptor.transactioninterceptor$1.proceedwithinvocation(transactioninterceptor.java:99) @ org.springframework.transaction.interceptor.transactionaspectsupport.invokewithintransaction(transactionaspectsupport.java:281) @ org.springframework.transaction.interceptor.transactioninterceptor.invoke(transactioninterceptor.java:96) @ org.springframework.aop.framework.reflectivemethodinvocation.proceed(reflectivemethodinvocation.java:179) @ org.springframework.aop.framework.jdkdynamicaopproxy.invoke(jdkdynamicaopproxy.java:208) @ com.sun.proxy.$proxy164.placeshots(unknown source) @ com.company.project.controller.front.frontgamecontroller.placeshots(frontgamecontroller.java:180)
looking @ stuff relevant app, see this:
java.lang.exception: stack trace @ java.lang.thread.dumpstack(thread.java:1329) @ com.company.project.domain.game.preupdate(game.java:85) @ com.company.project.service.userserviceimpl.findbyusername(userserviceimpl.java:117) @ com.company.project.service.userserviceimpl.subtractcredits(userserviceimpl.java:143) @ com.company.project.service.gameserviceimpl.subtractcreditsforplacedshotsandsave(gameserviceimpl.java:703) @ com.company.project.service.gameserviceimpl.placeshotsongamewhengameisopen(gameserviceimpl.java:641) @ com.company.project.service.gameserviceimpl.placeshotsongame(gameserviceimpl.java:629) @ com.company.project.service.gameserviceimpl.placeshots(gameserviceimpl.java:281) @ com.company.project.controller.front.frontgamecontroller.placeshots(frontgamecontroller.java:180)
so somehow, findbyusername
seems trigger update unrelated entity game
?
fyi: gameserviceimpl#placeshots
has @transactional
annotation. tried adding such annotation on controller method, did not change anything.
the problem not in use of @transactional
.
i using custom hibernate usertype stores object json using jackson library. game
object has field uses usertype. class of field did not implement equals()
. result, hibernate assumed object changed , issued save on game
object.
after implementing equals()
, problem went away.
Comments
Post a Comment