StaleObjectStateException after transaction failing with ORA-12899: Value Too Large for Column

Hello, I have next situation.

I do a transaction, doing an update on an entity column and it gives an ORA-12899 (Value Too Large for Column).

In the next transaction (I repeat it), I fix the size error to avoid length error (ORA-12899), it should go fine but now it throws an org.hibernate.StaleObjectStateException.

ERROR ExceptionTranslator:35 - concurrency error :
Object of class [Entityr] with identifier [565]: optimistic locking failed;
nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Entityr#323]

In the entity I use @Cache
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)

And one column @Version
@Column(name = “VERSION”)
@Version

I think it can be related with OptimisticLockType, but I read about problems with the annotation @Version or because of second level cache (I have it enabled with transactinal strategy)
jpaProperties.put(“hibernate.cache.use_second_level_cache”, true);
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)

How do you think I can fix it? I know I should’t allow the first error (ORA-12899), but I don’t want any blocking error.

If you load object A with version 1, change it and then try to flush the changes such a StaleObjectStateException can happen when

  • The row was deleted in the meantime
  • The row was updated to a new version in the meantime

If this doesn’t help you figure out what is wrong, I need to see the code that you are using.

On first transaction (with an ORA error) hibernate should do rollback and restore the previous object version in the cache (it should restore all the object). Second transaction should work because is a new and correct object. It happens in my local computer (DB in my local too), with no other user interactions.

I use transaction manager

<tx:advice id="txDefaultAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="find*" read-only="true" />
		<tx:method name="get*" read-only="true" />
		<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED"
			rollback-for="Exception" />
	</tx:attributes>
</tx:advice>

The service doesn’t need the annotation @Transaction because they are mapped by aop:config

<aop:config>
	<aop:pointcut id="servicesPointcut"	expression="execution(* org.test.rec.*.service..*.*(..)) or 
													execution(* org.test.rec.inte.*.service..*.*(..))" />
	<aop:advisor advice-ref="txDefaultAdvice" pointcut-ref="servicesPointcut" />
</aop:config>

With this configuration:

final Properties jpaProperties = new Properties();
jpaProperties.put(“hibernate.cache.use_second_level_cache”, true);
jpaProperties.put(“hibernate.cache.use_query_cache”, true);
jpaProperties.put(“hibernate.cache.infinispan.cachemanager”, “java:jboss/infinispan/hibernate”);
jpaProperties.put(“hibernate.transaction.jta.platform”, “org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform”); // → It is on a JBoss server
jpaProperties.put(“hibernate.cache.region.factory_class”, “org.jboss.as.jpa.hibernate4.infinispan.InfinispanRegionFactory”);
factory.setPersistenceUnitName(“testPU”);
factory.setPackagesToScan(“org.test.rec”);

Everything works fine if there is not an ORA error at one transaction. I think I have no more Hibernate configuration…

Even if you start a new transaction, as soon as an error happens in a session, the session should not be used any more. You will have to create a new session for the new transaction.

Can you show us your Java code? You’ll likely need to use #merge.

Its like this. It is a transaction inside the services class (as indicated at execution(* org.test.rec..service….*(…)))

public void update(RecognitionHlDto entry) throws BusinessException {
final LocalDateTime now = new LocalDateTime(System.currentTimeMillis());
final RecordDto recordDto = new RecordDto(entry.getRecord());
final Long idRecord = Long.parseLong(entry.getRecord().getIdentifiers().get(0).getValue());
final Record recordBBDD = recordRepository.findOne(idRecord);

recordBBDD.setMotiveText(recordDto.getMotiveText());
recordBBDD.setModificationTime(now);
encounterRepository.save(recordBBDD);

updateData(now, entry, recordBBDD);
}

private void updateData(final LocalDateTime now, RecognitionHlDto entry, Record recordBBDD) {
if ( ! entry.getCompleteSections().isEmpty()) {
updateComplete(now, entry.getComplete(), recordBBDD);
}

if ( ! entry.getSignedSections().isEmpty()) {
updateSigned(now, entry.getSigned(), recordBBDD);
}

if ( ! entry.getDiagnosticReports().isEmpty()) {
updateDiagnostic(now, entry.getDiagnostic(), recordBBDD);
}

if ( ! entry.getQuestionaires().isEmpty()) {
updateQuestionnaire(now, entry.getQuestionaires(), recordBBDD);
}
}