Lets say I retrieve an entity object that looks like this:
#Entity
public class Mother {
...
#OneToMany(mappedBy = "mother",
targetEntity = Child.class,
fetch = FetchType.LAZY)
public List<Child> getChildren() {
return children;
}
}
When retrieving the above object, I commit the transaction (and close the session associated with the object). Later on in the code, there is a need to retrieve the children. If I wanted to keep the fetch type as LAZY, is there a way to use the Mother object and still be able to call getChildren() to retrieve the children? Or would I have to just bite the bullet and query the children via the key of the Mother?
If I wanted to keep the fetch type as LAZY, is there a way to use the Mother object and still be able to call getChildren() to retrieve the children?
Not if the EntityManager has been closed.
Or would I have to just bite the bullet and query the children via the key of the Mother?
You could retrieve the children when getting the mother using a FETCH JOIN:
SELECT m
FROM Mother m LEFT JOIN FETCH m.children
WHERE m.id = :id
Other options include:
using the Open Entity Manager in View pattern (if you are using a webapp)
using an extended persistence context.
Related
Given the following domain model, I want to load all Answers including their Values and their respective sub-children and put it in an AnswerDTO to then convert to JSON. I have a working solution but it suffers from the N+1 problem that I want to get rid of by using an ad-hoc #EntityGraph. All associations are configured LAZY.
#Query("SELECT a FROM Answer a")
#EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Using an ad-hoc #EntityGraph on the Repository method I can ensure that the values are pre-fetched to prevent N+1 on the Answer->Value association. While my result is fine there is another N+1 problem, because of lazy loading the selected association of the MCValues.
Using this
#EntityGraph(attributePaths = {"value.selected"})
fails, because the selected field is of course only part of some of the Value entities:
Unable to locate Attribute with the the given name [selected] on this ManagedType [x.model.Value];
How can I tell JPA only try fetching the selected association in case the value is a MCValue? I need something like optionalAttributePaths.
You can only use an EntityGraph if the association attribute is part of the superclass and by that also part of all subclasses. Otherwise, the EntityGraph will always fail with the Exception that you currently get.
The best way to avoid your N+1 select issue is to split your query into 2 queries:
The 1st query fetches the MCValue entities using an EntityGraph to fetch the association mapped by the selected attribute. After that query, these entities are then stored in Hibernate's 1st level cache / the persistence context. Hibernate will use them when it processes the result of the 2nd query.
#Query("SELECT m FROM MCValue m") // add WHERE clause as needed ...
#EntityGraph(attributePaths = {"selected"})
public List<MCValue> findAll();
The 2nd query then fetches the Answer entity and uses an EntityGraph to also fetch the associated Value entities. For each Value entity, Hibernate will instantiate the specific subclass and check if the 1st level cache already contains an object for that class and primary key combination. If that's the case, Hibernate uses the object from the 1st level cache instead of the data returned by the query.
#Query("SELECT a FROM Answer a")
#EntityGraph(attributePaths = {"value"})
public List<Answer> findAll();
Because we already fetched all MCValue entities with the associated selected entities, we now get Answer entities with an initialized value association. And if the association contains an MCValue entity, its selected association will also be initialized.
I don't know what Spring-Data is doing there, but to do that, you usually have to use the TREAT operator to be able to access the sub-association but the implementation for that Operator is quite buggy.
Hibernate supports implicit subtype property access which is what you would need here, but apparently Spring-Data can't handle this properly. I can recommend that you take a look at Blaze-Persistence Entity-Views, a library that works on top of JPA which allows you map arbitrary structures against your entity model. You can map your DTO model in a type safe way, also the inheritance structure. Entity views for your use case could look like this
#EntityView(Answer.class)
interface AnswerDTO {
#IdMapping
Long getId();
ValueDTO getValue();
}
#EntityView(Value.class)
#EntityViewInheritance
interface ValueDTO {
#IdMapping
Long getId();
}
#EntityView(TextValue.class)
interface TextValueDTO extends ValueDTO {
String getText();
}
#EntityView(RatingValue.class)
interface RatingValueDTO extends ValueDTO {
int getRating();
}
#EntityView(MCValue.class)
interface TextValueDTO extends ValueDTO {
#Mapping("selected.id")
Set<Long> getOption();
}
With the spring data integration provided by Blaze-Persistence you can define a repository like this and directly use the result
#Transactional(readOnly = true)
interface AnswerRepository extends Repository<Answer, Long> {
List<AnswerDTO> findAll();
}
It will generate a HQL query that selects just what you mapped in the AnswerDTO which is something like the following.
SELECT
a.id,
v.id,
TYPE(v),
CASE WHEN TYPE(v) = TextValue THEN v.text END,
CASE WHEN TYPE(v) = RatingValue THEN v.rating END,
CASE WHEN TYPE(v) = MCValue THEN s.id END
FROM Answer a
LEFT JOIN a.value v
LEFT JOIN v.selected s
My latest project used GraphQL (a first for me) and we had a big issue with N+1 queries and trying to optimize the queries to only join for tables when they are required. I have found Cosium
/
spring-data-jpa-entity-graph irreplaceable. It extends JpaRepository and adds methods to pass in an entity graph to the query. You can then build dynamic entity graphs at runtime to add in left joins for only the data you need.
Our data flow looks something like this:
Receive GraphQL request
Parse GraphQL request and convert to list of entity graph nodes in the query
Create entity graph from the discovered nodes and pass into the repository for execution
To solve the problem of not including invalid nodes into the entity graph (for example __typename from graphql), I created a utility class which handles the entity graph generation. The calling class passes in the class name it is generating the graph for, which then validates each node in the graph against the metamodel maintained by the ORM. If the node is not in the model, it removes it from the list of graph nodes. (This check needs to be recursive and check each child as well)
Before finding this I had tried projections and every other alternative recommended in the Spring JPA / Hibernate docs, but nothing seemed to solve the problem elegantly or at least with a ton of extra code
Edited after your comment:
My apologize, I haven't undersood you issue in the first round, your issue occurs on startup of spring-data, not only when you try to call the findAll().
So, you can now navigate the full example can be pull from my github:
https://github.com/bdzzaid/stackoverflow-java/blob/master/jpa-hibernate/
You can easlily reproduce and fix your issue inside this project.
Effectivly, Spring data and hibernate are not capable to determinate the "selected" graph by default and you need to specify the way to collect the selected option.
So first, you have to declare the NamedEntityGraphs of the class Answer
As you can see, there is two NamedEntityGraph for the attribute value of the class Answer
The first for all Value without specific relationship to load
The second for the specific Multichoice value. If you remove this one, you reproduce the exception.
Second, you need to be in a transactional context answerRepository.findAll() if you want to fetch data in type LAZY
#Entity
#Table(name = "answer")
#NamedEntityGraphs({
#NamedEntityGraph(
name = "graph.Answer",
attributeNodes = #NamedAttributeNode(value = "value")
),
#NamedEntityGraph(
name = "graph.AnswerMultichoice",
attributeNodes = #NamedAttributeNode(value = "value"),
subgraphs = {
#NamedSubgraph(
name = "graph.AnswerMultichoice.selected",
attributeNodes = {
#NamedAttributeNode("selected")
}
)
}
)
}
)
public class Answer
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "value_id", referencedColumnName = "id")
private Value value;
// ..
}
Want to add a child object without fetching it but getting LazyInitializationException.
My child is hug list so dont want to fetch all child just to add one child object.
#Entity
public class JobRunId{
#OneToMany(mappedBy = "jobRun", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<JobStep> jobSteps = new ArrayList<>();
public void addStep(JobStep jobStep) {
if (jobStep != null) {
jobStep.setJobRun(this);
this.jobSteps.add(jobStep);// here getting LazyInitializationException
}
}
}
service class
jobRun = repository.findById(id);
//Dont want to do hibernate.initialize here as want to avoid fetch all
child records
JobStep jobStep = new JobStep();
//some jobStep status
jobRun.addStep(jobStep);//adding one step here to jobRun.getting error
is there a way just to add one child record without fetching all the child records.
Persist your jobStep directly (i.e. using something like em.persist(jobStep) or JobStepRepository.save(jobStep) ...
NOTE: the jobStep instance should have a reference to the JobRunId already persisted ...
is there a way just to add one child record without fetching all the child records.
Considering you want to add JobStep entities of given JobRunId, you can use JOIN FETCH directive like query. The JOIN FETCH directive instructs Hibernate to issue an INNER JOIN so JobRunId fetched along with its JobStep. The createQuery will look something similar:
SELECT js
FROM JobStep js
JOIN fetch js.jobRunId
WHERE js.id = :jobId"
Alternative approach is to use DTO projection, if you do not want to modify an entity then use DTO projection. Basically, it allows you to fetch fewer columns.
I am not sure the exact details of the relation and why you managing this way. Here is good article to read Best way to handle LazyInitializationException. Hope this would give you insight to solve your problem.
I am facing an issue where the data is getting fechted recursively. I wanted to avoid the child to fetch the parent data. Which is causing a recursive issue.
I have mentioned the code below
Pojo Structure
class Parent {
..
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> childs;
..
}
class Child {
..
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parentId")
private Parent parent;
..
}
Fetching the data like this
` em = EMF.get().createEntityManager();
Query q = em.createQuery("Select p from Parent p", Parent.class);
List<Parent> parents = q.getResultList();
// Till this point all looks good but when the code gets executed
parent.getChilds();
`
It is fetching the data like this:
Parent
child1
Parent
child2
Parent
child2
Parent
..
..
child2
..
Which I dont need I just want the data like this:
Parent1
child1
child2
Parent2
child1
child2
child3
While FetchType.EAGER is a contract, FetchType.LAZY is only a hint, because lazy fetching is not always possible. This may depend e.g. on the JPA provider you use as well as on its configuration. Lazy fetching is particularly problematic with to-one relationships.
If every Child has a Parent, try adding optional=false to your #ManyToOne. This might enable lazy fetching.
Since the Parent entity is already loaded into the persistence context, populating Children.parent shouldn't trigger queries against the database. Are you actually seeing queries being executed? How do you know Children.parent is being loaded? If you are accessing the value to check that fact, chances are you are actually triggering the on-demand loading yourself.
It'll work as infinite loop for hefty data. Best practice is to mention #JsonIgnore at child class column.
Thank me Later
To avoid this problem of cyclic references, I used a Mapper with MapStruct (see official documentation for a quick setup) :
Then I can easily write a mapper to ignore a property like this :
public interface SecondaryObjectMapper {
#Mapping(target = "primaryObject.secondaries", ignore=true),
SecondaryObjectDto toSecondaryObjectDto(SecondaryObject source);
}
and use it like this for example :
repository.findAll(pageable).map(secondaryObjectMapper::toSecondaryObjectDto)
To Avoid this issue , Please declare below annotation Parent & Child getter methods
#JsonBackReference
public Parent getParent() {
return parent;
}
#JsonManagedReference
public List<Child> getChilds() {
return childs;
}
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.
What is a best practice to store 'large' data, represented by List in Java, in database?
i'm considering 3 variants:
Use '#OneToMany' to store data in separate table.
Serialize data and store it in parent table.
Store data as files(naming conventions? same as id?).
To be more specific
'Large' data entities:
class SingleSleeper{
private Double startPositionOnLeft;
private Double endPositionOnLeft;
private Double startPositionOnRight;
private Double endPositionOnRight;
....
}
class RutEntry{
private Double width;
private Double position;
...
}
There are about 50 instances of SingleSleeper class and about 25000 instances of RutEntry class in one parent instance. Parent instances are generated about 40 times every day.
i'm using EclipseLink JPA 2.1, derby
Addition
Most of all i'm interested in best readability in Java. But i'm afraid that database speed will significantly decrease if i will store too much data into database. An overwhelming number of requests will be to select all instances of SingleSleeper or RutEntry classes of particular parent entity. I'm not interested for support to different database types, but i can move to other database, if needed.
I think I would do neither of your variants.
I would add a ManyToOne to the child entities (which is somehow the opposite of your first variant):
public class SingleSleeper {
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private ParentEntity parent;
...
}
public class RutEntry {
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private ParentEntity parent;
}
This ensures that you have a mapping and that you never load all 25000 entities for a parent object, if you don't need them (the lazy fetch ensures that you even don't need to load the parent entity).
You can create a OneToMany in the parent object with a mappedBy link, if you really want to. For example because you always need all child objects in the parent entity:
class ParentEntity {
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
Collection<SingleSleeper> singleSleepers;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
Collection<RutEntry> rutEntries;
}
But I don't know how EclipseLink is working here - for Hibernate you need at least an additional BatchSize annotation to indicate that it should load as many child entities as possible at once. It can't fetch all together with the parent instance (e.g. by defining both as FetchType.EAGER), as only one is allowed to be fetched eagerly (and otherwise you would have 25000 * 50 result rows in the result set of the corresponding SQL select statement).
The best to load all child entities for a parent entity is to load them separate, either using JPQL (easier to read, faster to write) or the Criteria API (typesafe, but you need a metamodel):
ParentEntity parent = entityManager.find(ParentEntity.class, id);
// JPQL:
List<SingleSleeper> singleSleepers = entityManager.createQuery(
"SELECT s FROM SingleSleeper s WHERE s.parent = %parent"
).setParameter("parent", parent).getResultList();
// Or Criteria API:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<SingleSleeper> query = criteriaBuilder.createQuery(SingleSleeper.class);
Root<SingleSleeper> s = query.from(SingleSleeper.class);
query.select(s).where(criteriaBuilder.equal(s.get(SingleSleeper_.parent), parent));
List<SingleSleeper> singleSleepers = entityManager.createQuery(query).getResultList();
You have three advantages of that approach:
Still easy to read - if you put the loading into its own method.
You are flexible to decide when to load the 25050 children.
You can load a subset of the children as well (by modifying the result of createQuery with Query.setFirstResult and Query.setMaxResults).