How to join with array in querydsl - postgresql

I a trying to write below query in querydsl
SELECT u.id
FROM users AS u
JOIN unnest(ARRAY[2,2,1]) WITH ORDINALITY AS arr(elem, ord)
ON u.id = arr.elem
ORDER BY arr.ord;
How can I join on array if ids
I tried registering unnest function too
public class Contributor implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction("unnest",
new SQLFunctionTemplate(StandardBasicTypes.STRING, "array[?1]"));
}
}
But this is also not working, I am getting unexpected token: unnest

Well, the problem is that while PostgreSQL has support for native arrays and the unnest operator, JPA currently does not in its query language (JPQL) which Querydsl / Spring Boot have to use as an intermediary. So unfortunately, this is not supported.

Related

Spring jpa returning specific column without nativeQuery

I trying to select from database specific column only
public interface ItemRepository extends PagingAndSortingRepository<Item,String> {
#Query("SELECT i.id FROM Item i where i.id =?1")
Optional<Item> getit(String id);
}
But instead of returning Object Item it returns String (the id value).
How Can I return the object? (I can't add any constructors to it)
p.s. if I use nativeQuery it does work.
This query in JQL can be simpler.
#Query("SELECT i FROM Item i where i.id =?1")
Optional<Item> getit(String id);
Or even
#Query("FROM Item where id =?1")
Optional<Item> getit(String id);
However, since you are already using JPA, you don't need to define any method with a #Query annotation.
findById is already defined.
Optional<Item> itemOpt = itemRepository.findById(id);

Is it possible to return custom Java objects combining multiple aggregates in Spring Data JDBC?

I have multiple aggregate classes, such as Request, Scribe, Candidate, and Exam.
Sample schema:
Request (id, scribe_id, candidate_id, exam_id, status)
Scribe (id, name)
Candidate (id, name)
Exam (id, name, schedule)
As you can see, Request table has references to Scribe, Candidate, and Exam tables.
For one of the requirements, I need to return all requests based on a condition by including all the corresponding details of scribe, candidate, and exam.
For this, the query in my repository class will be similar to the following:
SELECT r.id, r.status, c.name, s.name,
e.schedule, e.name
FROM request r
JOIN candidate c ON r.candidate=c.id
JOIN scribe s ON r.scribe=s.id
JOIN exam e ON r.exam=e.id
WHERE <some-condition>
Now, is there a way to map the result of this query directly to a custom Java object and return the same in Spring Data JDBC?
I believe another alternative is to use the Spring JDBC template.
Curious, any out-of-the-box support from Spring Data JDBC?
Thanks.
I am able to return custom Java object by setting rowMapperClass value of org.springframework.data.jdbc.repository.query.Query annotation. For this need to define RowMapper for custom Java object.
Changes look similar to the following:
public class RequestResourceRowMapper implements RowMapper<RequestResource> {
#Override
public RequestResource mapRow(ResultSet resultSet, int rowNumber) throws SQLException { ... }
}
In repository class, need to set rowMapper value.
#Query(value = """
SELECT r.id, r.status, c.name, s.name,
e.schedule, e.name
FROM request r
JOIN candidate c ON r.candidate=c.id
JOIN scribe s ON r.scribe=s.id
JOIN exam e ON r.exam=e.id
WHERE <some-condition>
""",
rowMapperClass = RequestResourceRowMapper.class)
List<RequestResource> searchRequestResources(...);
This could have even been possible without using a custom row mapper as well, but in that case, you will have to assign different names to the columns across the tables. You could have defined a simple class and defined all the fields in there and for mapping the java fields with the corresponding columns in the table, you could have used the #Column attributes example:
public class RequestData {
#Column("id")
private Integer requestId;
#Column("scribe_id")
private String scribeId;
#Column("candidate_id")
private Integer candidateId;
#Column("scribe_name")
private String scribeName;
#Column("candidate_name")
private String candidateName;
#Column("exam_name")
private String examName;
#Column("exam_schedule")
private String examSchedule;
}
However, for such case, you need to have different column names across the schema's which might not be possible in your case as you have same column names in multiple schemas.

QueryDSL Left Join not mapping properly with JPA entity

QueryDSL is not working properly with JPA for left join.
I am using queryDSL version 4.2.1 and mapping the response directly to javax.persistence entity.
For Left join/Right join, the joining condition it's not working. It fetches every entity disregarding the joining condition(here it's name = "testName") when entity1.getEntity2() is being called.
Is there any other way applicable for this case to map the result after JOIN tables ?
JPAQuery<Entity1> query = new JPAQuery<>(entityManager);
query.from(table1);
query.leftJoin(table2).on(table2.id.eq(table1.id).and(table2.name.eq("testName"));
List<Entity1> list = query.fetch();
#Entity
public class Entity1{
private Integer id;
#OneToMany(mappedBy = "entity1", fetch = FetchType.LAZY)
private List<Entity2> entity2;
}
An left or right join is an outer join.
So if you use left join all records from the left (in your case table1) will be selected.
If you only want records from table1 if there are corresponding records on table2 you have to use innerJoin.
JPAQuery<Entity1> query = new JPAQuery<>(entityManager);
query.from(table1);
query.innerJoin(table2).on(table2.id.eq(table1.id).and(table2.name.eq("testName"));
List<Entity1> list = query.fetch();
Read more about the join types here:
https://www.diffen.com/difference/Inner_Join_vs_Outer_Join

How to avoiding AND condition if parameter is null in Spring Data JPA query

I am trying to get the result of one query using Spring Data JPA. Here I am sending some parameter and receiving result according to that.
My repository query is,
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByUsername(#Param("uname") String uname , #Param("pname") String pname );
And calling from controller like the following,
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public List<Users> joinResultShow()
{
return (List<Users>) userRepo.findByUsername("test_user","testRole");
}
Here we can see that if I am passing some value then only checking according to that parameter. Here I need to modify my query like if parameter is null, then not need to use AND condition in query.
How can I modify this query for avoiding AND condition if parameter is null? I am new to Spring Data JPA world.
Here are some possible options for you
1. Create multiple methods in your repository like
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByusernamewithRole(#Param("uname") String uname , #Param("pname") String pname );
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname")
List<Users> findByUsernameWithoutRole(#Param("uname") String uname);
Write a custom respository and use EntityManager. With this you can create a dynamic queries based on your input using CriteriaBuilder and use this criteria in querying.
Last and the most preferred option in case of dynamic inputs(like you have) is Querydsl.
Some articles about querydsl
http://www.baeldung.com/querydsl-with-jpa-tutorial
http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html

Spring Data+JPA: enforce inner join for OneToOne relationship

I have an Entity with OneToOne relation, which is used just to sort results:
#Entity
public class Document {
#Id
Long id;
#OneToOne()
SortProperty sortProp;
...
}
Then I have repository (using QueryDSL predicates):
public interface DocumentRepository
implements PagingAndSortingRepository<Document, Long>,
QueryDslPredicateExecutor<Document> {
#EntityGraph(value = "Document.forceJoins")
Page<Document> findAll(Predicate queryDslPredicate, Pageable pageable);
...
}
As you see above, I use #EntityGraph to control joining relations in the main query. All this work well, the only problem is performance - #OneToOne is fetched vith left outer join which means that DB index is not used:
select * from
document document0_
left outer join
sortproperty sortproper3_
on document0_.documentid=sortproper3_.documentid
...
Is there any way how to enforce using inner join instead of left outer join?
I have tried several things - #OneToOne(optional = false), #org.hibernate.annotations.Fetch, but no success ... Parts generated from QueryDSL predicate(s) use properly inner joins for the property, but the main part of query use always left outer join. I was trying also use annotation with this method:
#Query("select doc from Document doc inner join doc.sortProperties props")
but I was unable to use it properly together with paging and QueryDSL predicates.
Any idea?
Try this with #Query annotation.
#Query("select doc from Document doc join doc.sortProp props")