spring data jpa findAll() not working properly - spring

I am having below classes
#Entity
#Table(name = "USR_E_GROUPS")
public class GroupEntity {
#Id
#Column(name = "UIDUSERGROUP")
#GenericGenerator(name = "generator", strategy = "uuid2")
#GeneratedValue(generator = "generator")
private String id;
.........
#OneToMany(mappedBy = "group", cascade = CascadeType.PERSIST)
private List<UserGroupEntity> users;
same is for UserGroupEntity
now if I use groupRepoository.findAll()
It's is firing select query for every Group and inside different select query for UserGroupEntity. so it's taking too much time.
I want to make it to fire select with join so it will be a single query.

This is probably an n + 1 issue.
From the docs
By default, Hibernate3 uses lazy select fetching for collections and
lazy proxy fetching for single-valued associations. These defaults
make sense for most associations in the majority of applications.
By default the children are fetched lazily. Use JOIN FETCH to get the result in a single query.
In your GroupRepoository
#Query("SELECT g FROM GroupEntity g JOIN FETCH g.users gu")
List<GroupEntity> findAllEager();

Related

Order by #oneToMany field using JPA Specification

Consider the entities below -
#Entity
public class Employee {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Phone> phones; //contains both "active" & "inactive" phones
}
#Entity
public class Phone {
#Id
#GeneratedValue
private long id;
private boolean active;
private String number;
#ManyToOne(fetch = FetchType.LAZY)
private Employee employee;
}
I need to pull all the employees and sort them depending on the count of "active" phones they have.
Please note that the employee can have active as well as inactive phones. So the query I am trying to achieve is
ORDER BY (SELECT
COUNT(phone4_.employee_id)
FROM
phone phone4_
WHERE
employee4_.id = phone4_.employee_id
AND phone4_.active = true
) DESC
I am stuck with specification here because of some reason and below is the code I have used -
List<Order> orders = new ArrayList<>();
orders.add(cb.desc(cb.size(employee.get("phones"))));
cq.orderBy(orders);
When I run the code the query that's get generated is
ORDER BY (SELECT
COUNT(phone4_.employee_id)
FROM
phone phone4_
WHERE
employee4_.id = phone4_.employee_id) DESC
I am unable to add an extra AND condition to the logic. Please suggest
As specified in the Persistence API specification:
4.6.16 Subqueries
Subqueries may be used in the WHERE or HAVING clause.
JPA doesn't support subqueries in the order by clause, nor in the select clause.
Hibernate ORM, though, supports them in the SELECT and WHERE clauses.
So you cannot write that query and being JPA compliant.
This HQL should work though and it's covered by Hibernate ORM:
SELECT e1, (SELECT count(p)
FROM Phone p
WHERE p.active = true AND p.employee = e1) as activeCount
FROM Employee e1
ORDER BY activeCount DESC
Surprisingly, writing this query with criteria doesn't work:
CriteriaBuilder builder = ormSession.getCriteriaBuilder();
CriteriaQuery<Object> criteria = builder.createQuery();
Root<Employee> root = criteria.from( Employee.class );
Subquery<Long> activePhonesQuery = criteria.subquery( Long.class );
Root<Phone> phoneRoot = activePhonesQuery.from( Phone.class );
Subquery<Long> phonesCount = activePhonesQuery
.select( builder.count( phoneRoot ) )
.where( builder.and( builder.isTrue( phoneRoot.get( "active" ) ), builder.equal( phoneRoot.get( "employee" ), root ) ) );
criteria.multiselect( root, phonesCount )
.orderBy( builder.desc( phonesCount ) );
The reason is that, Hibernate ORM tries to expand the subquery in the order by clause instead to refer to an alias. And as I mentioned before, this is not supported.
I think the HQL is the easiest option if you don't want to use native queries.

#Batchsize annotation not working for OneToMany

I have following classes and on annotating #BatchSize annotation it is not working and I am getting n+1 select query.
Class Shipment{
#OneToMany(fetch = FetchType.LAZY, mappedBy = order.shipment, cascade = CascadeType.ALL,
orphanRemoval = true)
#BatchSize(size=20)
Set<Orders> orders = new Hashset(); <---- Batch size annotation not working
}
Order.class
class Order{
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "item_fk")
Item item;
#ToString.Exclude
#ManyToOne
#JoinColumn(name = "shipment_fk")
Shipment shipment; }
Item.class
class Item{
String id;
String name;
}
What is mistake in implementation that i am getting n+1 queries?
Try to use List<Orders> instead of Set<Orders>.
Please note as it's mentioned in the documentation:
However, although #BatchSize is better than running into an N+1 query issue, most of the time, a DTO projection or a JOIN FETCH is a much better alternative since it allows you to fetch all the required data with a single query.
Your N + 1 query issue is due to the fact that you do eager fetching of Item in Order. Change to LAZY there and you should be good to go.

