Is it possible to use an Array object as a parameter in Spring Repository #Query annotation? - spring

Is it possible to use an Array object as a parameter in Spring Repository #Query annotation?
I'm trying to retrieve all rows in a table whose column node is present in an String array. Is it possible to do it at a time using the #Query annotation in Spring repository?
Here is my Location Entity:
#Entity
#Table(name = "LOCATIONS")
public class Location extends Measurement{
private String latitude;
private String nsIndicator;
private String longitude;
private String ewIndicator;
#ManyToOne
#JoinColumn(name="node")
private Node node;
}
Where node references the Node class, and it is mapped in the database as a BIGINT.
I have a repository like this:
public interface LocationRepository extends CrudRepository<Location, Long>{
#Query(value=
"SELECT l1.node, l1.id, l1.latitude, l1.longitude " +
"FROM LOCATIONS l1 WHERE l1.node IN (:ids)",
nativeQuery=true)
List<Location> findMeasureByIds(#Param("ids") String[] ids);
}
There you can see the query that I'm trying to execute, but it's not working. I don't know if it's possible to use an array there, or parameters must be just Strings and/or Integers, I couldn't find it anywhere.
I've tried several combinations like using a simple String with the right format or a long array.. but nothing has worked so far.
Thanks in advance.
SOLUTION:
#Query(value="SELECT * FROM LOCATIONS l1 " +
"INNER JOIN (SELECT node, MAX(id) AS id FROM LOCATIONS GROUP BY node) l2 " +
"ON l1.node = l2.node AND l1.id = l2.id " +
"WHERE l1.node IN :ids", nativeQuery=true)
List<Location> findLastLocationByIds(#Param("ids") Set<Long> ids);
I've added more functionality to the query because I needed to retrieve the last row inserted for each node identifier. So there's the MAX function and the INNER JOIN to do that work.

Use a collection instead of an array (Set<String>), and make sure it's not empty (otherwise the query will be invalid.
Also, there's no reason to use a native query for that, and you shouldn't have parentheses around the parameter:
#Query("SELECT l1 FROM Location l1 WHERE l1.node.id IN :ids")
List<Location> findLocationsByNodeIds(#Param("ids") Set<String> ids);

Related

Distinct on specific column using JPA Specification

I'm trying to write the following query using JPA Specification.
select distinct name from hcp where area = 'Dhaka';
select distinct name from hcp where area = 'Dhaka';
The hcp entity looks as following
#Entity
public class HCP implements Serializable {
#Id
#Column
private String id;
#Column
private String name;
#Column
private String area;
}
The table would look like this
I tried to convert the above query using jpa-specification as following. It's selecting 'name' field and I've set distinct as true
List<HCP> result = hcpRepository.findAll(new Specification<HCP>() {
#Override
public Predicate toPredicate(Root<HCP> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
query.select(root.get("name")).distinct(true);
return criteriaBuilder.equal(root.get("area"), "Dhaka");
}
});
But it's not working. It's only applying district on the field that has #Id annotation. I want to apply distinct on area field.
Generated Hibernate Query looks like this:
select
distinct hcp0_.id as id1_0_,
hcp0_.area as area2_0_,
hcp0_.name as name3_0_
from
hcp hcp0_
where
hcp0_.area=?
How can I get generated query like the following?
select
distinct hcp0_.name as name3_0_
from
hcp hcp0_
where
hcp0_.area= 'Dhaka'
The desired result will list distinct names based on area.
How can I apply distinct on a specific field using JPA Specification?
Desired query and output:
You can't use Specifications because you want to return a List of Strings.
So you could use JPQL
#Query("select distinct h.name from Hcp h where area = 'Dhaka'")
List<String> findDistinctName();

Spring-boot jpa how to find entity with max value

Lets tell I have two tables.
CREATE TABLE user (ID int AUTO_INCREMENT,PRIMARY KEY (ID));
CREATE TABLE points (ID int AUTO_INCREMENT, user_id int, points int,PRIMARY KEY (ID));
How can I use spring-boot jpa to request user and max points like this?
select u.ID,max(p.points) from user u, points p where u.id=p.user_id
Or any alternatives to solve this kind of problems?
Assuming you have a Repository of User:
public class User {
private int id;
private List<Point> points;
...
}
With a relationship to the Points object:
public class Point {
private int id;
private User User;
private int points;
...
}
I haven't tested, but you should be able to do:
User findFirstByIdOrderByPointPointsDesc(int userId)
Similar to example 18 in the docs.
The only problem you have, regardless of the query or Spring Data, is if you have two users with the same point values. If you need more logic around tie-breaking, it might be more worth it to write a #Query (with your query, plus the extra tie-breaking logic) or a #NativeQuery.
I usually create a class to hold result such as
public class Result {
private User user;
private int votes;
// getters and setters
}
And write a custom query in the repository to fetch the data
#Query(value = "SELECT new com.package.Result (u, MAX (p.points) )
FROM user u
JOIN points p
ON u.id = p.user_id
GROUP BY u")
List<Result> getPointsPerUser();
Replace com.package.Result with appropriate path to the Result class.
Below method can be written in Repo and used as Transaction as in dao layer, which will be accessible from service layer.
#Query(value = "SELECT max(transactionId) FROM TransactionPayloadInfo")
int getMaxTransactionId();
create a model of data.
public class Data {
private int id;
private int maxPoints;
// getters and setters method
}
And write your query like this for getting model of Data.
#Query(select packagename.Data(u.ID,max(p.points) ) from user u, points p where u.id=p.user_id)
Data findUserWithMaxVots();

Spring Data JPA Projection with select distinct

I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();

Sort by joined table's field Spring JPA

I have two entity classes Request,User:
//Ommiting some annotations for brevity
public class User{
private Long id;
private String name;
private Integer age;
}
public class Request{
private Long id;
private String message;
private Date createTime;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
}
I can sort request list by create time :
Sort = new Sort(Direction.ASC,"createTime");
Is there a possible way to sort request list by User's name? Like:
Sort = new Sort(Direction.ASC,"User.name");
Yes. new Sort(Direction.ASC,"user.name"); should work just fine.
Spring Data JPA will left outer join the User to the Request and order by the joined column's name resulting in SQL like this:
select
id, message, createTime
from
Request r
left outer join User u on u.id = r.user_id
order by
u.name asc
This works great on one-to-one and, like you have here, many-to-one relationships but because a left outer join is employed to implement the order by clause, if the joined entity represents a many relationship (like in a one-to-many), the Sort may result in SQL in which duplicate records are returned. This is the case because the Sort parameter will always result in a new left outer join even if the entity being joined is already joined in the query!
Edit Incidentally, there is an open ticket concerning this issue: https://jira.spring.io/browse/DATAJPA-776

Select fews columns (DTO) with specification JPA

I am using spring-data-jpa version 1.5.1.RELEASE .
My domain is :
public class MyDomain{
....
....
private String prop1;
private String prop2;
......
......
}
My JPA Specification is:
public final class MyDomainSpecs {
public static Specification<MyDomain> search(final String prop1,final String prop2) {
return new Specification<MyDomain>() {
public Predicate toPredicate(Root<MyDomain> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// Some tests if prop1 exist .....
Predicate predicate1 = cb.equal(root.get("prop1"), prop1);
Predicate predicate2 = cb.equal(root.get("prop2"), prop2);
return cb.and(predicate1, predicate2);
}
};
}
}
My Repository :
public interface MyDomainRepository extends JpaRepository<MyDomain, Long>, JpaSpecificationExecutor<MyDomain> {
List<MyDomain> findAll(Specification<MyDomain> spec);
}
All is Working .
But my need (For performance DB tunning) is to not return and select all fields of MyDomain from DB .
I need to select only for example tree properties (prop1, prop2, prop3) , idealy in a DTO Object .
I don't want to convert My List<MyDomain> to List<MyDto> because i am tunning DB request .
So , I don't find any way to do that with spring-data-Jpa and Specification .
Any Idea ?
Thanks
This is not possible as for now. There is a ticket for this but no idea if it will be ever implmented: https://jira.spring.io/browse/DATAJPA-51
Create a special version of MyDomain (e.g. MyDomainSummary or LightMyDomain) that only includes the fields you want to map.
Basic example
Borrowed from the excellent JPA WikiBook.
Assume a JPA entity (i.e. domain class) like so:
#Entity
#Table(name="EMPLOYEE")
public class BasicEmployee {
#Column(name="ID")
private long id;
#Column(name="F_NAME")
private String firstName;
#Column(name="L_NAME")
private String lastName;
// Any un-mapped field will be automatically mapped as basic and column name defaulted.
private BigDecimal salary;
}
The SQL query generated will be similar to
SELECT ID, F_NAME, L_NAME, SALARY FROM EMPLOYEE
if no conditions (where clause) are defined. So, to generalize the basic case one can say that the number of queried columns is equal to the number of mapped fields in your entity. Therefore, the fewer fields your entity, the fewer columns included in the SQL query.
You can have an Employee entity with e.g. 20 fields and a BasicEmployee as above with only 4 fields. Then you create different repositories or different repository methods for both.
Performance considerations
However, I seriously doubt you'll see noticeable performance improvements unless the fields you want to omit represent relationships to other entities. Before you start tweaking here log the SQL that is currently issued against the data base, then remove the columns you want to omit from that SQL, run it again and analyze what you gained.

Resources