Select multiple fields using findByField in JPA - spring-boot

I am trying to select mulitple values from a table where input can be any of the 5 columns of the table or a combination of those fields.
In order to use findByField in JPA I have to many combinations of findByField and call them selectively by checking which field is blanks.
Is there an easier solution to this?
I have 5 columns in a table that I would like to query on.
If user enters all 5 then i want to select using all 5 fields like below.
select * from table where field1 = :field1 and field2 = :field2 and field3 = :field3 and field4 = :field4 and field5 = :field5;
But if user enters only 2 fields, then I just want to query on those 2 fields like this.
select * from table where field3 = :field3 and field5 = :field5.
In JPA we can do findByField1AndField2AndField3
But i am having for 5 different input possibilities i will have to create like 120 possibilities.
So is there any simpler solution?
Thank you!

It seems there is no automatic generator to build all possible combinations of JPA queries, but you should use the power of JPA dynamic queries like this :
Build predicates for all the fields and apply them on the basis of user input.
public List<Employee> findByCriteria(String employeeName){
return employeeDAO.findAll(new Specification<Employee>() {
#Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(employeeName!=null) {
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeName"), employeeName)));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
});
}
Helpful Link: https://javadeveloperzone.com/spring/spring-jpa-dynamic-query-example/

You can use a custom #Query in your #Repository interface and add as many params as you need.
#Query(value="SELECT *
FROM table
WHERE (:field1 IS NULL
OR field1 = :field1)
AND (:field2 IS NULL
OR field2 = :field2)",
nativeQuery=true)
public Table findMatchingTable(#Param("field1") String field1, #Param("field2") String field2)

Related

List of multiple column condition in query (kind of batch)

when trying to search with single record then this query works
#Query(value = "select * from table t where t.column1 = :column1 and t.column2 = :column2 and t.column3 = :column3")
Flux<Invoice> findByMultipleColumn(#Param("column1”) String column1, #Param("column2”) String column2, #Param("column3”) String column3);
But when I have list of criterias instead of a single row condition then I have to loop over the list of criterias & call the above query multiple times which is not feasible solution.
Sudo code
for (Criteria criteria : criteriaList) {
repository.findByMultipleColumn(criteria.getColumn1(), criteria.getColumn2(), criteria.getColumn3());
}
What I am trying to find a way to solve the above query for multiple LIST of all the 3 column criteria pair, something like below (this is not working solution)
#Query(value = "select * from table t where t.column1 = :column1 and t.column2 = :column2 and t.column3 = :column3")
Flux<Invoice> findByMultipleColumn(#Param List<Table> table);
Is there any way somehow we can try to achieve the above case?
Would be doable if column1, 2 and 3 were Embedded, then you could do
#Query(select * from Entity where embeddedProperty in (:values))
Flux<Entity> findByEmbeddedPropertyIn(Collection<EmbeddedClas> values);
Which would generate the following native SQL clause
Where (column1, column2, column3) in ((x, y, z), ...)
If you don't want to pack these fields i to an embeddable class, you can also try to do a workaround
#Query(select * from Entity where Concat(column1, ';', column2, ';', column3) in (:parametersConcatrenatedInJava)
Flux<Entity> findBy3Columns(Collection<String> parametersConcatrenatedInJava);
It's ofcourse not bulletproof, all three columns could have ";" as their values, this might be problematic if their type is not string, etc.
Edit.:
Third option is to use specification api. Using the criteria builder you can concatenate multiple and / or queries. And pass that specification as an argument to the repository that extends JpaSpecificationExecutor (if you're fetching whole entities) or an entity manager if you're using projections. Read more about specifications

Spring MVC, Select Special Columns in Native SELECT Query

this is my native SELECT Query in Repository
#Modifying
#Query(value = "SELECT * FROM tasks WHERE title LIKE '%Java%' ORDER BY id DESC ", nativeQuery = true)
List<Task> listAllTasks();
this works ok, but when I use custom column name instead of *, like this
#Modifying
#Query(value = "SELECT title FROM tasks WHERE title LIKE '%Java%' ORDER BY id DESC ", nativeQuery = true)
List<Task> listAllTasks();
I have this error :
org.postgresql.util.PSQLException: The column name id was not found in this ResultSet.
any Help?
The resultset doesn't have the "id" in it, you have to provide it.
You should change the way you are declaring your SQL:
SELECT t.title, t.id FROM tasks t WHERE t.title LIKE '%Java%' ORDER BY t.id DESC
Check out this sort example:Native Queries
Select * from Entity -> returns a List of Entity
Example:
#Query(select * from tasks)
List<Task> findAllTasks();
Select column from Entity -> returns a List of Types of the entity.
Example:
#Query(select t.title from tasks t)
List<String> findTitle_AllTasks();
title is of the type String
Select multiple columns from Entity -> returns an Object[] holding the data
Example:
#Query(select t.id, t.title from tasks t)
List<Object[]> findIdTitle_AllTasks();
So, you are retrieving String type data - title and asking to return a List of Task type. This is causing the problem. You can actually check the hibernate docs under HQL and JPQL to understand this.
Plus, you are doing a SELECT (DQL operation). #Modifying is rudimentary here as it is used for DML operations using Data JPA - UPDATE/DELETE.

jdbctemplate equivalent of following query

I have a long list of argument which I need to send to oracle database. I was able to do it by splitting the query but I am unable to find a way to do similar using jdbcTemplate. my query is:
select name,age from person where personId in (A1,F2,D3...G900)
or personId in (A901, C902 , ... R1800)
or personId in (A1801,G1802 .... H2700)
or personId in (P2701, G2702 ... R3600)
or since oracle allow more than 1000 touple but does not allow in so JDBC equivalent for
SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
List<Map<String, Object>> findPeeps(List<Long> personIds) {
String sql = "select name,age from person where personId in (:personIds)";
return namedParameterJdbcTemplate.queryForList(sql, new MapSqlParameterSource("personIds", personIds));
}
As #zaki said you can use that but the error that you are getting is from Oracle since there is limit of records you can put in WHERE IN clause. You can try something like this
insert into TEMP values ( ... );
select * from T where (a,b) in (select x,y from temp);
delete from TEMP;

Find by many columns

I want to perform a query like this:
SELECT * FROM table WHERE
col1 = 'value1' OR
col2 = 'value2' OR
col3 = 'value3' OR
col4 = 'value4';
Using Spring's JpaRepository I should use something like this:
List<MyEntity> findByCol1OrCol2OrCol3OrCol4(
String col1, String col2, String col3, String col4
);
Now imagine I don't want to check 4 columns but 10 or 20, the method name would be really long!
I have seen in this answer that I could use Specification to search the same text in many columns, but I want different text values for every one.
Is there any way to shorten the find method and add the columns (and the corresponding values) dynamically?
Thanks
This can be achieved via Specifications and Map(attribute name, value). Code sample (works for any data):
Map<String, Object> whereClause = new HashMap<>();
whereClause.put("lastname", "Super-Lastname");
whereClause.put("firstname", "Firstńame");
userRepository.findOne(Specifications.where(
CommonSpecifications.attributesEquals(whereClause))
)
public static <T> Specification<T> attributesEquals(Map<String, Object> whereClause) {
return (root, query, builder) -> builder.or(root.getModel().getDeclaredSingularAttributes().stream()
.filter(a -> whereClause.keySet().contains(a.getName()))
.map(a -> builder.equal(root.get(a.getName()), whereClause.get(a.getName())))
.toArray(Predicate[]::new));
}

Criteria to apply Restrictions and Projection then count distinct results

I'm trying to perform the following functionality using Criteria, but it's giving me a Run-time error indicating an error in SQL.
var query = session.CreateCriteria<TableName>()
.Add(Restrictions.Disjunction()
.Add(Restrictions.InsensitiveLike("Property1", keyword, MatchMode.Anywhere))
.Add(Restrictions.InsensitiveLike("Property2", keyword, MatchMode.Anywhere)));
query.SetProjection(Projections.Count(Projections.Distinct(
Projections.ProjectionList()
.Add(Projections.Property("Property1"))
.Add(Projections.Property("Property3"))
)));
The table mapping looks like this:
public class TableName
{
public int Property1 {get;set;}
public int Property2 {get;set;}
public int Property3 {get;set;}
public int Property4 {get;set;}
}
I need to count Distinct Results Based on my Projections, I don't want to count the results as a whole row!
Can anyone please help me with this?
----------UPDATE----------
This what I'm trying to accomplish:
select Count(*)
from
(
select distinct Property1 , Property2
from tableName
where Property1 like '%t%' or Property3 like '%t%'
) As x
Your criteria, if they could ever be translated, would result in an SQL like:
SELECT count(DISTINCT Property1, Property3)
FROM TableName
WHERE Property1 LIKE '%keyword%' OR Property2 LIKE '%keyword%'
which would not work. Use
Projections.RowCount() or Projections.RowCountInt64()
for a COUNT(*) equivalent.
Possibly, a GROUP BY clause makes sense (instead of DISTINCT). (I hope you don't need an Int64 result count when searching with LIKE '%keyword%'! ;-)

Resources