Join 2 tables using Spring JPA

I have two entities(Invoice and InvoiceItems) without adding any relationship.
Invoice
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invoiceID;
#Column(name="code")
private String code;
//other columns
}
Invoice Items
public class InvoiceItems {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long invItemID;
#Column(name="invoice_id")
private Integer invoiceId;
//other columns
}
Can I join these entities and get data without adding relationship using JPA?
If it isn't possible how to join 2 entities using JPQL or Native query?
If your data is valid then using native query you can do that
#Query(nativeQuery = true, "select * from Invoice i join InvoiceItems im on i.id = im.invoice_id")
public List<Invoice> findData();
But that is not a good way join without relation using JPA.
Yes, you can join these entities and get data without adding relationship using JPA, but it's a little bit losing the purpose of using JPA.
You need to create a java class first, which will be the returning data object from the DB. After that you can use entityManager's createNamedQuery method to get the result.
createNamedQuery(String sqlString, ResultClass.Class)
sqlString may be something like:
SELECT INV.INVOICE_ID
INV.CODE
INV_ITEMS.INV_ITEM_ID
FROM INVOICE INV
JOIN INVOICE_ITEMS INV_ITEMS
ON INV.INVOICE_ID = INV_ITEMS.INVOICE_ID;
And the corresponding ResultClass:
public class ResultClass {
private Long invoiceID;
private String code;
private Long invItemID;
// other columns
}
Or you can even use RowMapper to map the object all by yourself for more flexibility by using JdbcTemplate with query() method.

Do we have to explitly use Left Join query while using JPA in SpringBoot?

I am working in SpringBoot and I am using JPA repositories for DB access. I have two entities say
Class A{
#Id
private String primarykeyColumnA;
#OneToMany(mappedBy="campaign",fetch = FetchType.EAGER,cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
private Set<B> b;
....
}
Class B{
#Id
private Long primaryKeyColB;
#ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE })
#JoinColumn(name = "primarykeyColumnA")
private A a;
}
So I have to join these two tables and it is one to many(for one A, there will be many B) relationship. It works fine. But I want to make sure I use Left join. Is it normally Left join or should I specifically use a query?
Some help is greatly appreciated.
I think it depends on the JPA provider you use. In the case of Hibernate, with the fetch=EAGER attribute, Hibernate will not perform what you are expecting when you load some A entities.
Instead the following steps will be performed:
Perform a SQL query to load the A entities, without loading the B entities.
For each A entity, perform a SQL query to load its related B entities.
If the B entities are not in the cache, you will have the 1+n queries problem.
If you want to load the A entities with the related B entities with a single SQL query, you have to use a query using the join fetch clause (left, inner, ... depending on your case).

JPA Criteria API query with Join and inheritance

I am trying to write Query using JPA Criteria API. I have the following classes:
Class Booking {
#ForeignKey(name = "BVisit_ID_FK")
private List<BVisit> BVisits = new LinkedList<>();
//other properties
...
}
Class Visit{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
}
Class VisitSpecial extends Visit{
#Column(name = "ARRIVAL_TIME", nullable = false)
private Date arrivalTime;
#Column(name = "DEPARTURE_TIME", nullable = false)
private Date departureTime;
//other properties...
}`
How can I write query using JPA Criteria Api (and metamodels) that will find all bookings that have
visits with date value(parameter) that is between min arrival time, an max departure time for its booking visits.
I use org.springframework.data.jpa.domain.Specification
The query should look like something like this:
SELECT Booking
from Booking B, Visit V, VisitSpecial VS
where Visit.bookingId = Booking.id and Visit.id = VisitSpecial.id
and VisitSpecial.arrivalTime = (SELECT MIN(VisitSpecial.arrivalTime) from VisitSpecial VS1 WHERE V.id = VS1.id)
and VisitSpecial.arrivalTime <= :date
and VisitSpecial.departureTime = (SELECT MAX(VisitSpecial.departureTime) from VisitSpecial VS1 Where V.id = VS1.id)
and VisitSpecial.departureTime >= :date
See my answer here for dealing with InheritanceType.JOINED and other inheritance strategies through JPA Criteria Subquery.
Downcasting with treat may work, but I do not recommend it for complex queries as it may produce inefficient queries (redundant joins) or not work at all based on your JPA provider (Hibernate, Eclipselink, etc.).

Resources