hibernate setting fetch dynamically - java

I have an entity named Entity with a one to many relation with some children of the type Child set up by this:
#OneToMany(fetch=FetchType.LAZY, mappedBy = "child" ) private Set<Child> children;
I put fetch=FetchType.LAZY because I need it to be as quik as possible whenever I needn't know what children are.
Sometimes, instead I need to get them when I look for some Entity.
In those cases, according to the suggestions of these 1, 2, 3, 4 questions, I wrote something of the kind:
from Entity as ent left join fetch ent.children where /* some conditions */
the point is that I get conditions from another class, so I won't use Criteria, unless I find a way to transform a query object of the kind
getSession().createQuery("from Entity where /* some conditions */");
in a criteria. The problem is that I get the number of records given by the join, so one for each empty set of children and one for each Child.
What I need, is just the instance (or the list of instances, according to the fact that I call getResultList() on the list) of the Entity where the set is properly populated by the instance's children.

Actually this is a feature: Imagine you would fetch your Entity without it's children and later persist it again, Hibernate would delete all the children. Because it can not determine weather you did or did not delete them "yourself".
What you might want to try is fetching a Tuple (and in so doing only select the table columns you are interested in) instead of the whole entity. You can only access lazy fields (properly) within an active transaction.

Related

Duplicated results in Hibernate OneToMany List

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

Querying table with subclasses-like relationships

In the database of my app there are currently 3 tables:
Parent Table - (general goal)
ChildA
ChildB
If I were to speak in terms of OOP, both ChildA and ChildB are "subclasses" of the Parent table, however they are not similar.
The Relationships between the tables:
A row in the Parent Table has an integer that defines whether the row is related to type A (ChildA) or type B (ChildB).
In both ChildA and ChildB there is a reference to the related row in Parent Table (id). There can be only 1 Parent row related to a child and there can also be 1 child related to a parent (one-to-one r/s).
There is not any pair of columns with the same name inside all of the tables.
What I'm trying to do is to basically retrieve all of the rows in the Parent table, then according to the type column of each row to retrieve additional related info from either ChildA or ChildB.
This would be very easy to do if I were to first retrieve all of the parent rows, and then run through the rows with a loop and query n times for every row, but that would probably be highly inefficient, I guess.
I was wondering whether there is a better approach to this, perhaps even in a single query.
I know I could use INNER JOIN or something, but I'm not sure how it'd work in this case where I need to join 2 tables with a third one (and where the columns are different both in number and content).
So the question is, what would be the most efficient way to preform it?
EDIT:
I saw this question was marked as a duplicate of another question, however, I do not ask how to design my database, but how to QUERY it.
I'm using a Table-Per-Type design, and would like to get all of the rows from all of the different types (currently 2).
I would know how to do so in a case where I wanted to get all of the rows from a single type, but not in this situation, which is why I'm asking whether and how it would be possible with a single query (with a mechanism similar to JOIN for example). I know I could achieve it by querying twice, but I'd like to learn a more efficient way to do it.
I can think of two different approaches (with their pluses and minuses :)
1) Have as many queries as subtypes and retrieve a subtype at a time. In the example case, you will have two queries:
select * from ChildA where id in (select childId from Parent where childType='A')
select * from ChildB where id in (select childId from Parent where childType='B')
This will give you the lowest possible data transfer between your application and the database at a relatively reasonable performance. You will "waste" the effort your database makes to filter the Parent table (the database will have to do it twice)
2) You have one query which retrieves both ChildA and ChildB as part of the same result set like this:
select ChildA.*, ChildB.* from Parent
left outer join ChildA on Parent.ChildId=ChildA.id
left outer join ChildB on Parent.ChildId=ChildB.id
The above query only works if children have unique ids (that is, if there is a ChildA with id 5, there is no ChildB with id 5). If this is not the case, you need a slightly "uglier" query:
select ChildA.*, ChildB.* from Parent, ChildA, ChildB
where (Parent.ChildType='A' and Parent.ChildId=ChildA.id) or
(Parent.ChildType='B' and Parent.ChildId=ChildB.id)
This will give you a result set which contains all columns from both ChildA and ChildB with many NULL values (for each ChildA record, all ChildB columns would be NULL). This way, you have a single query (which may perform faster that the multiple queries in the first approach), but you need to ship more data.
You can create a join for this like this:
select * from (a outer join b on a.key = b.fg_key) outer join c on a.key = c.fg_key
I am not 100% sure about the placement of the opening brace but I remember using this before
But as the amount of sub-types grows, it will become more and more complex to maintain this properly, all column names must be aliased in the query. It would be easiest to perform a two step load, first load all items of type 1, then of type 2. This will keep the code cleaner and easier to maintain.
Efficiency-wise I don't expect this to be much slower than a single query. I would suggest going with the multiple query variant in the first version and optimize when performance becomes an issue.
Basically this kind of requirement we should try to handle in our programme
we should not try to achieve from database.
However I tried below query where we it outputs from one of the child table and in other child only null;
select parent.id, parent.description, (select name from car where id = parent.id) as child1, (select Name from bike where id = parent.id) as child2 from vehicle parent;
Hope this will help you.

