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.
Related
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.
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.
I am attempting to create a domain/entity class based on a complex query. The query unions a bunch of tables together and unfortunately I am not able to create a view on the database for this query. I have been trying to set up the entity object but I am unsure of how to ensure that the marshaling works properly (and ensure the entity acts as read-only object).
As an example of the query, I am doing something like this:
Select
U_T.a,
U_T.b,
U_T.c,
C_T.a
FROM
(select
A_T.a,
null as b,
A_T.c,
1 as ind
from A_T
UNION
select
B_T.a,
B_T.b,
null,
0 as ind
FROM B_T
) U_T
left outer join C_T on C_T.fk_a = U_T.a;
The other issues are that this union can result in instances where there is no unique key column. This is fine as this data is for viewing only, and never editing. However the #Entity annotation wants a column to be listed with the #ID annotation. Another issue is that I do not believe I can use the other entity classes as the goal is to reduce the number of DB transactions from this query (as the actual one can result in hundreds of recursive queries being performed).
If I need to give any more information please let me know.
Please help me with these Hibernate querying issues.
Consider the following structure:
#Entity
class Manager {
#OneToMany
List<Project> projects;
}
0) there are 2 possible ways of dynamic fetching in HQL:
select m from Manager m join m.projects
from Manager m join fetch m.projects
In my setup second one always returns a result of cartesian product with wrong number of objects in a list, while the first one always returns correct number of entities in a list. But the sql queries look the same. Does this mean that "select" clause removes redundant objects from the list in-memory? In this case its strange to see an advice in a book to use select distinct ... to get rid of redundant entities, while "select" does the job. If this is a wrong assumption than why these 2 queries return different results?
If I utilize dynamic fetching by one of the 2 methods above I see a classic n+1 select problem output in my hibernate SQL log. Indeed, FetchMode annotations (subselect or join) do not have power while fetching dynamically. Do I really can't solve the n+1 problem in this particular case?
Looks like Hibernate Criteria API does not support generics. Am I right? Looks like I have to use JPA Criteria API instead?
Is it possible to write HQL query with an entity name parameter inside? For example "from :myEntityParam p where p.id=1" and call setParameter("myEntityParam", MyClass.class) after this. Actually what I want is generic HQL query to replace multiple non-generic dao's by one generic one.
0) I always use a select clause, because it allows telling what you want to select, and is mandatory in JPQL anyway. If you want to select the managers with their projects, use
select distinct m from Manager m left join fetch m.projects
If you don't use the distinct keyword, the list will contain n instances of each manager (n being the number of projects of the manager): Hibernate returns as many elements as there are rows in the result set.
1) If you want to avoid the n + 1 problem, fetch the other association in the same query:
select distinct m from Manager m
left join fetch m.projects
left join fetch m.boss
You may also configure batch fetching to load 10 bosses (for example) at a time when the first boss is accessed. Search for "batch fetching" in the reference doc.
2) The whole Hibernate API is not generified. It's been made on JDK 1.4, before generics. That doesn't mean it isn't useful.
3) No. HQL query parameters are, in the end, prepared statement parameters. You must use String concatenation to do this.
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.