Today I learned one tutorial where author explained hibernate association one-to-many / many-to-one.
I don't want to write all his code here. So, I try to focus on main...
We have two entities: "Team" and "Player"
In code we have:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="team_id")
private Team team;
and
#OneToMany(mappedBy="team", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Player> players;
The main thing which I interested is type of fetch. So, we inserted one team and some players which are belongs to this team.
My DAO class marked with
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
And now I want to load team entity from database with filled collection of players.
Team team = new Team();
sessionFactory.getCurrentSession().get(team, id);
//and after somewhere in code
team.getPlayers();
At result I will get LazyInitializationException. It's obviously...
We can resolve this problem:
Hibernate.initialize(team.getPlayers());
Or we can can load collection of players using HQL. I mean:
Query query = currentSession().createQuery("from Player p where p.team.id =:id");
query.setParameter("id",key);
team.setPlayers(new HashSet(query.list()));
Is it right solutions? And which of them I should use in real development?
And one more question. After I initialized collection of players (no matter which of solutions I used) I can get one of them. Ok, And in "player" entity field Team will initialize. Why? And this team will be with filled player's collection... we have circular dependencies... This is normal?
LazyInitializationException comes when session is closed and you try to load lazy object from detached Object. Here team is detached object (As session might have closed before this call) and trying to load lazy object players.
And About which approach need to use for this kind of problem, it is up to your requirement. if you call Hibernate.initialize on each proxy, Each call will generate one query.
And with below you are using joint :-
Query query = currentSession().createQuery("from Player p where p.team.id =:id");
this will only require one query, i think you should use this approach if you query a lots of data.
For more information take a look at this link.
Related
I've always thought what could've been the best way to deal with this problem in Spring/Hibernate. Imagine we have an Entity like House:
public class House {
Long id;
String name;
String address;
List<Person> people;
}
Where Person is another Entity. Now, if we want to show a list of every house that is stored, we just want some details, id, name and address, we don't want the entire data to be fetched because we are not showing the people in the list.
When we enter a detail view, however, we are interested in showing the people as well.
What's the best way to fetch the people just when you want them, taking care of the LazyInitializationException as well?
There are a variety of options. I just highlight the one I use:
JOIN FETCH
If you need the people eager loaded you can create a query like:
select h from House h fetch join people p
If you think this will produce to many queries go for entity graphs.
ENTITY GRAPH
Definition:
#Entity
#NamedEntityGraph(name = "graph.House.people",
attributeNodes = #NamedAttributeNode("people"))
public class House {
Usage with Spring Data JPA:
#EntityGraph(value = "graph.House.people", type = EntityGraphType.LOAD)
List<House> findById(Long id);
But there are more. Please read: https://thoughts-on-java.org/5-ways-to-initialize-lazy-relations-and-when-to-use-them/
You can use the Entity graph in the repository. Just use #EntityGraph to define an ad-hoc entity graph on your method.
#EntityGraph(attributePaths = {"people"})
List<House> findAll();
Here, you can define attributes which you want to fetch as attributePaths.
Call this when you want to show people otherwise call findAll().
And you can use lazy fetching for people also, whenever you want people just call the getter of it then JPA will automatically fetch people in another query.
I have read some code describing relationship of two entities on both sides which look like following:
public class Department {
#OneToMany(mappedBy = "department",fetch = FetchType.EAGER , cascade = CascadeType.ALL)
private List<Course> courses = new ArrayList<>();
}
public class Course {
#ManyToOne
private Department department;
}
There are two scenarios: When I use relationship annotation on both sides("on both tables Department and Course ") with One-To-Many relationship and When i only use on one side("only table Derpartment). Scenario is similar for Many-To-Many relationship as well.
My question: Should "fetch = FetchType.EAGER , cascade = CascadeType.ALL" be defined only on one side or both sides in the above mentioned scenarios ?
fetch and cascade options can be defined on both sides. If its defined only on one side that it won't have any impact when the other side object is fetched. e.g. If eager fetch is set for courses in Department class but not in Course class then, if a select query is made on department then, it will fetch all its courses along with it But if a select query is made on course then, it won't fetch its associated department unless explicitly called out in query.
Same goes for cascade option. Thus, its definition on either side depends on the kind of queries which are required to be made. If there are going to be a lot of queries on department which needs all the courses information every time but its not the same for fetching a course then, fetch option should be defined only in Department class for courses.
Bi-directional association is good but with additional update in your code for efficient queries i.e. use JoinColumn with #ManyToOne association so that additional association mapping information between two entities doesn't have to be maintained on code side.
I have a class Entry which has two fields serving auditing purposes: startAuditAction and endAuditAction. One audit action can affect several entries, therefore the class Entry describes ManyToOne relationships as follows:
public class Entry{
#Id
#Column(nullable = false)
protected String path;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "start_action_id")
protected AuditAction startAction;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(updatable = true, nullable = true, name = "end_action_id")
protected AuditAction endAction;
}
I want to retrieve instances of Entry based on conditions on the path field and the audit fields. For example to retrieve entries which have not yet been deleted, the HQL would look something like that:
SELECT DISTINCT entry FROM ENTRY_TABLE entry JOIN FETCH entry.startAction startAct LEFT JOIN FETCH entry.endAction endAct WHERE entry.path LIKE myPath and endAct IS NULL
I am using lazy loading together with JOIN FETCH to avoid the N+1 problem while still being able to access the audit fields. However, I have two problems with this:
Firstly, this really does not seem clean to me: if I know I want to access the audit fields (namely the audit actions timestamp), then they should not be lazy loaded. But if I use eager loading I am facing the n+1 problem even if I use JOIN FETCH (and in that case I do not understand why fetch = FetchType.EAGER would ever be useful)...
Secondly, even though I am avoiding the n+1 problem and therefore firing less SQL queries, I get some performance issues for the overall use of my database, probably because of the joins.
What is the proper way to avoid firing additional queries while preserving a good throughput ?
Thanks!
1- Using join fetch is useful when you have FetchType.LAZY in a field that you know you'll need in that specific case whereas using FetchType.EAGER will force that entity to always load the collection independently from the query
(e.g. with your same configuration example you can do multiple query and only when you need the collection use the JOIN FETCH)
2- You probably have problems somewhere else, i doubt the join is what is slowing you down
I have mapped an 1:N relation with a #OneToMany List, but when I access the list, the results are duplicated due to an OUTER JOIN.
This is how the mapping looks like:
#Entity
public class Programmer
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(name="emails", joinColumns=#JoinColumn(name="id", nullable=false))
#Column(name="email", nullable=false)
protected Set<String> emails = new HashSet<String>();
#OneToMany(mappedBy="programmer", fetch=FetchType.EAGER)
private List <Game> games = new ArrayList<Game>();
When I get the attribute with prog.getGames(), the results comes duplicated because the Hibernate SQL makes an OUTER JOIN:
from programmer
left outer join emails on programmer.id=emails.id
left outer join game on programmer.id=game.id
where programmer.id=?
Is there any solution without transforming the List into a Set? I need to get the games with prog.getGames(), can not use a custom HQL or Criteria.
While the use of Set<> fundamentally resolves your issue, I'd argue that is simply a bandaid to get the expected results you're after but it doesn't technically address the underlying problem.
You should ultimately be using the default lazy fetch strategy because I'm of the opinion that eagerly loading any associations, particularly collection-based ones, are specific to a query and therefore should be toggled when you construct specific queries and not influenced as a part of your entity mapping model as you're doing.
Consider the future where you add a new query but you're only interestesd in attributes from the aggregate root entity. Your mapping model will still impose eagerly fetching those associations, you'll consume additional resources to by having a larger persistence context which means more memory consumption and impose unnecessary database joins for something which you aren't going to use.
If there are multiple collections that you need to hydrate, I would instead recommend you consider using FetchMode.SUBSELECT instead.
If we assume your query has 10 entities being returned, the default lazy strategy with 2 collections would issue 21 queries (1 for the base result set and 2 for each loaded entity).
The benefit of SUBSELECT is that Hibernate will actually only issue 3 queries (1 for the base result set and 1 for each collection to load all collection elements for all entities). And obviously, depending on certain queries, breaking one query with left-joins into 3 queries could actually perform better at the database level too.
Ive resolved this problem with #Fetch(FetchMode.SUBSELECT)
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.SUBSELECT)
private List<CompanyUserEntity> companyUserRelations;
I had the same problem. companyUserRelations had duplicate objects (I mean the same pointers to the same object, not duplicated data)
So after reading #dimitry response, I added #Fetch(FetchMode.SUBSELECT) and it worked
I have two classes InvitedPerson and Flight with a one to one relationship with each other. Here is how they are annotated.
public class InvitedTech{
...
#OneToOne(mappedBy="invitedTech", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Flight flight;
#OneToOne(mappedBy="invitedTech", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Hotel hotel;
...
}
public class Flight{
...
#OneToOne
#JoinColumn(name="invitedTechId", nullable=false)
public InvitedTech invitedTech;
...
}
As you can see Flight is the owner of the relationship and InvitedTech is the other side of this bidirectional relationship. InvitedTech also has a OneToOne relationship with Hotel Now, when I write a simple query to fetch all flights, it triggers three queries in total. 1st which gets me the results, but fires 2 additional queries.
List<Flight> flg = JPA.em().createQuery("SELECT flg from Flight flg").getResultList();
Query that gets all flights (This is the only one that I need)
Query with a join between InvitedTech and Flight
Query with a join between invitedTech and Hotel
Why are query 2&3 being executed even though I have set FetchType=Lazy. I am not accessing Hotel Information. And Flight should not be queries again as the first query returns the data.
After some playing around when I remove mappedBy attribute from both the annotations, those 2 addition queries don't get executed(i.e only 1st gets executed).
Why does the mappedBy attribute cause additional queries to be executed even though FetchType=Lazy. Is there a way to stop this?
I believe this is due to one of Hibernate's idiosyncrasies:
non-optional one-to-one relationships are eagerly loaded regardless of whether they are mapped as Lazy.
The reasoning behind this is that as the engine has to look in the association table anyway - to determine whether it should set the association as a proxy or as null - then it may as well load the associated entity anyway.
I have experienced this myself and as far as I know the only way round it is to mark the relationship with optional=false which tells Hibernate it can always set a proxy.
If the relationship is optional then the only other option seems to be byte code instrumentation.
See also:
https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one
Making a OneToOne-relation lazy
You have not set the association from Flight to InvitedTech lazy. So it loads the InvitedTech associated with the flight.
Since it can't know from the InvitedTech if there exist a Hotel and a Flight associated with the InvitedTech, it can't decide if these fields should be null or should be proxies. So it's forced to execute additional queries to know if a hotel/flight exists for the InvitedTech.