I'm having an issue where a Validation instance is added to a Collection on a Step instance.
Declaration is as follows:
Step class:
@Entity
@Table
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Step extends AbstractEntity implements ValidatableStep {
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "step_id", nullable = false)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Validation> validations = new HashSet<>();
@Override
public void addValidation(Validation validation) {
// do some stuff
...
// add validation instance to collection
getValidations().add(validation);
}
}
Validation class:
@Entity
@Table
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Validation extends AbstractEntity {
//some properties
}
Both classes are Cacheable with a READ_WRITE strategy applied. The unidirectional Collection of Validations are also cached with same strategy.
One would expect when a read-write transaction that invokes addValidation(new Validation('userName')); commits, the new Validation would be visible in a subsequent read-only transaction. The weird thing is that sometimes it does work and sometimes it doesn't work...
The first transaction always succeeds; we see the new validation being persisted in database and Step's version property (for optimistic locking puposes) getting incremented. But sometimes, the 2nd read transaction contains a Step instance with an empty Validation Collection...
Our Hibernate caching config is as follows:
hibernate.cache.use_second_level_cache = true
hibernate.cache.use_query_cache = true
hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
hibernate.cache.provider_configuration_file_resource_path = classpath:ehcache.xml
net.sf.ehcache.hibernate.cache_lock_timeout = 10000
Any idea what's causing this weird (and random) behavior?
The Hibernate Collection Cache always invalidates existing entries and both the Entity and the Collection caches are sharing the same AbstractReadWriteEhcacheAccessStrategy, so a soft-lock is acquired when updating data.
Because you are using a unidirectional one-to-many association, you will end up with a Validation table and a Step_validation link table too. Whenever you add/remove a Validation you have to hit two tables and that's less efficient.
I suggest you adding the @ManyToOne side in the Validation entity and turn the @OneToMany side into a mapped-by collection:
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "step")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Validation> validations = new HashSet<>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With