I have an entity and I want it to have an attribute of the count of the children.
Let's say I have these two tables:
create table father
(
id int,
name
);
create table child
(
id int,
father_id int
);
I want the father entity in Hibernate to have an attribute like this:
private int countChildren;
From SQL perspective, it is easy:
SELECT f.id, f.name, count(father_id) as count_children
FROM father f JOIN child c ON f.id = c.father_id
GROUP BY f.id, f.name;
Easy and efficient. But, since I want to have it as an Hibernate attribute of the entity, the closest I found is:
#Formula("(select count(*) from child c where c.father_id = id)")
private int countChildren;
However, behind the scenes it is performed as a Scalar sub-query, where the access to the child table is per every father record - highly inefficient (some databases like Oracle do some optimization for these kind queries, but I don't want to rely on that, and it is still not optimal compared to the SQL version with GROUP BY).
Is there an Hibernate way to solve this one? One that behind the scenes is performed in a similar way to the above GROUP BY example?
#Formula("(SELECT COUNT(*) FROM father f JOIN child c ON f.id = c.father_id WHERE f.id = id)")
Where last id is your entity field name.
Related
I am attempting to map the result of a SQL left join to an object structure of the form parent -> [child], where a condition on the SQL query restricts the result to one parent. The result set contains n rows because there are n children, but of course each row only belongs to the single parent.
I'm using Hibernate, and have placed a #Subselect on my 'parent' entity, and this contains my entire left join query.
#Subselect("SELECT x.*, y.* FROM x INNER JOIN y on x.a = y.a WHERE x.a = 1")
public class X {
... X attributes
private List<Y>;
}
public class Y {
... Y attributes
}
How can I instruct Hibernate to 'collapse' the columns on the left side of the result set to a single parent object, and coalesce the remaining columns from each row into many instances of 'child' which are added to the list on the parent?
Is this possible, or do I need to join using Hibernates' #OneToMany annotation. This does work for me, but results in two separate calls to the database, which I feel is inefficient.
This can be achieved using JPQL (or HQL if you are using Hibernate) and the 'join fetch' command :
SELECT a from Author a left join fetch a.books;
Attempting to perform multiple joins in this manner can produce a 'MultipleBagFetchException'. This can be worked around, but in any case, by farming the joins (especially several joins) off to the DB server, we produce a cartesian product result set which can grow very large.
Counterintuitively, it can actually be more efficient to make multiple round trips to the database (using batching to mitigate the N + 1) problem, and then join the parent and children in memory using ORM.
Thanks to https://stackoverflow.com/users/1025118/vlad-mihalcea for many pointers on this topic across the web, in particular this article on his blog - https://stackoverflow.com/users/1025118/vlad-mihalcea
I have a tree structure defined in hibernate. Where I have an abstract element called TreeObject. A tree object can have several children and only one parent.
I also have some implementation for this class: form, category, group of questions and question. All of them inherits from TreeObject.
The idea is that a form can have as children categories and questions. The category can have as children groups and questions, and groups can have as children other groups and questions.
Then I have defined the TreeObject as:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeObject{
#ManyToOne
private TreeObject parent;
#OneToMany(mappedBy = "parent")
private List<TreeObject> children;
[...]
}
Then, the other objects are very simple. For example, the form element is:
#Entity
public class Form extends TreeObject {
[...]
}
And the other elements are similar, except by some irrelevant code for this question.
The problem resides when I want to retrieve the children of an element (as TreeObject is not a real table in the database). For example, to get all children of a form, hibernates create multiples union from the tables Form, Category, Group, Question for representing the TreeObject table equivalence and selecting the children. When the database has several elements (but not so much), getting the children can takes around 0.5secs due to the multiple union generated. Then when I will have a big amount of data, I am going to have big performance issues with that query.
For example, an example of a query obtained to get a form is:
select form0_.ID as ID1_7_0_, form0_.createdBy as createdB3_7_0_, form0_.name as name2_12_0_, form0_.parent_ID as parent_I5_12_0_, children1_.parent_ID as parent_I5_7_1_, children1_.ID as ID1_12_1_, children1_.ID as ID1_7_2_, children1_.createdBy as createdB3_7_2_, children1_.name as name2_12_2_, children1_.parent_ID as parent_I5_12_2_, children1_.version as version2_2_2_, children1_.clazz_ as clazz_2_, questionva2_.BaseQuestionWithValue_ID as BaseQues1_7_3_, questionva2_.questionValues as question2_38_3_, treeobject3_.ID as ID1_7_4_, treeobject3_.comparationId as comparat2_7_4_, treeobject3_.name as name2_12_4_, treeobject3_.parent_ID as parent_I5_12_4_, treeobject3_.clazz_ as clazz_4_ from form form0_ left outer join
( select ID, name, parent_ID, 10 as clazz_ from questions
union select ID, name, parent_ID, 24 as clazz_ from group
union select ID, name, parent_ID, 32 as clazz_ from category
union select ID, name, parent_ID, 26 as clazz_ from form )
children1_ on form0_.ID=children1_.parent_ID left outer join
question_with_values questionva2_ on children1_.ID=questionva2_.BaseQuestionWithValue_ID left outer join
( select ID, name, parent_ID, 10 as clazz_ from questions
union select ID, name, parent_ID, 24 as clazz_ from group
union select ID, name, originalReference, parent_ID, 32 as clazz_ fromcategory
union select ID, comparationId, name, parent_ID, 26 as clazz_ from form )
treeobject3_ on form0_.parent_ID=treeobject3_.ID where form0_.ID=344820 order by children1_.sortSeq asc;
(note: I have removed several columns to make it simpler to understand the code)
Now I have use #BatchSize to increase performance, and the general performance of the application is better, but still is not a real solution.
My idea is to use something like #WhereJoinTable to filter the 'big' union query and only retrieve the real children from Category and Questions and not all of them, avoiding the performance issue. But as children parameter is mappedBy by parent, I have not any clue how can I achieve this.
#ManyToOne
#JoinColumn(name="parent_ID")
#WhereJoinTable(clause=" ???? ")
private TreeObject parent;
Maybe with #Filter option of Hibernate:
#ManyToOne
#JoinColumn(name="parent_ID")
#Filter(name="parentGroup",condition=" ???? ")
private TreeObject parent;
Of course, another solution is to change the InheritanceType.TABLE_PER_CLASS to only have one big table and therefore the union will not appear in the query. But the database will be very hard to read, and I want to avoid it.
The question is: exists any way to improve the Hibernate performance for retrieve all children of a TreeObject?
A ManyToOne annotation is by default EAGER and I think you don't really need the parent of the objects you are loading directly (via primary key), right?
You could change the association like this:
#ManyToOne(fetch = FetchType.LAZY)
private TreeObject parent;
This should at least remove the last join with the unions.
But due to the nature of your model (it's a recursion) you will never be able to select an entire object graph without a native query, because JPA simply doesn't support it. Even a native query may not be the best solution and I think you should consider using a document store which enables you to store/load entire graphs in one operation.
I have following table structure
TABLE1
T1_ID
T1_Col1
TABLE2
T1_ID
T3_ID
TABLE3
T3_ID
T3_COL1
Table1 and Table3 are joined by a middle table which is Table2. Now I have only T1_ID and I want to fetch all the rows from Table3 which are associated with T1_ID. A simple SQL query would be
select T1.*, T3.*
from TABLE1 T1, TABLE T2, TABLE3 T3
where T1.T1_ID = T2.T1_ID
and T2.T3_ID = T3.T3_ID
So how can i do this in hibernate/jpa ... I have yet to write my entity classes for Table1, Table2, Table3. I want to execute this query as part of Table1, so that i can write a method say entity.fetchAssociatedTable3(). The easiest approach i can think of is in fetchAssociatedTable3 i can put custom queries like the one i mentioned above. But since i am using hibernate/jpa I want to know if there is a better way to do this.
UPDATE
Apparently, my question isn't clear enough. I want to do something as user Dragan Bozanovic mentioned. However, What i want to know that
How would i write Table1 entity ? I mean what annotations i would put on the columns etc which will make hibernate/jpa understand that this column is related to Table3 column via Table2
I guess if question 1 is answered, then it would be difficult to write getEntity3s() method. But if (for a noob) there is something that I need to know, I would appreciate.
Assuming that you will have a many-to-many association between Entity1 (mapped to TABLE1) and Entity3 (mapped to TABLE3), you can either:
1) read the Entity1 by id and get all of the Entity3s from it:
Entity1 entity1 = entityManager.find(Entity1.class, entity1Id);
Collection<Entity3> entity3s = entity1.getEntity3s();
2) or, execute the JPQL query to get Entity3s without loading the Entity1 instance:
select distinct e3 from Entity3 e3 join Entity1 e1 where e1.id = :entity1Id
First thing you would need do is to stop thinking in terms of tables when using ORM tool (Hibernate/JPA). You model your classes and their relations and let the ORM tool help with the mappings declaratively. Your join table just is serving here for creating relation many to many relation between two entities. So in terms of classes you would have only Entity1 and Entity3. You would not be creating a class representing join table (unless of course you want it to have other attributes other than foreign keys in which case it would qualify to be a Entity class in it's own right). And then you can use either method suggested by #Dragan i.e., loading by primary key or using explicit HQL/JPQL.
I attach one image of my problem:
In Testing jFrame jTextField I will insert customer id then after pressing ok button query will select and collect information related to that customer.Then it will show in the jTableModel.
I attach my database image.
Error is "SQL code cannot be executed".
You can do like this (without Join):
SELECT papers.paper_list,papers_rate.monday,papers_rate.tuesday,
papers_rate.wednesday,papers_rate.thrsday,papers_rate.friday,
papers_rate.saturday,papers_rate.sunday,magzines.magzine_name,magzines_rate.rate
FROM papers,papers_rate,magzines,magzines_rate
WHERE example_table.customer_id = ? AND other conditions"
This syntax is, in effect, a simple INNER JOIN. Some databases treat it exactly the same as an explicit JOIN. The WHERE clause tells the database which fields to correlate, and it returns results as if the tables listed were combined into a single table based on the provided conditions.(http://www.techrepublic.com/article/sql-basics-query-multiple-tables/)
You need to join the tables properly.
Like this:
SELECT
paper_list,monday,tuesday,wednesday,thrsday,friday,saturday,sunday,magzine_name,rate
FROM papers
LEFT JOIN papers_rate
ON papers_rate.paperId = papers.id
LEFT JOIN magzines
ON magzines.paperId = papers.id
LEFT JOIN magzines_rate
ON magzines_rate.magazineId = magzines.id
WHERE customer_id = ?"
If you do an inner join, all your results will vanish if you don't have a magazine_rate for example...
And check your spelling.
You're writing thrsday instead of thursday and magzine instead of magazine...
PS: And where does customer_id come from ?
Use joins to select multiple column from multiple tables. Refer this to get an understanding about the join and for join examples.
Note: There should be a common field between two tables to perform join operation
If the tables are related you must use JOIN: let's see an example (I don't know your tables fields, so I'll invent a custom example). Think about person and pet tables; the person tables could contain these fields:
Person (personID, first_name, last_name, age)
the pet table could contain these other fields:
Pet (petID, name, age, personID)
The personID field in the pet table identifies the owner of the pet. It is a simple 1:N relation. To select some values from these two tables you must do something like:
SELECT Person.first_name, Person.last_name, Pet.name
FROM Person INNER JOIN Pet ON
Person.personID = Pet.personID
WHERE Person.age > 30
This is just an example, clearly. And the INNER JOIN is just a join type (there are several join methods). Here you can find some documentation concerning these issues.
You need to either use a Join clause (... FROM papers JOIN papers_rate ON papers.[id_column] = papers_rate.[foreign_key]) or use an equi-join (replace JOIN...ON clause with a condition in the WHERE clause) (... FROM papers,papers_rate WHERE papers.[id] == papers_rate.[foreign_key])
Could you please post the schema of your tables?
I have an entity, say
#Entity
public class Category {
private String name;
private int usersInCategory;
}
I want to fill usersInCategory field with some aggregating SQL query (select count(*) ... group by ...).
How can I do this in Hibernate?
Use a Formula (this is an Hibernate specific annotation). From the documentation:
2.4.3.1. Formula
Sometimes, you want the Database to do
some computation for you rather than
in the JVM, you might also create some
kind of virtual column. You can use a
SQL fragment (aka formula) instead of
mapping a property into a column. This
kind of property is read only (its
value is calculated by your formula
fragment).
#Formula("obj_length * obj_height * obj_width")
public long getObjectVolume()
The SQL fragment can be as complex as
you want and even include subselects.
As the documentation writes, the SQL fragment can be pretty complex and can reference the owning entity like in the example below (the non aliased id column in the o.customer_id=id part of the where clause references the owning entity column):
#Formula("(select coalesce(extract ('day' from age(max(o.creation_date))), 9999) from Orders o where o.customer_id = id)")
private int daysSinceLastOrder;
See also
Hibernate Derived Properties - Performance and Portability
Another way which has been figured out to do that is following query for retreiving:
select c, (select count(i) from Item i where c = i.category) from Category c
This will produce list of tuples (Categroy, items_count). The initial task to init entity's field won't be accomplished that way, but that will be more efficient when you don't need to have that field always.