How to add limit to Hibernate query annotation? - java

I have this in Java Hibernate
#Query("SELECT dirPar FROM DirectiveParagraph dirPar, Directive dir "
+ "WHERE dirPar.directive = dir "
+ "AND dir.txtDirCode = :txtDirCode ");
List<DirectiveParagraph> getByName(#Param("txtDirCode") String name, #Param("page") int page ,#Param("size") int size);
I want to retrieve with limit and size, same way like this
SELECT * FROM tblDirectiveParagraph where intDirectiveID = 1 limit 10,10;
How do I add limit to above #Query annotation

You can try adding a Pageable parameter to your getByName method
#Query("SELECT dirPar FROM DirectiveParagraph dirPar, Directive dir "
+ "WHERE dirPar.directive = dir "
+ "AND dir.txtDirCode = :txtDirCode ");
Page<DirectiveParagraph> getByName(#Param("txtDirCode") String name, Pageable page);
And here's a sample method call:
public void someMethod(){
PageRequest pageR = new PageRequest(10,10);
Page<DirectiveParagraph> result = directiveParagraphRepository.getByName("txtDirCode",pageR);
List<DirectiveParagraph> resultList = result.getContent();
}

Limit is not supported in HQL. To set the size, you would use setMaxResults. To set start point, you use setFirstResult.
Query q = session.createQuery("...");
q.setFirstResult(10);
q.setMaxResults(10);
If you don't want to do it that way, you would have to use createSQLQuery to write a native sql query instead of hql.

Related

how to pass sql query condition dynamically to #query as param

Is it possible to add dynamic sql condition to query annotations
Say when n= : name
n != : name
n like "%:name%"
The formation of condition based on the user input
public interface PersonRepository extends CrudRepository<Person, Long> {
#Query("select p from Person AS p"
+ " ,Name AS n"
+ " where p.forename = n.forename "
+ " and p.surname = n.surname"
+ " and n = :name")
Set<Person>findByName(#Param("name") Name name);
}
instead of writing "and n = :name " i want form sql condition dynamically when user select = , or != or like
"and n != :name "
"and n like '%:name%'"
seems like Filtering option
I think the most simple way for Spring Data Jpa would be using specifications based on JPA Criteria API.

Using Spring JPA to setup a Aggregate Function for STRING_AGG with Postgresql for a Group_By

I am trying to setup a repository call to retrieve the ID's of a list of test results ids used in the GROUP_BY. I can get this to work using createNativeQuery but I am unable to get this to work using Spring's JPA with the FUNCTION call.
FUNCTION('string_agg', FUNCTION('to_char',r.id, '999999999999'), ',')) as ids
I am using Spring Boot 1.4, hibernate and PostgreSQL.
Question
If someone can please help me out to setup the proper function call
shown below in the JPA example it would be much appreciated.
Update 1
After implementing the custom dialect it looks like its trying to cast the function to a long. Is the Function code correct?
FUNCTION('string_agg', FUNCTION('to_char',r.id, '999999999999'), ','))
Update 2
After looking into the dialect further it looks like you need to register the return type for your function otherwise it will default to a long. See below for a solution.
Here is my code:
DTO
#Data
#NoArgsConstructor
#AllArgsConstructor
public class TestScriptErrorAnalysisDto {
private String testScriptName;
private String testScriptVersion;
private String checkpointName;
private String actionName;
private String errorMessage;
private Long count;
private String testResultIds;
}
Controller
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<Set<TestScriptErrorAnalysisDto>> getTestScriptErrorsByExecutionId(#RequestParam("executionId") Long executionId) throws Exception {
return new ResponseEntity<Set<TestScriptErrorAnalysisDto>>(testScriptErrorAnalysisRepository.findTestScriptErrorsByExecutionId(executionId), HttpStatus.OK);
}
Repository trying to use Function Not working
#Query(value = "SELECT new com.dto.TestScriptErrorAnalysisDto(r.testScriptName, r.testScriptVersion, c.name, ac.name, ac.errorMessage, count(*) as ec, FUNCTION('string_agg', FUNCTION('to_char',r.id, '999999999999'), ',')) "
+ "FROM Action ac, Checkpoint c, TestResult r " + "WHERE ac.status = 'Failed' " + "AND ac.checkpoint = c.id " + "AND r.id = c.testResult " + "AND r.testRunExecutionLogId = :executionId "
+ "GROUP by r.testScriptName, r.testScriptVersion, c.name, ac.name, ac.errorMessage " + "ORDER by ec desc")
Set<TestScriptErrorAnalysisDto> findTestScriptErrorsByExecutionId(#Param("executionId") Long executionId);
Repository using createNativeQuery working
List<Object[]> errorObjects = entityManager.createNativeQuery(
"SELECT r.test_script_name, r.test_script_version, c.name as checkpoint_name, ac.name as action_name, ac.error_message, count(*) as ec, string_agg(to_char(r.id, '999999999999'), ',') as test_result_ids "
+ "FROM action ac, checkpoint c, test_result r " + "WHERE ac.status = 'Failed' " + "AND ac.checkpoint_id = c.id "
+ "AND r.id = c.test_result_id " + "AND r.test_run_execution_log_id = ? "
+ "GROUP by r.test_script_name, r.test_script_version, c.name, ac.name, ac.error_message " + "ORDER by ec desc")
.setParameter(1, test_run_execution_log_id).getResultList();
for (Object[] obj : errorObjects) {
for (Object ind : obj) {
log.debug("Value: " + ind.toString());
log.debug("Value: " + ind.getClass());
}
}
Here was the documents I found on FUNCTION
4.6.17.3 Invocation of Predefined and User-defined Database Functions
The invocation of functions other than the built-in functions of the Java Persistence query language is supported by means of the function_invocation syntax. This includes the invocation of predefined database functions and user-defined database functions.
function_invocation::= FUNCTION(function_name {, function_arg}*)
function_arg ::=
literal |
state_valued_path_expression |
input_parameter |
scalar_expression
The function_name argument is a string that denotes the database function that is to be invoked. The arguments must be suitable for the database function that is to be invoked. The result of the function must be suitable for the invocation context.
The function may be a database-defined function or a user-defined function. The function may be a scalar function or an aggregate function.
Applications that use the function_invocation syntax will not be portable across databases.
Example:
SELECT c
FROM Customer c
WHERE FUNCTION(‘hasGoodCredit’, c.balance, c.creditLimit)
In the end the main piece which was missing was defining the functions by creating a new class to extend the PostgreSQL94Dialect. Since these functions were not defined for the dialect they were not processed in the call.
public class MCBPostgreSQL9Dialect extends PostgreSQL94Dialect {
public MCBPostgreSQL9Dialect() {
super();
registerFunction("string_agg", new StandardSQLFunction("string_agg", new org.hibernate.type.StringType()));
registerFunction("to_char", new StandardSQLFunction("to_char"));
registerFunction("trim", new StandardSQLFunction("trim"));
}
}
The other issue was that a type needed to be set for the return type of the function on registration. I was getting a long back because by default registerFunction returns a long even though string_agg would return a string in a sql query in postgres.
After updating that with new org.hibernate.type.StringType() it worked.
registerFunction("string_agg", new StandardSQLFunction("string_agg", new org.hibernate.type.StringType()));

LIKE COALESCE in Spring JPA does not work

So my problem is the following:
I have a table I want a filter to apply to. This filter should call a query which finds based on the given information the reports.
This is how my query looks:
#Query("SELECT r FROM Report r WHERE r.importanceLevel = COALESCE(importance,'%')" +
"AND r.source = COALESCE(source,'%')" +
"AND r.resolvedStatus = COALESCE(resolvedStatus,'%')" +
"AND r.header LIKE + '%' COALESCE(query,'%') + '%'")
List<Report> getReportsByAppliedFilter(#Param("importance") int importance, #Param("source") String source,
#Param("resolvedStatus") int resolvedStatus, #Param("query") String query);
Problem is: IntelliJ does not like the following:
LIKE + '%' COALESCE(query,'%') + '%'
Error: expected, got +
Do you have any idea how to solve this otherwise?
Yeah, time to go to bed. You dont use + for concat , you use concat():
#Query(value = "SELECT r FROM Report r WHERE r.importanceLevel = COALESCE(importance,'%')" +
"AND r.source = COALESCE(source,'%')" +
"AND r.resolvedStatus = COALESCE(resolvedStatus,'%')" +
"AND r.header LIKE CONCAT('%', COALESCE(query,'%'), '%')")
If your column name is request_status and table name is connections then use COALESCE like
#Query(value = "SELECT connections.id, COALESCE(connections.request_status,'') as request_status.... ", nativeQuery = true)
List<ZXYType> xysfunction();
Here if request_status is null then it will be replaced by an empty string.

