I have a query that joins 5 tables.
Then I fill my hand-made object with the column values that I need.
What are solutions here that are wide-common to solve that problem using specific tools ? are there such tools?
I'm only beginning to learn Hibernate, so my question would be: is Hibernate the right decision for this problem?
Hibernate maps a table to a class. So, there's no difference if I would have 5 classes instead of 5 tables. It would still be difficult to join the query result into a class
Could hibernate be used to map THE QUERY into the structure (class) I would define beforehand as we do with table mapping? Or even better, can it map the query result into the meaningful fields [auto-create the class with fields] as it does with reverse-engineering?
I've been thinking about views but.. create a new view everytime we need a complex query.. too verbose.
As S.Lott asked, here is a simple version of a question:
General problem:
select A.field_a, B.field_b, C.field_c
from table_a A inner join table_b B inner join table_c C
where ...
every table contains 100 fields
query returns 3 fields, but every field belongs to the unique table
How do I solve that problem in an OO style?
Design a new object with properties corresponding to the returning values of the query.
I want to know if it is the right [and the only one possible] decision and are there any common solutions.
See also my comments.
The point of ORM is to Map Objects to Relations.
The point of ORM is -- explicitly -- not to sweat the details of a specific SQL join.
One fundamental guideline to understanding ORM is this.
SQL joins are a hack because SQL doesn't have proper navigation.
To do ORM design, we to intentionally set the SQL join considerations aside as (largely) irrelevant. Give up the old ways. It's okay, really. The SQL crutches aren't supporting us very well.
Step 1. Define the domain of discourse. The real-world objects.
Step 2. Define implementation classes that are a high-fidelity model of real-world things.
Step 3. Map the objects to relations. Here's where the hack-arounds start. SQL doesn't have a variety of collections -- it only has tables. SQL doesn't have subclasses, it only has tables. So you have to design a "good-enough" mapping between object classes and tables. Ideally, this is one-to-one. But in reality, it doesn't work out that way. Often you will have some denormalization to handle class hierarchies. Other than that, it should work out reasonably well.
Yes you have to add many-to-many association tables that have no object mapping.
Step 4. You're done. Write your application.
"But what about my query that joins 5 (or 3) tables but only takes one attribute from each table?"
What about it? One of those tables is the real object you're dealing with. The other of those 5 (or 3) tables are either part of 1-m nested collections, m-1 containers or m-m associations. That's just navigation among objects.
A 1-m nested collection is the kind of thing that SQL treats as a "result set". In ORM it will become a proper object collection.
A m-1 contain is the typical FK relationship. In ORM it's just a fetch of a related object through ordinary object navigation.
A m-m association is also an object collection. It's a strange collection because two objects are members of each other's collections, but it's just an object collection.
At no time do you design an object that matches a query. You design an object that matches the real world, map that to the database.
"What about performance?" It's only a problem when you subvert the ORM's simple mapping rules. Once in a blue moon you have to create a special-purpose view to handle really big batch-oriented joins among objects. But this is really rare. Often, rethinking your Java program's navigation patterns will improve performance.
Remember, ORM's cache your results. Each "navigation" may not be a complete "round-trip" to the database query. Some queries may be batched by the ORM for you.
There are a few options:
Create a single table mapping using <join> elements for the related tables. A join in that way will allow other tables to contribute properties to your class.
Use a database view as previously suggested.
Use a Hibernate mapping view - instead of <class name=... table=... you can use <class name=... select="select A.field_a, B.field_b, ... from A, B, ...">. It's essentially creating a view on the Hibernate side so the database doesn't have to change. The generated sql will end up looking like "select * from (select A.field_a, B.field_b from A, B, ...)
". I know that works in Oracle, DB2, and MySQL
All that is fine for selecting; if you need to do insert/update, you'll probably need to rethink your data model or your object model.
I think you could use the Criteria API in Hibernate to map the results of your join into your target class.
Related
I am using Spring Boot and Hibernate.
Some complex logic, dictated by business, needs to use various nested fields, which traverse various DB relationships (again, some are NxN, Nx1, 1xN, 1x1).
I encountered the N+1 problem, and I solved it at first with HQL, but some queries need several joins and the result sets become unmanageable.
I started working on a custom utility that collects the ids of things that need to be fetched, fetches them all at once and uses the setters to then populate the fields on the starting objects. This utility works for ManyToOne relationships, but is still inefficient with ManyToMany relationships, because it falls back in the N+1 problem when I collect the ids (as it queries the join table once per object via the getter).
How can I solve this? Has this problem really not been solved yet? Am I missing some obvious settings that solves this automagically?
EDIT:
I made a toy example with some commentary: https://github.com/marcotama/n-1-queries-example
I had faced the same situation and I had 3 ways to solve it;
increase the fetchsize for the dependent attribute so that the queries are executed in batch
write a custom query for the purpose
define entity graph relations and map accordingly to attributes
I personally preferred the 3rd option as it was convenient to do that and was cleaner with spring data JPA.
you can refer to examples from the comments from the below answers:
Spring Data JPA And NamedEntityGraphs
What is the solution for the N+1 issue in JPA and Hibernate?
Write fetch logic on your own.
E.g You have author which has book, author_devices
You can join fetch author with books. Than you can separatly fetch author_devices using repository "where author_id IN (authorsList.stream().map(author.getId())". Than you should detach author and iterate author_devices and assign it to apropriate author devices list. I think it's only adequate solution for situations where you need to join-fetch more than 1 relation.
This can seem a stupid questions for some people, but I couldn't find any information anywhere why we should use mappings (#OneToOne, #OneToMany etc) in JPA while defining entity classes. I know one the advantage is code reduction, so that we don't have to explicitly write queries in order to fetch data from relationship tables. But is there any other benefit (from code optimisation perspective at SQL side) that we have?
The reason is to load object trees or graphs.
That's the goal of object-relational mapping to fill the gap between database tables and objects.
And as you said it reduces code.
In a summary the idea of the ORM is to map tables to objects, so the developer works with the objects instead of the tables.
Tables in SQL have relationships through key columns.
in JPA these relationships are expressed via #OneToMany, #OneToOne etc.
This means that if you want to fetch a row from one table and join that with corresponding row from another table (via a relationship) the JPA implementation (mostly Hibernate) can do that for you, looking at the relationships you have defined for your entities.
You need to describe the entities relationships because it's part of the DB schema model which you are mapping to application level objects. As you mention - it saves you writing the SQL queries yourself, but that's not the main point.
The main point is that you can model/represent one domain (database tables, rows, relationships, SQL commands) as another type of domain (objects/classes, OOP paradigm, programming language commands) which completely shifts the way you work with it.
I have an application that has the following java files:
Services:
AccountService.java
UserService.java
MessageService.java
DAOs:
AccountDAO.java
UserDAO.java
MessageDAO.java
Tables:
ACCOUNTS
USERS
MESSAGES
In MessageService.java, I have a function newMessage() that has to query data from all the 3 tables.
(1) According to Spring's decoupling standards, this is how the calls should be made:
AccountDAO.java -- ACCOUNTS
/
MessageService.java -- MessageDAO.java -- MESSAGES
\
UserDAO.java -- USERS
But the problem is, this approach makes 3 DB calls.
(2) For better Performance, I would do:
MessageService.java -- MessageDAO.java -- Join ACCOUNTS, MESSAGES and USERS
But this way, it's tightly coupled and if there's a change in USERS table, I'll have to change MessageDAO.java (and all other DAOs that I have, that use the USERS table) too. That is really bad, since (in the non-hypothetical) we have a LOT of DAOs
Which approach is considered a better practice? Or is there another approach that I'm missing?
According to Spring's decoupling standards, this is how the calls should be made
This is false. There are no "decoupling standards" with Spring. Please find me a reference in the Spring documentation that tells you how you must structure your persistence layer code.
Typically, you would have one DAO for each "entity" that your application wants to operate on, but it would be foolish to take this pattern to the extreme of deconstructing a query that joins multiple tables together into three distinct queries.
If you need to have a newMessage() method that joins some tables together in a query, choose which DAO that makes the most sense in - probably the MessageDAO and write the query/method in the way that makes sense.
But there is no rule saying that you must have distinct queries for each entity and that one DAO class is not allowed to make queries that touch the tables of other entities. This is too extreme and has no benefit.
If one the other hand you are worried about the maintainability of having multiple data layer classes that are aware of all of your tables, then look into an ORM solution as parsifal mentioned to alleviate some of this work.
The alternative is to use an ORM such as Hibernate, mapping each of your tables to an entity. Then define the logical relationships between those entities. For example, a 1:M relationship between users and messages. As your tables change, your mappings will need to change, but your SQL won't (because Hibernate will generate it).
For most relationships, Hibernate is very good at creating joins to retrieve related entities in one query. You can control whether this happens; I recommend using lazy loads as a default for most relationships, and switching to eager loads as-needed.
Doing this as 3 separate queries might impact correctness if the data might change between one query and the next. Don't let (your idea of) Spring's guidelines make you write code that gets the wrong results.
It sounds like a join in SQL is the right approach.
Whichever method you follow, it's always about finding a sweet spot between decoupling and performance. It holds with even the selection of number of layers, etc.
So I guess as #mattb recommended, it's completely fine to join tables in a DAO if it makes sense in the particular context
I have an odd business requirement.
We have multiple, unrelated entity types that will need to be displayed in a unified list, with some basic information from the entity, sorted by the only field they are all guaranteed to have, DATE. These entities may or may not even be in the same database. The result set needs to be pageable.
Is there any feasible way of achieving this through Criteria, HQL or some sane means?
Normally you would let all these classes extend common base class and use polymorphic Hibernate query. From your description this doesn't seem to be feasible.
Of course if you want to go the Hibernate way, you would have to first fetch the size of each unrelated table, determine in which table do the records in requested page lie (or maybe in several ones) and manually fetch proper page. This is really cumbersome and definitely should be hidden under some deep DAO.
Looks like do only sane solution is the good old SQL with UNION and mapping native query to your domain objects. Hibernate supports native queries quite well.
In my hypothetical I have an annotated User model class. This User model also holds references to two sets:
A set of Pet objects (a Pet object is also an annotated model represented in the data layer)
A set of Food objects (a Pet object is also an annotated model represented in the data layer)
When I pull the User entity from the database (entityManager.find(User.class, id)) it will automatically fill all the User fields but it obviously wont fill the two sets.
Do I need to do entityManager.createQuery and just use a normal SQL join query then manually create the User object?
Thanks in advance
If you map your relations from User to Pet and Food using OneToMany you can chose whether to have the fields automatically collected or not.
See the API doc for javax.persistence OneToMany.
Depending on how you constructed the mapping (PK-FK or join tables etc), you may or may not get good performance with this. Having two OneToMany relations that are joined, means you may end up with a ridiculous amount of rows when you read up your user.
Mmm, No? That's probably not how you want to do it. I don't know why you say "it obviously won't fill the two sets." It's quite capable of filling in the sets for you, that's sort of the point behind using an ORM like hibernate in the first place. Your objects do what they look like they should in code and 'databasey' things are handled automatically as much as possible.
It is true that Hibernate will complain if you mark more than one collection as EAGER fetched, but it's not really clear you actually need either of them to be eager. Essentially once they are mapped, just accessing them causes the queries to be run to fill them in with data (assuming the Session is still open and so forth.) If you explain how you want it to work it would be easier to help with a solution.