#ManyToOne relationship,JPA

I have a bidirectional many to one relationship. When I retrieve the parent object from the table, all the child objects should get retrieved but only the first one is getting retrieved
The parent looks like
#Entity
public class xyz{
#OneToMany(mappedBy="xyz",cascade=CascadeType.PERSIST,fetch=FetchType.EAGER)
private Set<zyx> zyxDO;}
The child class looks like
public class zyx{
#ManyToOne
#JoinColumn(name="id")
private xyz xyzDO;
}
Is there any annotation where I can retrieve all the rows of the underlying database
Maybe it is a typo, but the value of mappedBy should be the name of the attribute in the owning entity that points back to the inverse entity, in this case xyzDO.
And maybe because you are using a Set instead of a Collection could cause that only one child is retrieved (if they are identical).
What do you mean by retrieve all the rows of the underlying database? You retrieve only the children which parent's key set. Note also, that fetch eager can easily cause great performance issues, because children will be fetched always, even if you don't need them.
Ensure you set both sides of the relationships when adding/setting the relationship.

Recursive JPA query?

Does JPA 2 have any mechanism for running recursive queries?
Here's my situation: I have an entity E, which contains an integer field x. It also may have children of type E, mapped via #OneToMany. What I'd like to do is find an E by primary key, and get its value of x, along with the x values of all its descendants. Is there any way to do this in a single query?
I'm using Hibernate 3.5.3, but I'd prefer not to have any explicit dependencies on Hibernate APIs.
EDIT: According to this item, Hibernate does not have this feature, or at least it didn't in March. So it seems unlikely that JPA would have it, but I'd like to make sure.
Using the simple Adjacency Model where each row contains a reference to its parents which will refer to another row in same table doesn't co-operate well with JPA. This is because JPA doesn't have support for generating queries using the Oracle CONNECT BY clause or the SQL standard WITH statement. Without either of those 2 clauses its not really possible to make the Adjacency Model useful.
However, there are a couple of other approaches to modelling this problem that can applied to this problem. The first is the Materialised Path Model. This is where the full path to the node is flattened into a single column. The table definition is extended like so:
CREATE TABLE node (id INTEGER,
path VARCHAR,
parent_id INTEGER REFERENCES node(id));
To insert a tree of nodes looks some thing like:
INSERT INTO node VALUES (1, '1', NULL); -- Root Node
INSERT INTO node VALUES (2, '1.2', 1); -- 1st Child of '1'
INSERT INTO node VALUES (3, '1.3', 1); -- 2nd Child of '1'
INSERT INTO node VALUES (4, '1.3.4', 3); -- Child of '3'
So to get Node '1' and all of its children the query is:
SELECT * FROM node WHERE id = 1 OR path LIKE '1.%';
To map this to JPA just make the 'path' column an attribute of your persistent object. You will however have to do the book-keeping to keep the 'path' field up to date. JPA/Hibernate won't do this for you. E.g. if you move the node to a different parent you will have to update both the parent reference and determine the new path value from the new parent object.
The other approach is called the Nested Set Model, which is bit more complex. Probably best described by its originator (rather than added verbatim by me).
There is a third approach called Nested Interval Model, however this has a heavy reliance of stored procedures to implement.
A much more complete explanation to this problem is described in chapter 7 of The Art of SQL.
The best answer in this post seems like a massive work-around hack to me. I've already had to deal with data models where brilliant engineers decided it would be a good Idea to code Tree Hiarchies in DB fields as text such as: "Europe|Uk|Shop1|John" and with massive volumes of data in these tables. Not surprsingly, the performance of query of the form MyHackedTreeField LIKE 'parentHierharchy%' where killers.
Addressing this type of problem ultimately required creating In memory cache of the tree hiearchies and so many others...
If you need to run a recursive query and your data volume is not massive... make your life simple and simply load the DB fields you need to run your plan. And code your recursion in java.
Don't make it in the DB unless you have a good reason to do it.
And even if the volume of data you have is massive, you most likely can subdivide your problem into indepent recursive tree batches and process those one at time without needing to load all the data at once.
I know this question is old, but as it was linked in a different question, I wanted to give an update on this, as Blaze-Persistence offers support for working with recursive CTEs on top of the JPA model.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. To model CTEs or recursive CTEs, which is what you need here, you first need to introduce a CTE entity that models the result type of the CTE.
#CTE
#Entity
public class GroupCTE {
#Id Integer id;
}
A query that fetches a hierarchy of groups could look like the following
List<Group> groups = criteriaBuilderFactory.create(entityManager, Group.class)
.withRecursive(GroupCTE.class)
.from(Group.class, "g1")
.bind("id").select("g1.id")
.where("g1.parent").isNull()
.unionAll()
.from(Group.class, "g2")
.innerJoinOn(GroupCTE.class, "cte")
.on("cte.id").eq("g2.parent.id")
.end()
.bind("id").select("g2.id")
.end()
.from(Group.class, "g")
.fetch("groups")
.where("g.id").in()
.from(GroupCTE.class, "c")
.select("c.id")
.end()
.getResultList();
This renders to SQL looking like the following
WITH RECURSIVE GroupCTE(id) AS (
SELECT g1.id
FROM Group g1
WHERE g1.parent_group_id IS NULL
UNION ALL
SELECT g2.id
FROM Group g2
INNER JOIN GroupCTE cte ON g2.parent_group_id = cte.id
)
SELECT *
FROM Group g
LEFT JOIN Group gsub ON gsub.parent_group_id = g.id
WHERE g.id IN (
SELECT c.id
FROM GroupCTE c
)
You can find out more about recursive CTEs in the documentation: https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#recursive-ctes
I had problem like this, querying a menu nodes from one table,
The way I founded was this:
suppose we have a class named Node,created a Unidirectional One-to-Many Association like this:
#OneToMany( fetch = FetchType.EAGER)
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private List<Node> subNodeList;
also have a filed named for example boolean isRoot in entity, to mention if this node is root menu item ,
and then, by querying for nodes that there isRoot is true, we just get top nodes and because of FetchType.EAGER, we also get sub nodes in List.
This will cause multiple queries , but for small menu like things it will be ok.

