Entity relation: Transaction(#ManyToOne - eager by default) -> Account
String sql = "SELECT new com.test.Pojo(t.account, SUM(t.value)) FROM Transaction t GROUP BY t.account";
List list = entityManager.createQuery(sql).getResultList();
By default JPA using Hibernate implementation will generate 1 + n queries. The n queries are for lazy loading of the account entities.
How can I make this query eager and load everything with a single query? The sql equivalent would be something like
SELECT account.*, SUM(t.value) FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id
, a syntax that works well on PostgreSQL. From my findings Hibernate is generating a query that justifies the lazy loading.
SELECT account.id, SUM(t.value) FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id
Try marking the #ManyToOne field as lazy:
#ManyToOne(fetch = FetchType.LAZY)
private Account account;
And change your query using a JOIN FETCH of the account field to generate only one query with all you need, like this:
String sql = "SELECT new com.test.Pojo(acc, SUM(t.value)) "
+ "FROM Transaction t JOIN FETCH t.account acc GROUP BY acc";
UPDATE:
Sorry, you're right, the fetch attribute of #ManyToOne is not required because in Hibernate that is the default value. The JOIN FETCH isn't working, it's causing a QueryException: "Query specified join fetching, but the owner of the fetched association was not present".
I have tried with some other approaches, the most simple one that avoids doing n + 1 queries is to remove the creation of the Pojo object from your query and process the result list, manually creating the objects:
String hql = "SELECT acc, SUM(t.value)"
+ " FROM " + Transaction.class.getName() + " t"
+ " JOIN t.account acc"
+ " GROUP BY acc";
Query query = getEntityManager().createQuery(hql);
List<Pojo> pojoList = new ArrayList<>();
List<Object[]> list = query.getResultList();
for (Object[] result : list)
pojoList.add(new Pojo((Account)result[0], (BigDecimal)result[1]));
Well PostgreSQL (And any other SQL database too) will block you from using mentioned query: you have to group by all columns of account table, not by id. That is why Hibernate generates the query, grouping by ID of the account - That is what is intended to be, and then fetching the other parts. Because it cannot predict in general way, what else will be needed to be joined and grouped(!!!), and in general this could produce situation, when multiple entities with the same ID are fetched (just create a proper query and take a look at execution plan, this will be especially significant when you have OneToMany fields in your Account entity, or any other ManyToOne part of the Account entity) that is why Hibernate behaves this way.
Also, having accounts with mentioned IDs in First level cache, will force Hibernate to pick them up from that. Or IF they are rarely modified entities, you can put them in Second level cache, and hibernate will not make query to database, but rather pick them from Second level cache.
If you need to get those from database in single hint, but not use all the goodness of Hibernate, just go to pure JPA Approach based on Native queries, like this:
#NamedNativeQuery(
name = "Pojo.groupedInfo",
query = "SELECT account.*, SUM(t.value) as sum FROM transactions JOIN accounts on transactions.account_id = accounts.id GROUP BY account.id, account.etc ...",
resultClass = Pojo.class,
resultSetMapping = "Pojo.groupedInfo")
#SqlResultSetMapping(
name = "Pojo.groupedInfo",
classes = {
#ConstructorResult(
targetClass = Pojo.class,
columns = {
#ColumnResult(name = "sum", type = BigDecimal.class),
/*
* Mappings for Account part of entity.
*/
}
)
}
)
public class Pojo implements Serializable {
private BigDecimal sum;
/* .... */
public Pojo(BigDecimal sum, ...) {}
/* .... */
}
For sure this will work for you well, unless you will use the Account, fetched by this query in other entities. This will make Hibernate "mad" - the "entity", but not fetched by Hibernate...
Interesting, the described behaviour is as if t instances are returned from the actual query and t.account association in the first argument of Pojo constructor is actually navigated on t instances when marshalling results of the query (when creating Pojo instances from the result rows of the query). I am not sure if this is a bug or intended feature for constructor expressions.
But the following form of the query should work (no t.account navigation in the constructor expression, and no join fetch without the owner of the fetched association because it does not make sense to eagerly initialize something that is not actually returned from the query):
SELECT new com.test.Pojo(acc, SUM(t.value))
FROM Transaction t JOIN t.account acc
GROUP BY acc
EDIT
Very good observation by Ilya Dyoshin about the group by clause; I completely oversaw it here. To stay in the HQL world, you could simply preload all accounts with transactions before executing the query with grouping:
SELECT acc FROM Account acc
WHERE acc.id in (SELECT t.account.id FROM Transaction t)
Related
I'm trying to do the following query in linq, however I'm getting an exception error, though my query looks fine to me. So here is the story:
Diagram
I have a many to many relationship between the users and the organizations. A user can be a part of many organizations, and an organization can have many users.
What Im trying to query
So given a user id, i want to query all the team members (users) i have in all the organizations i belong to. So
Input: User X id (guid), and this user belongs to Organization A, and Organization B
Output:
User A, Organization A
User B, Organization A
User C, Organization B
The Actual Query
I though this would do just that
var user = db.Users.Include(q => q.UserOrganization).SingleOrDefault( q => q.Id == id.ToString());
var members = (from us in db.Users.Include(q => q.UserOrganization)
let orgs = user.UserOrganization.Select(z => z.OrganizationId)
where us.UserOrganization.Any(q => orgs.Contains(q.OrganizationId) )
select new UserResource{
id = Guid.Parse(us.Id),
email = us.Email
}
).ToArray();
My query fails on the where clause, with the error:
Processing of the LINQ expression 'AsQueryable<long>((Unhandled parameter: __Select_0))' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core
Not sure what to change in the query. Please help.
PS: I wrote the query initially in MySql as follows:
SELECT UU.`Id`, UU.`Email`, UUO.`OrganizationId`
FROM aspnetusers AS UU
LEFT JOIN userorganization AS UUO ON UUO.`UserId` = `UU`.Id
WHERE UUO.`OrganizationId` IN
(
SELECT UO.`OrganizationId` FROM aspnetusers AS U
LEFT JOIN userorganization AS UO ON UO.UserId = U.Id
WHERE u.Id = '6caa67e7-69f3-49a3-ad61-10b07d379f10'
)
AND UU.Id != '6caa67e7-69f3-49a3-ad61-10b07d379f10'
The "SingleOrDefault" always executes the Query. User is not an IQueryable.
So the let orgs = user.UserOrganization.Select(z => z.OrganizationId) cannot be translated to SQL, do your var orgs = user.UserOrganization.Select(z => z.OrganizationId) before the Query, in Plain C#. This cannot be used in SQL-Queries.
With orgs being an IList<int> it will work.
But it should be prefered to find a solution that can be solved with one query only. Here you have two.
The SingleOrDefault might be not useful, you go better without, than you have a simple IQueryable. And The "Any" can most often be realized with a simple (Inner) Join, returning only values, if you have a match between to tables. That is the Same as Where - Any - Contains
But updation works with below code snippet
Department department= departmentRepository.findOne(300L);
department.setName("rajiv");
departmentRepository.saveAndFlush(department);
but then this scenario being onetone mapping it could be a case where i would end up hitting 3 queries which include 2 select queries and one one update query .
To optimize the way i update it i'm trying to use this approach which is not getting updated thought executeUpdate() returns affected row as 1 .
There is a small mistake in your query.
You have used the = operator instead you should have used the like operator
You can also take help of #NamedNativeQueries
Example
DepartmentRepository.java
#Query(nativeQuery = true)
public List<Department> update(Long id);
Department.java
#SqlResultSetMapping(name="updateResult", columns = { #ColumnResult(name = "count")})
#NamedNativeQueries({
#NamedNativeQuery(
name = "Department.update",
query = "UPDATE departmemnt SET name like 'rajiv' WHERE id = ?",
resultSetMapping = "updateResult")
})
NativeQueries works more faster than normal Hibernate Queries
Yeah may this would help , in case of spring data i found a way to make it updatable and since they insist to use #transactional and extend the functionality of jparepository and now it hits only one line of statement
#Modifying(clearAutomatically = true)
#Transactional(readOnly=false)
#Query("update Department d set d.name =:name where d.id=:id")
int update(#Param("name")String name,#Param("id")Long id);
in case if i choose to for nativeQuery
#Modifying(clearAutomatically = true)
#Transactional(readOnly=false)
#Query(value="update Department d set d.name =:name where d.id=:id",nativeQuery=true)
int update(#Param("name")String name,#Param("id")Long id);
Hi I have a namedquery defined as below but when I execute it it retunrns me the whole object rather than just the fields that I have requested. Is there something that I am missing when I only want to return just a column of that object. Thanks in advance
#NamedQueries ({
#NamedQuery(
name="findSubmissionForSubmissionRowUniqueBankId",
query="SELECT o.submission FROM SubmissionRow o WHERE o.uniqueBankId = :uniqueBankId",
hints={#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_SIZE, value="1000"),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_EXPIRY, value="18000")
})
})
The sql that it excecutes for this query is
EJBQueryImpl(ReadObjectQuery(name="findSubmissionForSubmissionRowUniqueBankId" referenceClass=SubmissionRow sql="SELECT ID, ARCHIVE_BANK_ID, EXTERNAL_SOURCE_DETAILS,UNIQUE_BANK_ID, SUBMISSION_ID FROM FE_TEST.SUBMISSION_ROW WHERE (UNIQUE_BANK_ID = ?)"))
I have defined the join as folllows
#ManyToOne
#JoinColumn(name = "SUBMISSION_ID", referencedColumnName = "ID")
private Submission submission;
Your hints do not make sense,
#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_SIZE, value="1000"),
#QueryHint(name=QueryHints.QUERY_RESULTS_CACHE_EXPIRY, value="18000")
You seem to think you are using query caching, but are not. CACHE_USAGE does not enable query caching, but in-memory querying (searches the entire cache for the object).
To enable the query cache use, QueryHints.QUERY_RESULTS_CACHE = true.
Remove CACHE_USAGE. CACHE_USAGE in-memory querying is only supported with the whole objects, it does not support selecting parts. If you want to use in-memory querying, just query the whole object, and then access the part you want.
I have two entities: parent Customer and child Order.
Each Customer has 1,000,000 Orders for example, so it is not needed in any given time to load a Customer with all Orders but I want to have this ability to make join query on these two entities in JPA.
So because of this, I must create #OneToMany relationship for making join queries.
My question is: how to get query without making joinColumn because even in Lazy mode it is possible to load 1,000,000 objects!
I just want to get query on these object with where restrictions like native join.
If you don't want the #OneToMany relationship implicitly set in your Customer class than you don't have to. You can execute JPQL queries (in very precise manner) without the marked relationship.
Assume you have:
#Entity
public class Customer {
// all Customer-related fields WITHOUT #OneToMany relationship with Order
}
#Entity
public class Order {
#ManyToOne
private Customer owner;
}
Then if you want to get all Orders for particular Customer you can execute a simple JPQL query like that:
// Customer customer = ...
// EntityManager em = ...
String jpql = "SELECT o FROM Order o WHERE o.owner = :customer";
TypedQuery<Order> query = em.createQuery(jpql, Order.class);
query.setParameter("customer", customer);
List<Order> orders = query.getResultList();
In this way you can execute the code only when you're really sure you want to fetch Customer's orders.
I hope I've understood your problem correctly.
EclipseLink has support for QueryKeys, that allow you to define fields or relationships for querying that are not mapped. Currently there in no annotation support for query keys, but you can define them using the API and a DescriptorCustomizer.
Also you do not need the OneToMany to query on it, just use the inverse ManyToOne to query,
i.e.
Select distinct c from Customer c, Order o where o.customer = c and o.item = :item
Or,
Select distinct o.customer from Order o join o.customer c where o.customer = c and o.item = :item
I want to select a list of file references from a table by looking at which users have the rights to retrieve that file. To do this I have 3 tables, a file table, an access control table, and a users table.
I am using JPA and Criteriabuilder (because there are more tables involved and I need dynamicle create the query, I am leaving out the other tables and predicates from this question for the sake of readability).
The following code works
CriteriaBuilder queryBuilder = em.getCriteriaBuilder();
CriteriaQuery<File> queryDefinition = queryBuilder.createQuery(File.class);
Root<File> FileRoot = queryDefinition.from(File.class);
List<Predicate> predicateList = new ArrayList<Predicate>();
Predicate userPredicate = FileRoot .join("FileAccesControlCollection").join("userId").get("usersId").in(loggedInUser.getUsersId());
predicateList.add(userPredicate );
queryDefinition.where(predicateArray).distinct(true);
Query q = em.createQuery(queryDefinition);
List<Files> results = (List<Files>) q.getResultList();
For the userpredicate I want to leave out the last join to the users table because the ID that I want to filter on is already present in the FileAccesControlCollection table, and a join is a computational expensive database operation.
What I tried is to do this:
Predicate userPredicate = FileRoot .join("FileAccesControlCollection").get("usersId").in(loggedInUser.getUsersId());
But I guess because the userId value in the FileAccesControlCollection entity class is a foreignkey reference to the Users class I get the following error:
Exception Description: Object comparisons can only use the equal() or notEqual() operators. Other comparisons must be done through query keys or direct attribute level comparisons.
Is there a way, using the loggedInUser entity or its Id, to filter the files by just joining the File class to the FileAccesControlCollection class and filtering on the userId foreign key? I am kind of new to JPA and using google lead me to a lot of pages but not a clear answer for something which seems to me should be possible.
So "userId" is mapped as a OneToOne? Then you could do,
get("userId").get("id").in(...)
You could also add a QueryKey in EclipseLink using a DescriptorCustomizer for the foreign key field and then use it in the query,
get("userFk").in(...)
try this:
Predicate userPredicate = FileRoot.join(FileAccesControlCollection.class).join(Users.class).get("{id field name in class Users}").in(loggedInUser.getUsersId());
good luck.