I am using EhCache together with Hibernate, and I am kind of stuck with following thing:
If my entity has fields like:
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#OneToOne(cascade = CascadeType.ALL) #PrimaryKeyJoinColumn VkAuth vka
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#OneToOne(cascade = CascadeType.ALL) #PrimaryKeyJoinColumn OkAuth oka
vka is present but oka is null, vka gets cached but query for oka is being sent every time
I understand that oka is simply not being cached because there is nothing to cache, but what could be the possible workaround for this scenario?
Related
I am implementing soft delete with Hibernate. Each entity that supports soft-deleting has an attribute for it.
#Column(name = "DELETED")
private boolean deleted;
I have created #FilterDef in package-info.java for package with domain objects.
#FilterDef(name = "deletedFilter",
parameters = #ParamDef(name = "includeDeleted", type = Boolean.class),
defaultCondition = ":includeDeleted = true OR DELETED = false"
)
applied it to all DeleteAware entities
#Filter(name = "deletedFilter")
public class CustomerGroup
and enabled in when using in queries
Session session = em.unwrap(Session.class);
session.enableFilter("deletedFilter")
.setParameter("includeDeleted", fp.isDeleted());
Filter is applied and works correctly for primary entity (for example when I query customers I can see that additional where condition is always applied as needed).
Problem is with filter of association. Let's say Customer entity has collection of CustomerGroup.
#ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
#JoinTable(name = "CUSTOMER_CUSTOMER_GROUP",
joinColumns = #JoinColumn(name = "CUSTOMER_ID"),
inverseJoinColumns = #JoinColumn(name = "CUSTOMER_GROUP_ID"))
private Set<CustomerGroup> groups;
However when I query for Customer, groups collection contains deleted entities. I have turned on sql logging and I can see that condition is not applied for lazy query. However if I change
#ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
to
#ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
it works.
Both entities are annotated with #Filter. I have also tried applying #Filter annotation to collection itself success without. For initial testing I have also ensured that filters are not disabled and includeDeleted parameter is always false.
#Where annotation on entities works like a charm, but cannot be disabled (99% percent of queries we want to filter out deleted objects but there is that pesky 1% where we need deleted ones).
We are using Hibernate 6.1.13 provided in WildFly 27 application server. Looks like filters are not applied when relation is lazy loaded. Am I missing something?
JPA entity Batch has OneToMany relation to Event:
#OneToMany(mappedBy = "owningBatch")
private Set<Event> containingEvents;
and Event has ManyToOne relation to Batch:
#ManyToOne
#JoinColumn(name = "BATCH_ID")
private Batch owningBatch;
after creating new instance and persisting it the containingEvents are still NULL.
But when I use empty Set:
#OneToMany(mappedBy = "owningBatch")
private Set<Event> containingEvents = Sets.newHashSet();
then after persisting the containingEvents are replaced with Hibernate's PersistentSet.
I would expect this replacement to happen even in first case. Why is it not happening?
DAOs are implemented by Spring Data JPA.
Spring Boot 2.0.4.RELEASE
Spring Data JPA 2.0.4.RELEASE
Hibernate 5.2.17.Final
Hibernate JPA 2.1 1.0.2.Final
You need add CascadeType in your #OneToMany annotation, for example:
#OneToMany(mappedBy = "owningBatch", cascade = CascadeType.ALL)
private Set<Event> containingEvents;
And you Event class must contain:
#ManyToOne
#JoinColumn(name = "batch_id", referencedColumnName = "id")
private Batch owningBatch;
We are using Spring Data repositories with Hibernate 5.x
We have a entity graph with a deep hierarchy.
The mapping looks like this:
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
#Entity
public class Foo {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foobar_id")
private FooBar fooBar;
#OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Bar> chassis = new HashSet<>(0);
...
}
#Entity
public class Bar {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "foo_id")
private FooBar foo;
...
}
As you can see the FooBar entity has a set of Foo entities. Each Foo entity contains more Bar entities and so on.
We use the Fetchgraph feature to load the FooBar entity with the relations we need during runtime to avoid n+1 query issue when fetching lazy associations.
After the service call to load the entity graph the transaction has ended and the entity is detached.
When calling save on the FooBar entity at a later time, this causes multiple select statements. Each fetching one of the child entities.
I know that this comes from the entitymanager merge() call which fetches the object graph from the db before copying state changes from the detached objects.
I have two questions:
Why is hibernate not able to join these statements to one big select like what happens when using the fetchgraph?
When i remove all cascade options from the relations it still causes multiple selects but only attributes of the top, FooBar entity, will be updated. Why is hibernate still fetching all loaded child entites during merge even with no cascade merge?
Thanks
You can use session.update instead of merge to overcome this issue.
Session session = entityManager.unwrap(Session.class);
for (Post post: posts) {
session.update(post);
}
I have similar issue with your case, and the reason is the setting of cascading CascadeType.ALL on the #OneToMany association. Updating and merging the parent entity cause a lot of select on the child association.
#Entity
public class FooBar {
#OneToMany(mappedBy = "fooBar", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
...
}
I fix my case by reducing the scope of cascading, only PERSIST and REMOVE is sufficient
#OneToMany(mappedBy = "fooBar", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true)
private Set<Foo> chassis = new HashSet<>(0);
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<>();
I use ehcache and hibernate 3.6.7 Final. This a pseudo code sample that reveals problem with caching.
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class A{
long id;
#OneToMany(mappedBy = "aId", targetEntity = B.class, fetch = FetchType.LAZY)
#Fetch(value = FetchMode.JOIN)
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
protected Set<B> fieldB;
}
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class B {
long id;
long bId;
}
1) First time when I load entity A from hibernate it does not read fieldB. And this is ok - cause FetchType.LAZY is set.
2) Second time when I load entity A I see sql queries retrieving entity A JOIN entity B.
3)If remove #Fetch(value = FetchMode.JOIN) point 2 will not be performed.
So the question is this bug or feature? And how can I avoid such latent things.
You have two conflicting fetches, you definitely do not want to specify the fetch on the column and the #Fetch annotation as it will provide unpredicatable behavior.