Multiple #ManyToMany sets from one join table

I'm mapping a proprietary database to Hibernate for use with Spring. In it, there are a couple of jointables that, for entity A and entity B have the following schema:
CREATE TABLE AjoinB (
idA int not null,
idB int not null,
groupEnum enum ('groupC', 'groupD', 'groupE'),
primary key(idA, idB, groupEnum)
);
As you can see, this indicates that there can be multiple A-B relationships that put them in different groups. I'd like to end up with, first line for entity A and second for entity B, the following sets
Set<B> BforGroupC, BforGroupD, BforGroupE;
Set<A> AforGroupC, AforGroupD, AforGroupE;
So far, I've only managed to put them in one set and disregard the groupEnum relationship attribute:
#ManyToMany(targetEntity=B.class, cascade={ CascadeType.PERSIST, CascadeType.MERGE } )
#JoinTable(name="AjoinB", joinColumns=#JoinColumn(name="idA"), inverseJoinColumns=#JoinColumn(name="idB") )
private Set<B> BforAllGroups;
and
#ManyToMany( mappedBy = "BforAllGroups", targetEntity = A.class )
private Set<A> AforAllGroups;
How can I make multiple sets where they belong either in groupC, groupD or groupE?
Cheers
Nik
If you're considering doing this, don't. Tables are cheap nowadays what's with the economy and all, so just create one per association; it'll be so much easier.
If you're bound by a legacy database and you can't change the structure of that table I would
Consider skaffman's solution first (+1, btw). Depending on your target database you may be able to write a trigger for your views that would insert adequate "discriminator" value.
If the above isn't possible in your DB, another solution is to use custom SQL for CRUD operations for your collections. Keep in mind that this will NOT work (e.g. your "discriminator value" won't get applied) for complex HQL queries involving your association as part of condition. You can also mix / match this with above - e.g. use views and use custom SQL for insert / delete.
If both of the above fail, go with "association as a separate entity" as suggested by framer8. That's going to be rather ugly (since we're assuming here you can't change your tables) due to composite keys and all extraneous code. It may, in fact, be impossible if any of your associations allows duplicates.
To my knowledge, Hibernate cannot use such a "discriminator" column in the way that you want. Hibernate requires a join table for each of them.
Perhaps you might be able to define additional views on the table, showing each of the groupings?
I think the advise anytime you need to access a field in a link table is to make the link table an object and a hibernate entity in its own right. A would have a set of AtoB objects and AtoB would have a set of B objects. I have a simmilar situation where the link table has a user associated with the link.
select joinTable.b from A a
left join a.AtoB joinTable
where joinTable.group = 'C'
It's not as elegant as having an implicit join done by hibernate, but it does give you the control you need.

Categories