Spring: How to get data from two table in JPA - java

In the following code I want to get the data from Order table and also from User table How can I modify my query so that I can achieve this ? user_id is foreign key in order table
public interface OrderRepository extends JpaRepository<Order, Long> {
#Query("Select o from Order o where o.customer.id= :customerId and o.orderStatus='DELIVERED'")
List<Order> orderHistory(#Param("customerId") long customerId);
}

To fetch the Customer with Order, do the join fetch.
The JOIN FETCH expression is not a regular JOIN and it does not define a JOIN variable. Its only purpose is specifying related objects that should be fetched from the database with the query results on the same round trip. Using this query improves the efficiency of iteration over the result Country objects because it eliminates the need for retrieving the associated Capital objects separately.
http://www.objectdb.com/java/jpa/query/jpql/from
public interface OrderRepository extends JpaRepository<Order, Long> {
#Query("Select o from Order o inner join fetch o.customer as customer left join fetch o.user as user where customer.id= :customerId and o.orderStatus='DELIVERED'")
List<Order> orderHistory(#Param("customerId") long customerId);
}

You want to put customerId and order fields into param?
I think list the order fields in params is enough.
of course sql statement must be right.

Related

Order By in JPQL query on a #ElementCollection annotated map

In my #Entity annotated Course class, I have the following #ElementCollection annotated map:
#ElementCollection
private Map<Student, Double> courseStudAndAvgStudGrade;
In the example above, Student is another #Entity annotated class and the value is the average grade from a Course for each Student. I'm trying to write a query in JPQL that would retrieve all the entries of this map and sort by entry value in descending order.
So far, I have the following:
TypedQuery<Tuple> query =
em.createQuery("SELECT KEY(map), VALUE(map) "
+ "FROM Course c JOIN c.courseStudAndAvgStudGrade map WHERE c.id = :id", Tuple.class);
This retrieves the values correctly in a Tuple for the desired Course, however adding ORDER BY VALUE(map) DESC to the JPQL query results in a java.sql.SQLException: Subquery returns more than 1 row.
Is it possible to do ORDER BY on a map in JPQL?
JPA/Hibernate does not require the VALUE qualifier i.e. it's actually just there for completeness. Try the following query:
em.createQuery("SELECT KEY(map), map FROM Course c JOIN c.courseStudAndAvgStudGrade map WHERE c.id = :id ORDER BY map DESC", Tuple.class);

What is the LIMIT clause alternative in JPQL?

I'm working with PostgreSQL query implementing in JPQL.
This is a sample native psql query which works fine,
SELECT * FROM students ORDER BY id DESC LIMIT 1;
The same query in JPQL doesnt work,
#Query("SELECT s FROM Students s ORDER BY s.id DESC LIMIT 1")
Students getLastStudentDetails();
seems like LIMIT clause doesn't work in JPQL.
According to JPA documentation we can use setMaxResults/setFirstResult, Can anyone tell me how can I use that in my above query?
You are using JPQL which doesn't support limiting results like this. When using native JPQL you should use setMaxResults to limit the results.
However you are using Spring Data JPA which basically makes it pretty easy to do. See here in the reference guide on how to limit results based on a query. In your case the following, find method would do exactly what you want.
findFirstByOrderById();
You could also use a Pageable argument with your query instead of a LIMIT clause.
#Query("SELECT s FROM Students s ORDER BY s.id DESC")
List<Students> getLastStudentDetails(Pageable pageable);
Then in your calling code do something like this (as explained here in the reference guide).
getLastStudentDetails(PageRequest.of(0,1));
Both should yield the same result, without needing to resort to plain SQL.
As stated in the comments, JPQL does not support the LIMIT keyword.
You can achieve that using the setMaxResults but if what you want is just a single item, then use the getSingleResult - it throws an exception if no item is found.
So, your query would be something like:
TypedQuery<Student> query = entityManager.createQuery("SELECT s FROM Students s ORDER BY s.id DESC", Student.class);
query.setMaxResults(1);
If you want to set a specific start offset, use query.setFirstResult(initPosition); too
Hello for fetching single row and using LIMIT in jpql we can tell the jpql if it's a native query.
( using - nativeQuery=true )
Below is the use
#Query("SELECT s FROM Students s ORDER BY s.id DESC LIMIT 1", nativeQuery=true)
Students getLastStudentDetails();
You can not use Limit in HQL because Limit is database vendor dependent so Hibernate doesn't allow it through HQL query.
A way you can implement is using a subquery:
#Query("FROM Students st WHERE st.id = (SELECT max(s.id) FROM Students s)")
Students getLastStudentDetails();
The correct way is to write your JPA interface method like this
public interface MyRepository extends PagingAndSortingRepository<EntityClass, KeyClass> {
List<EntityClass> findTop100ByOrderByLastModifiedDesc();
}
In the method name, "100" denotes how many rows you want which you would have otherwise put in the limit clause. also "LastModified" is the column which you want to sort by.
PagingAndSortingRepository or CrudRepository, both will work for this.
For the sake of completeness, OP's interface method would be
List<Students> findTop1ByIdDesc();
JPQL does not allow to add the limit keyword to the query generated by the HQL. You would get the following exception.
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token:
LIMIT near line 1
But don't worry there is an alternative to use the limit keyword in the query generated by the HQL by using the following steps.
Sort.by(sortBy).descending() // fetch the records in descending order
pageSize = 1 // fetch the first record from the descending order result set.
Refer the following service class
Service:
#Autowired
StudentRepository repository;
public List<Student> getLastStudentDetails(Integer pageNo, Integer pageSize, String sortBy)
{
Integer pageNo = 0;
Integer pageSize = 1;
String sortBy = "id";
Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).descending());
Slice<Student> pagedResult = repository.findLastStudent(paging);
return pagedResult.getContent();
}
Your repository interface should implement the PagingAndSortingRepository
Repository:
public interface StudentRepository extends JpaRepository<Student,Long>, PagingAndSortingRepository<Student,Long>{
#Query("select student from Student student")
Slice<Student> findLastStudent(Pageable paging);
}
This will add the limit keyword to you query which you can see in the console. Hope this helps.
Hardcode the pagination(new PageRequest(0, 1)) to achieve fetch only one record.
#QueryHints({ #QueryHint(name = "org.hibernate.cacheable", value = "true") })
#Query("select * from a_table order by a_table_column desc")
List<String> getStringValue(Pageable pageable);
you have to pass new PageRequest(0, 1)to fetch records and from the list fetch the first record.
Here a Top Ten Service (it's a useful example)
REPOSITORY
(In the Query, I parse the score entity to ScoreTo ( DTO class) by a constructor)
#Repository
public interface ScoreRepository extends JpaRepository<Scores, UUID> {
#Query("SELECT new com.example.parameters.model.to.ScoreTo(u.scoreId , u.level, u.userEmail, u.scoreLearningPoints, u.scoreExperiencePoints, u.scoreCommunityPoints, u.scoreTeamworkPoints, u.scoreCommunicationPoints, u.scoreTotalPoints) FROM Scores u "+
"order by u.scoreTotalPoints desc")
List<ScoreTo> findTopScore(Pageable pageable);
}
SERVICE
#Service
public class ScoreService {
#Autowired
private ScoreRepository scoreRepository;
public List<ScoreTo> getTopScores(){
return scoreRepository.findTopScore(PageRequest.of(0,10));
}
}
You can use something like this:
#Repository
public interface ICustomerMasterRepository extends CrudRepository<CustomerMaster, String>
{
#Query(value = "SELECT max(c.customer_id) FROM CustomerMaster c ")
public String getMaxId();
}
As your query is simple, you can use the solution of the accepted answer, naming your query findFirstByOrderById();
But if your query is more complicated, I also found this way without need to use a native query:
#Query("SELECT MAX(s) FROM Students s ORDER BY s.id DESC")
Students getLastStudentDetails();
Here a practical example where the named query method cannot be used.