sql to hql throwing exception

I'm using hibernate in my project and I'm trying to convert an existing sql query from DaoImplementation class to hql,
The sql query I have is
JdbcTemplate select = new JdbcTemplate(dataSource);
String sql = "SELECT * FROM (SELECT site_id,rtc,sigplan,cycle_time,health,phase_no,phase_time,active_groups,groupscolour,ip "+
"FROM status_data where rtc>='" + fromDate + "' and rtc<'" + toDate + "' and "+
"site_id=" + SiteId + " order by rtc desc limit "+recordLimit+" )as temp ORDER BY RTC ASC";
I wrote the hql version to get data from HealthLog table as
String hql = " select f from (select h from HealthLog h where rtc>='"+fromDate+"' and rtc <'"+toDate+"' "
+ "and siteId = "+siteId+" order by rtc desc limit "+limit+" ) as f order by rtc asc ";
return super.readListByHql(hql);
But the above hql throws the following exception
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 16 [ select f from (select h from com.traff.hibernate.model.HealthLog as h where rtc>='1974-08-01 14:10:00.0' and rtc <'1974-09-01 23:46:20.6' and siteId = 20 order by rtc desc limit 50000 ) as f order by rtc asc ]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:47)
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:79)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:276)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:180)
at org.hibernate.hql.intern
I also tried the below code snippet but that giving me wrong results
Criteria criteria = createEntityCriteria();
criteria.add(Restrictions.ge("rtc", fromDate));
criteria.add(Restrictions.lt("rtc", toDate));
criteria.add(Restrictions.eq("siteId", siteId));
criteria.setMaxResults(limit);
criteria.addOrder(Order.asc("rtc"));
criteria2 = criteria;
criteria2.addOrder(Order.desc("rtc"));
return criteria2.list();
Which is the correct way to achieve the result?
First of all, as already mentioned in the comments, you cannot do a subquery within the FROM clause in HQL.
See: Hibernate Documentation
Secondly, the limit keyword is not supported by HQL.
Usually you would use query.setFirstResult(0) and query.setMaxResults(recordLimit) methods where query has the type of the Query Interface. But since you are using the limit in a subquery, there is no way.
See: How to set a limit to inner query in Hibernate?
Some options:
Use a native SQLQuery
Since you are only sorting in the outer Query. You could only execute the inner query and sort in Java.
Example for Option 2:
Session session = factory.openSession();
Query query = session
.createQuery("FROM HealthLog "
+ "WHERE rtc >= :rtcL and rtc < :rtcG and siteId = :siteId "
+ "ORDER BY rtc DESC");
query.setParameter("rtcL", fromDate);
query.setParameter("rtcG", toDate);
query.setParameter("siteId", siteId);
query.setFirstResult(0);
query.setMaxResults(recordLimit);
List<HealthLog> res = query.list();
session.close();
Collections.sort(res, new Comparator<HealthLog>() {
public int compare(HealthLog o1, HealthLog o2) {
return o1.getRtc().compareTo(o2.getRtc());
}
});
The query above returns HealthLogs with all attributes. If you want to only retrieve specific attributes, you can add a SELECT new HealthLog(siteId,rtc,sigplan,cycle_time,...) to your Query with a fitting constructor in HealthLog.
Please note that the code snippet might not be ready to use, since i do not know your model and attribute names.

