Suppose I have 2 entities, EntityA and EntityB.
EntityB is #OneToOne related to EntityA:
#Entity
public class EntityB {
#OneToOne(fetch = FetchType.LAZY)
private EntityA entA;
// other stuff
}
When I load EntityB from DB, the corresponding EntityA (say entA1) is lazy loaded.
After that I load EntityA list by
List result = entityManager.createQuery("select A from EntityA A")
.setFirstResult(start).setMaxResults(end).getResultList();
The result list contains both previously lazy loaded and proxied EntityA and normal materialized EntityAs such like:
EntityA
EntityA_$$_javassist_nnn <--- entA1 which is loaded lazily earlier
EntityA
...
So my questions:
1) Is this an expected behavior? Where can I find apidoc info about that?
2) Can I entirely load proxied entities only or entirely load eagerly all of them? Not mixed.
Yes, it's expected behavior. Hibernate does everything it can to have one and only one instance of an entity in the session. Since it already has a proxy to EntityA, stored in the session when you loaded EntityB, a subsequent query returning the same EntityA instance effectively return the same instance: the proxy already stored in the session.
You shouldn't care much about the fact that the list contains proxies. Calling any method on the proxy (except getClass()) will return the same thing as calling it on the unproxied entity.
AFAIK, that's what allows having collections of entities behaving correctly with attached objects, although the objects don't even have an equals() method.
Related
Entity{
String code;
String parentCode;
...
#ManyToOne
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
My entity class is like this. what i want to do is using findAll() to get an entity list with each entity get its own direct parent. But spring jpa will get parent's parent until the root , i need to avoid this.
Thank you!
Since a default fetch type for a #ManyToOne relation is an FetchType.EAGER I think you have just add a fetch type as LAZY explicitly:
Entity{
String code;
String parentCode;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
It's not about Spring JPA but JPA itself. When you add a relationship, following defaults apply unless specified otherwise.
#xxToOne - FetchType.EAGER
#xxToMany- FetchType.LAZY
So, in your example you have a #ManyToOne which has a default EAGER fetch and one join query is appended. If your parent has another #xxToOne it adds one more join and so on. It's good to know the boundaries of your entities and decide which type of FetchType is required.
Even if you add like this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
.. if you parent has more relationships you might be getting everything loaded while fetching parent. Thus, all relationships need to be Lazy. It's a design choice based on entities.
But be aware about the ORM's N+1 problem : JPA Hibernate n+1 issue (Lazy & Eager Diff)
What is the good way to force initialization of Lazy Loaded field in each object of collection?
At this moment the only thing that comes to my mind is to use for each loop to iterate trough collection and call getter of that field but it's not very effiecient. Collection can have even 1k objects and in that case every iteration will fire to db.
I can't change the way I fetch objects from DB.
Example of code.
public class TransactionData{
#ManyToOne(fetch = FetchType.LAZY)
private CustomerData customer;
...
}
List<TransactionData> transactions = getTransactions();
You may define Entity Graphs to overrule the default fetch types, as they are defined in the Mapping.
See the example below
#Entity
#NamedEntityGraph(
name = "Person.addresses",
attributeNodes = #NamedAttributeNode("addresses")
)
public class Person {
...
#OneToMany(fetch = FetchType.LAZY) // default fetch type
private List<Address> addresses;
...
}
In the following query the adresses will now be loaded eagerly.
EntityGraph entityGraph = entityManager.getEntityGraph("Person.addresses");
TypedQuery<Person> query = entityManager.createNamedQuery("Person.findAll", Person.class);
query.setHint("javax.persistence.loadgraph", entityGraph);
List<Person> persons = query.getResultList();
In that way you are able to define specific fetch behaviour for each differet use-case.
See also:
http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs001.htm
By the way: afaik do most JPA provider perform eager loading of #XXXtoOne relations, even if you define the mapping as lazy. The JPA spec does allow this behaviour, as lazy loading is always just a hint that the data may or may not be loaded immediately. Eager Loading on other other hand has to be performed immediately.
What you can do is something like this:
//lazily loaded
List<Child> childList = parent.getChild();
// this will get all the child in memory of that particular Parent
Integer childListSize = childList.size();
But if you eager load then all the child will be loaded for each of the parents. This should be your best bet.
I have these classes:
#Entity
public class Invoice implements Serializable {
#Id
#Basic(optional = false)
private Integer number;
private BigDecimal value;
//Getters and setters
}
#Entity
public class InvoiceItem implements Serializable {
#EmbeddedId
protected InvoiceItemPK invoiceItemPk;
#ManyToOne
#JoinColumn(name = "invoice_number", insertable = false, updatable = false)
private Invoice invoice;
//Getters and setters
}
When i run this query:
session.createQuery("select i from InvoiceItem i").list();
It executes one query to select the records from InvoiceItem, and if I have 10000 invoice items, it generates 10000 additional queries to select the Invoice from each InvoiceItem.
I think it would be a lot better if all the records could be fetched in a single sql. Actually, I find it weird why it is not the default behavior.
So, how can I do it?
The problem here is not related to Hibernate but to JPA.
Prior to JPA 1.0, Hibernate 3 used lazy loading for all associations.
However, the JPA 1.0 specification uses FetchType.LAZY only for collection associations:
#OneToMany,
#ManyToMany
#ElementCollection)
The #ManyToOne and #OneToOne associations use FetchType.EAGER by default, and that's very bad from a performance perspective.
The behavior described here is called the [N+1 query issue][5], and it happens because Hibernate needs to make sure that the #ManyToOne association is initialized prior to returning the result to the user.
Now, if you are using direct fetching via entityManager.find, Hibernate can use a LEFT JOIN to initialize the FetchTYpe.EAGER associations.
However, when executing a query that does not explicitly use a JOIN FETCH clause, Hibernate will not use a JOIN to fetch the FetchTYpe.EAGER associations, as it cannot alter the query that you already specified how to be constructed. So, it can only use secondary queries.
The fix is simple. Just use FetchType.LAZY for all associations:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "invoice_number", insertable = false, updatable = false)
private Invoice invoice;
More, you should use the Hypersistence Utils to assert the number of statements executed by JPA and Hibernate.
Try with
session.createQuery("select i from InvoiceItem i join fetch i.invoice inv").list();
It should get all the data in a single SQL query by using joins.
Yes there is setting you need: #BatchSize(size=25). Check it here:
20.1.5. Using batch fetching
small cite:
Using batch fetching, Hibernate can load several uninitialized proxies if one proxy is accessed. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can configure batch fetching: on the class level and the collection level.
Batch fetching for classes/entities is easier to understand. Consider the following example: at runtime you have 25 Cat instances loaded in a Session, and each Cat has a reference to its owner, a Person. The Person class is mapped with a proxy, lazy="true". If you now iterate through all cats and call getOwner() on each, Hibernate will, by default, execute 25 SELECT statements to retrieve the proxied owners. You can tune this behavior by specifying a batch-size in the mapping of Person:
<class name="Person" batch-size="10">...</class>
With this batch-size specified, Hibernate will now execute queries on demand when need to access the uninitialized proxy, as above, but the difference is that instead of querying the exactly proxy entity that being accessed, it will query more Person's owner at once, so, when accessing other person's owner, it may already been initialized by this batch fetch with only a few ( much less than 25) queries will be executed.
So, we can use that annotation on both:
collections/sets
classes/Entities
Check it also here:
#BatchSize but many round trip in #ManyToOne case
In this Method there are Multiple SQLs fired. This first one is fired for retrieving all the records in the Parent table. The remaining are fired for retrieving records for each Parent Record. The first query retrieves M records from database, in this case M Parent records. For each Parent a new query retrieves Child.
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.
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.