I have code like:
#Entity
#Table(name = "A")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class A
{
#OneToOne(cascade={CascadeType.ALL}, fetch=FetchType.EAGER, mappedBy="a")
public B getB() {};
}
#Entity
#Table(name = "B")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class B
{
#OneToOne(cascade={}, fetch=FetchType.LAZY)
#JoinColumn(name="A_ID")
public A getA() {};
}
each time when A is loaded there is query for B. Why is A.getB() not cached after A is loaded and is it possible to cache it?
Workaround that work for me is create additional method with #OneToMany
#OneToMany(cascade={}, fetch=FetchType.EAGER, mappedBy="a")
public Set<B> getBSet() {};
#Transient
public B getB() { return b.iterator().next(); }
I'm not very happy with this solutions, but it works and I can't find other way.
Try putting #Cache annotation on getB() getter as well. My observations are that if you cache the object, it's associations may not be considered cached.
It may be a little more work, but you could try making the fetchType Lazy, and
do the fetching of B explicitly. That way you could check whether the instance of B has already been loaded or not?
On a side note, have you seen this post? I think the problem is similar:
https://forum.hibernate.org/viewtopic.php?p=2378461
I feel that the original answer does not cover entirly why this is happening.
Why OneToOne is not cached ?
It is not cached because class A is not the owner of the relationship and does not contain the #JoinColumn inside its table. Therefore there is no way for class A to tell what is the ID of class B. This is why when trying to retrieve class A it needs to send a query for class B to figure out what the ID of class B is, but when it sends the query the class B is already loaded so there is no need for it to actualy retrieve it from the cache.
When OneToOne will be cached ?
Now if you navigate the opposite way from class B to class A then you will hit the cache straight away :)
Why is #OneToMany(cascade={}, fetch=FetchType.EAGER, mappedBy="a") working ?
In hibernate collections are cached in their dedicated region known as collection cache. Hibernate caches the primary keys of the entities that make up the collection. Not the entities themselves; i.e. there is no Set stored somewhere in the second level cache.
Once the primary key for is retrieved from the collection cache region it falls back to the regular entity cache to retrieve the actual object. Therefore the #OneToMany hack works for you.
Related
I'm using Spring Boot and Hibernate.
Lets assume we have two entities:
#Entity
public class A{
#OneToMany(mappedBy = "objectA",fetch = FetchType.EAGER,cascade = CascadeType.ALL,orphanRemoval=true)
private Set<B> objectSet = new HashSet<>();
}
#Entity
public class B{
#ManyToOne
private A objectA;
}
And we have two transacional methods;
deleteB_X(int idB){
entityManager.remove(entityManager.find(idB,B.class));
}
deleteB_Y(int idB){
B obj=entityManager.find(idB,B.class);
obj.getObjectA().getObjectSet().remove(obj);
}
What I understand (correct me if I'm wrong):
We have orphanRemoval=true so deleteB_Y(int) will work.
By setting mappedBy argument we say that class A is "the owning
site" of relation.
CascadeType is used when we persist/update/merge/remove class A (then it invokes persist/update/merge/remove on child property objectSet). I think we can say that it protects me from situation where I end up with object of B and no object of A class (unless we manually add some B objs).
From what I understand CascadeType should not interfare with orphanRemoval, because CascadeType takes care of things where we do 'some stuff' with A's objects (and then recursively do it to B's objects). And here is something that I don't understand at all.
Why deleteB_x(int) doesn't work and why if we remove CascadeType it starts working? I feel like that deleteB_X(int) is much cleaner solution to removing object B from the DB than deleteB_Y(int), but sadly it won't work since it colides with CascadeType.
EDIT1.
Method deleteB_X(int) just doesn't remove object from DB, if we remove cascade = CascadeType.ALL evertyhing works just fine. Why?
The issue was that my class A was fetched EAGER in class B instance and because of that (I assume) that there was a conflict when I was deleting B's instance alone without taking care of the same B instance in private Set<B> objectSet. Changing EAGER to LAZY or excluding CascadeType.PERSIST from #OneToMany(cascade=...) solved my issue.
I have the following entities:
#Entity
public class B{
#OneToMany
private List<C> cList;
private Long d;
}
In my managed bean, I need to load a specific b (which is perfectly working) in order to edit the contained attributes (cList, d):
#ManagedBean
public class Bean{
private B b;
public void onEvent(Long bId){
b = bManager.load(bId);
}
}
The attributes of B will have to be edited using a JSF-Form. I do not want these changes to be reflected to the database.
The problem is pretty much the same like in this (old) thread.
But none of the suggestions worked in my case (tried em.flush(), em.detach(), session.evict()).
Is there no solution except deep copying?
It is possible to do it, but first would be better to know what hibernate is doing and why you are getting exception. Here is documentation about object states
If you want to access list with objects C or you want to modify some of them, you must fetch it before it gets to your managed bean. By default hibernate is fetching objects lazy and associated objects will be loaded when you access them, but preconditions is to have a transaction and session attached to objects. So in your managed bean objects are detached and list of C cannot be fetch at that time. To solve that problem you must fetch all object that you want to change before they gets to the managed bean. i.e.
#OneToMany
#Fetch(FetchMode.JOIN) // load it with sql join
private List<C> cList;
There many other ways you can achieve same result. So now you can update your B and list of C
entities and then call update function for your B entity.
Hope it helps.
I am using Hibernate 4.3.1.Final
If I have two Entities, let's say A and B. A contains a set of B objects that is annotated as a OneToMany association.
If I set "org.hibernate.envers.global_with_modified_flag" to true and "org.hibernate.envers.modified_flag_suffix" to "Modified", then Envers correctly adds columns for the all of the columns in that table with the specified suffix, but it also expects to find a modified column for each of the associations even though they are owned by the foreign side.
In the below case, Envers expects columns in A for "foo" "fooModified", and "bObjectsModified" when I would think that it should expect columns for "foo" and "fooModified" in A and "aIdModified" in B.
#Entity
#Table("A")
#Audited
class A {
private String foo;
private Set<B> bObjects;
#Column(name = "foo")
public getFoo( return foo; )
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "a")
public Set<B> getBObjects() { return bObjects; }
}
#Entity
#Table("B")
#Audited
class B {
private A a;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "aId")
public getA(){ return a; }
}
Has anyone else seen this? How do I change that behavior other than annotating every one of my #ManyToOne relationships with #Audited(withModifiedFlag=false). I have many thousands of relationships, so even testing that part will be a huge pain.
The alternative is forcing the database to know details about our Java code that it has no business knowing and makes it much more difficult to add bi-directional associations.
For those who may come later, at least as of 4.3.1.Final, the only way to do this is to remove the global configuration flag and add that option to the #Audited annotation on every class so that it is #Audited(withModifiedFlag=true) and then add #Audited(withModifiedFlag=false) to every property (not column!) in that class for which you do not want a modified field to be created.
In the other Hibernate modules, global configuration options can be overridden at the class or attribute level. For Envers, global configuration options can never be overridden.
Also note that the modified field names are based on the attribute name in the Java class and not the value in the #Column annotation that the rest of Hibernate ORM uses.
I have a weird problem with two entities with one-to-many relation in JPA. I am using Glassfish 3.1.2.2 with EclipseLink 2.3.2. This is the first entity:
#NamedQueries({
#NamedQuery(name="SampleQueryGroup.findAll", query="SELECT g FROM SampleQueryGroup g")
})
#Entity
public class SampleQueryGroup implements Serializable {
// Simple properties, including id (primary key)
#OneToMany(
mappedBy = "group",
fetch = FetchType.EAGER,
cascade = {CascadeType.REMOVE, CascadeType.MERGE}
)
private List<SampleQuery> sampleQueries;
// Gettes/setters, hashcode/equals
}
And this is the second one:
#Entity
public class SampleQuery implements Serializable {
// Simple properties, including id (primary key)
#ManyToOne(cascade = {CascadeType.PERSIST})
private SampleQueryGroup group;
// Gettes/setters, hashcode/equals
}
I have a stateless session bean which uses an injected EntityManager to run SampleQueryGroup.findAll named query. I also have a CDI managed bean which calls the SSB method and iterates through SampleQueryGroup.getSampleQueries() for each SampleQueryGroup returned by the method. I didn't paste the code as it is pretty straightforward and somehow standard for any Java EE application.
The problem is the eager fetch does not work and getSampleQueries() returns an empty list. However, when I change the fetch type back to FetchType.LAZY, everything works and I get the list correctly populated. I don't understand why this happens. Does it have anything to do with internal caching mechanisms?
My guess is that when you add a new SampleQuery you are not adding it to the SampleQueryGroup sampleQueries, so when you access it, it is not their. When it is LAZY you do not trigger it until you have inserted the SampleQuery, so then it is there.
You need to maintain both sides of your relationships. (you could also disable caching, or refesh the object, but your code would still be broken).
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
Bellow are my entities:
public class EntityA {
//...
#OneToMany(mappedBy="entityA")
private Set entitieBs;
}
public class EntityB {
//...
#ManyToOne(cascade=CascadeType.PERSIST)
private EntityA entityA;
}
with accessors methods(getters and setters).
I intend that every time when I save a new EntityB object in the database (with an EntityA object set up as the "parent"), if I call EntityA.getEntityBs() on the parent of new EntityB, to have it added in the result Set. But if I do it as in my example it doesn't work.
Does anybody know where I am wrong?
Thanks!
Here is my java code how I persist the entity:
//...some code
EntityB eb = new EntityB();
eb.setEntityA(entityA);
entityManager.persist(entityB);
I want to make clear that I don't add entityB to entityA's set of entityBs.
This question is asked every two days.
JPA doesn't maintain the coherence of the object graph for you. It's your responsibility to maintain both sides of a bidirectional association. Everything will be as you expect if you commit the transaction, close the session, and reload the entities, because you have initialized the owning side of the association. But if you modify one side of the association in memory, JPA won't modify the other side for you.