JPA: Join Fetch results to NULL on empty many side

I have a one to many relationship between User and GameMap. One user can have many maps.
User class:
// LAZY LOADED
#OneToMany(cascade = CascadeType.ALL, mappedBy = "creater")
private final List<GameMap> maps = new ArrayList<>();
However, sometimes I need to eager load the maps. To avoid the LazyInitializationException after closing Session, I have two variants of retrieving Users.
User Repository:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findById( Long id );
#Query("SELECT u FROM User u JOIN FETCH u.maps WHERE u.id = (:id)")
public User findByIdEagerFetch( #Param("id") Long id );
}
Problem:
However, the JPQL JOIN FETCH variant to eager load in one go the user and his maps returns a NULL user if there are no maps for this user in the table.
Question:
How can I rewrite the JPQL statement in order to retrieve the user and optionally(!) all his maps but if there are no maps, than thats okay, but dont return a NULL user.
A FETCH JOIN actually will resolve to an inner join in SQL. This means that any records/entities in the User table which have no maps will be removed from the result set. You need the LEFT keyword on your FETCH JOIN to get all the results, even those without a map.
#Query("SELECT u FROM User u LEFT JOIN FETCH u.maps WHERE u.id = (:id)")
public User findByIdEagerFetch( #Param("id") Long id );

Spring Data JPA : How to write subquery having IN operator

I am a new to Spring Data JPA, wanted to know how do I write the following subquery:
select o from Owner o where o.ownerId IN (Select c.ownerId from Cars c)
Here Owner is one entity class and Cars is another entity class and I'll be having two repositories one as OwnerRepository and the other as CarRepository, both extending JPARepository.
Help needed in writing this sort of custom queries with IN operator.
Thanks in advance.
You could always just use your query as a custom query:
#Query(value = "select o from Owner o where o.ownerId IN (Select c.ownerId from Cars c")
Owner getOwner();
If you need to pass variables to the query, use the #Param tag, like so:
#Query(value = "SELECT * FROM Owner o WHERE o.ownerId = :id")
Owner getOwnerWithId(#Param("id") Long id);

How to create a JPA query with LEFT OUTER JOIN

I am starting to learn JPA, and have implemented an example with JPA query, based on the following native SQL that I tested in SQL Server:
SELECT f.StudentID, f.Name, f.Age, f.Class1, f.Class2
FROM Student f
LEFT OUTER JOIN ClassTbl s ON s.ClassID = f.Class1 OR s.ClassID = f.Class2
WHERE s.ClassName = 'abc'
From the above SQL I have constructed the following JPQL query:
SELECT f FROM Student f LEFT JOIN f.Class1 s;
As you can see, I still lack the condition OR s.ClassID = f.Class2 from my original query. My question is, how can I put it into my JPQL?
Write this;
SELECT f from Student f LEFT JOIN f.classTbls s WHERE s.ClassName = 'abc'
Because your Student entity has One To Many relationship with ClassTbl entity.
If you have entities A and B without any relation between them and there is strictly 0 or 1 B for each A, you could do:
select a, (select b from B b where b.joinProperty = a.joinProperty) from A a
This would give you an Object[]{a,b} for a single result or List<Object[]{a,b}> for multiple results.
Normally the ON clause comes from the mapping's join columns, but the JPA 2.1 draft allows for additional conditions in a new ON clause.
See,
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#ON
Please see :
public interface YourDBRepository extends JpaRepository<Employee, Long> {
#Query("select new com.mypackage.myDTO(dep.empCode, dep.empName, em.EmployeeCode, em.EmployeeName) \n" +
"from Department dep\n" +
"left join Employee em\n" +
"on dep.DepartmentCode = em.DepartmentCode") // this is JPQL so use classnames
List<myDTO> getDeptEmployeeList();
}
You can also use CrudRepository and include #JoinColumn with FK table class in PK table class and have List return list and then do find operation to achieve the same.
In Department entity class:
#OneToMany
#Fetch(FetchMode.JOIN)
#JoinColumn(name="DEPT_CODE")
private List<Employee> employees;
CriteriaBuilder is yet another option.

Categories