Spring Like clause

I am trying to use a MapSqlParameterSource to create a query using a Like clause.
The code is something like this. The function containing it receives nameParam:
String namecount = "SELECT count(*) FROM People WHERE LOWER(NAME) LIKE :pname ";
String finalName= "'%" +nameParam.toLowerCase().trim() + "%'";
MapSqlParameterSource namedParams= new MapSqlParameterSource();
namedParams.addValue("pname", finalName);
int count= this.namedParamJdbcTemplate.queryForInt(namecount, namedParams);
This does not work correctly, giving me somewhere between 0-10 results when I should be receiving thousands. I essentially want the final query to look like:
SELECT count(*) FROM People WHERE LOWER(NAME) LIKE '%name%'
but this is evidently not happening. Any help would be appreciated.
Edit:
I have also tried putting the '%'s in the SQL, like
String finalName= nameParam.toLowerCase().trim();
String namecount = "SELECT count(*) FROM People WHERE LOWER(NAME) LIKE '%:pname%' "
;
but this does not work either.
You don't want the quotes around your finalName string. with the named parameters you don't need to specify them. This should work:
String namecount = "SELECT count(*) FROM People WHERE LOWER(NAME) LIKE :pname ";
String finalName= "%" + nameParam.toLowerCase().trim() + "%";
MapSqlParameterSource namedParams= new MapSqlParameterSource();
namedParams.addValue("pname", finalName);
int count= this.namedParamJdbcTemplate.queryForInt(namecount, namedParams);
This solution worked for me. I put the "%" on the Object[] parameters list:
String sqlCommand = "SELECT customer_id, customer_identifier_short, CONCAT(RTRIM(customer_identifier_a),' ', RTRIM(customer_identifier_b)) customerFullName "
+ " FROM Customer "
+ " WHERE customer_identifier_short LIKE ? OR customer_identifier_a LIKE ? "
+ " LIMIT 10";
List<Customer> customers = getJdbcTemplate().query(sqlCommand, new Object[] { query + "%", query + "%"}, new RowMapper<Customer>() {
public Customer mapRow(ResultSet rs, int i) throws SQLException {
Customer customer = new Customer();
customer.setCustomerFullName(rs.getString("customerFullName"));
customer.setCustomerIdentifier(rs.getString("customer_identifier_short"));
customer.setCustomerID(rs.getInt("customer_id"));
return customer;
}
});
return customers;
Have you tried placing the % wild cards in your sql string (not the bind variable value itself):
String finalName= nameParam.toLowerCase().trim();
String namecount = "SELECT count(*) FROM People WHERE LOWER(NAME) LIKE '%:finalName%'";
We can use simple JdbcTemplate instead of NamedParamJdbcTemplate
String namecount = "SELECT count(*) FROM People WHERE LOWER(NAME) LIKE ? ";
String finalName= "%" +nameParam.toLowerCase().trim() + "%"; //Notes: no quote
getJdbcTemplate().queryForInt(namecount, new Object[] {finalName});
Hope it helpful for someone using JdbcTemplate

Categories

Resources