I am looking for a way to bind a given param in a native query where the value has to be inside single quotations, like so:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - interval ':timeThreshold' HOUR " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);
However, when I try to run this, it results in hibernate not being able to bind the timeThreshold value as it is provided inside the single quotations ''.
Does anyone know how this can be resolved?
The problem you are having with your native Oracle query has to do with trying to bind a value to an interval literal. You can't do that. Instead, use the NUMTODSINTERVAL() function:
#Transactional(readOnly = true)
#Query(value = " SELECT c.ID " +
" FROM table1 clh " +
" LEFT JOIN table2 nks " +
" on clh.SERIAL = nks.SERIAL_REF " +
" WHERE clh.CREATED_DATE >= :now - numtodsinterval(:timeThreshold, 'hour') " +
" AND nks.SERIAL_REF IS NULL" , nativeQuery = true)
List<Long> getIdsWithoutAnswer (#Param("timeThreshold") Integer timeThreshold, #Param("now") LocalDateTime now);
Related
Given the following native SQL query:
#Query(
value =
"SELECT "
+ "jp.BLABSTATUSID BlabStatus, "
+ "s.BLABSTATUSBEZ BlabStatusBez, "
+ "COUNT(*) sumvalue "
+ "FROM JP jp "
+ " LEFT JOIN BLAB_STATUS s "
+ " ON jp.BLABSTATUSID = s.BLABSTATUSID "
+ "GROUP BY jp.BLABSTATUSID, s.BLABSTATUSBEZ",
nativeQuery = true)
that I use in my Java / spring context, I have to switch to querydsl.
I tried:
final QJpEntity e = jpEntity;
final QBlabStatusEntity f = blabStatusEntity;
return jpaQueryFactory
.from(e)
.leftJoin(e.blabStatus, blabStatusEntity)
.on(blabStatusEntity.blabstatusid)
.groupBy(e.blabStatus.blabstatusid, f.blabstatusbez)
.select(
Projections.constructor(
JPStatisticCounter.class,
e.blabStatus,
f.blabstatusbez,
e.count()))
.fetch();
but it cannot compile because it seems syntactically wrong.
Any ideas?
solved, it was a constructor type mismatch
Here I'm having a SQL query which is working fine to get count from the MySQL database which is as
#Query("SELECT count(is_accepted) as value, post_type, is_accepted from agent_activities where "
+ "YEAR(created_date_time) as year and post_type = 'ticket' " + "GROUP BY is_accepted")
And when I'm trying into Java as JPA query it's not working.
public interface AgentActivitiesRepo extends JpaRepository<AgentActivitiesEntity, Long> {
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) as :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
}
Here ProductIssuesModel is like:
public class ProductIssuesModel {
private Long acceptCount;
private Long rejectCount;
private Long year;
private Long month;
...}
By running above query, I face an error as:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: as near line 1, column 137 [select new com.accenture.icoe.fm.model.ProductIssuesModel(count(data.isAccepted) as acceptCount) from data where YEAR(data.createdDate) as :year and data.postType = :postType group by data.isAccepted]
Please let me know if you see any error.
There are two ways to write a JPQL -
Indexed Query Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = ?1 "
+ "and data.postType = ?2 " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(Long year, String postType);
The values of the parameters will be bind to the query based on the position/index.
Named Parameters
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);
In your case, the second approach is applicable.
Here , YEAR(data.createdDate) as :year is not a condtional expression.
You may be try to do this YEAR(data.createdDate) = :year.
And use #Param to use parameter in JPQL
#Query("select new ProductIssuesModel"
+ "(count(data.isAccepted) as acceptCount) "
+ "from data where YEAR(data.createdDate) = :year "
+ "and data.postType = :postType " + "group by data.isAccepted")
public List<ProductIssuesModel> findAgentActivitiesYearly(#Param("year") Long year, #Param("postType") String postType);
I'm trying to run a query, but I get an error when running it.
However, I'm using Intellij and when I use the copy string concatenation to clipboard feature, and run the query in the datagrip, the query runs fine (after putting in the parameters)
The error I am getting is "ERROR: syntax error at or near "tstoredarticle"\n Position: 199"
I find it a bit weird that it's showing there's a line change, after tstoredartiacle but besides that I don't see what's wrong.
What could be some issues that could cause a problem like that?
my query in intellij looks like this:
private ResultSet getHuaweiSqlQuery(Integer intBrandID, Integer intStorageID, Vector<Integer> vecArticleTypes, int iCurrencyID, int iContactSupplierID, int secondaryStorage, BigDecimal exchangeRate) throws SQLException {
return Hibernate3To4Utils.getConnection(session).createStatement().executeQuery("SELECT storedarticleid, "
+ " CASE WHEN storedarticle_storageid = " + intStorageID
+ " THEN id_storedarticleid_replacement ELSE"
+ " (SELECT COALESCE(id_storedarticleid_replacement, storedarticleid)"
+ " FROM etel.tstoredarticle x"
+ " WHERE tstoredarticle.storedarticle_articleid = x.storedarticle_articleid"
+ " AND x.storedarticle_storageid = " + intStorageID + ")"
+ " END as id_storedarticleid_replacement,"
+ " articlename,"
+ " articledescription, CASE WHEN price IS NULL THEN last_innprice*" + exchangeRate
+ " ELSE price END as last_innprice, id_modelids[1] as order_modelid, storedarticleamount-amount_in_order-amount_waiting as disponibelt,"
+ " amount_in_order, amount_in_bestilling, sum(tusedarticle.amount) as ant_artikler, articleid,amount_waiting, location"
+ " FROM etel.tstoredarticle"
+ " INNER JOIN etel.tarticle as b ON storedarticle_articleid = b.articleid"
+ " LEFT JOIN etel.tusedarticle ON usedarticle_storedarticleid = storedarticleid"
+ " AND ((tusedarticle.datesnap >= CAST(now() as date)-" + LOOK_BACK_DAYS
+ " AND usedarticlefromstorage = true) OR waiting = true)"
+ " LEFT JOIN etel.torder ON usedarticle_orderid = orderid AND torder.id_serviceplaceid = " + Constants.SERVICEPLACE
+ " LEFT JOIN etel.tsupplier_price ON id_articleid = b.articleid"
+ " AND tsupplier_price.id_serviceplaceid = " + Constants.SERVICEPLACE
+ " AND tsupplier_price.id_currencyid = " + iCurrencyID
+ " AND tsupplier_price.id_contactid_supplier = " + iContactSupplierID
+ " LEFT JOIN etel.tusedarticle as last_used on storedarticleid = last_used.usedarticle_storedarticleid"
+ " AND last_used.usedarticleid = (select MAX(latest_usedarticle.usedarticleid) from etel.tusedarticle as latest_usedarticle where"
+ " latest_usedarticle.usedarticle_storedarticleid = storedarticleid)"
+ " WHERE storedarticle_storageid IN(" + intStorageID + ", " + secondaryStorage + ") AND b.id_brandid = " + intBrandID
+ " AND id_articletypeid IN (" + getStringFromArray(vecArticleTypes.toArray()) + ")"
+ " AND tstoredarticle.passive <> true"
+ " GROUP BY storedarticleid,id_storedarticleid_replacement, articlename, articledescription, last_innprice, id_modelids[1],"
+ " storedarticleamount, amount_in_order, amount_in_bestilling, articleid,price,amount_waiting, location, last_used.datesnap"
+ " ORDER BY id_storedarticleid_replacement, storedarticleid");
}
and here's the same query after using the copy string concatenation to clipboard feature:
(this runs fine)
SELECT storedarticleid,
CASE WHEN storedarticle_storageid = ?
THEN id_storedarticleid_replacement ELSE
(SELECT COALESCE(id_storedarticleid_replacement, storedarticleid)
FROM etel.tstoredarticle x
WHERE tstoredarticle.storedarticle_articleid = x.storedarticle_articleid
AND x.storedarticle_storageid = ?)
END as id_storedarticleid_replacement,
articlename,
articledescription, CASE WHEN price IS NULL THEN last_innprice*?
ELSE price END as last_innprice, id_modelids[1] as order_modelid, storedarticleamount-amount_in_order-amount_waiting as disponibelt,
amount_in_order, amount_in_bestilling, sum(tusedarticle.amount) as ant_artikler, articleid,amount_waiting, location
FROM etel.tstoredarticle
INNER JOIN etel.tarticle as b ON storedarticle_articleid = b.articleid
LEFT JOIN etel.tusedarticle ON usedarticle_storedarticleid = storedarticleid
AND ((tusedarticle.datesnap >= CAST(now() as date)-42
AND usedarticlefromstorage = true) OR waiting = true)
LEFT JOIN etel.torder ON usedarticle_orderid = orderid AND torder.id_serviceplaceid = ?
LEFT JOIN etel.tsupplier_price ON id_articleid = b.articleid
AND tsupplier_price.id_serviceplaceid = ?
AND tsupplier_price.id_currencyid = ?
AND tsupplier_price.id_contactid_supplier = ?
LEFT JOIN etel.tusedarticle as last_used on storedarticleid = last_used.usedarticle_storedarticleid
AND last_used.usedarticleid = (select MAX(latest_usedarticle.usedarticleid) from etel.tusedarticle as latest_usedarticle where
latest_usedarticle.usedarticle_storedarticleid = storedarticleid)
WHERE storedarticle_storageid IN(?, ?) AND b.id_brandid = ?
AND id_articletypeid IN (?)
AND tstoredarticle.passive <> true
GROUP BY storedarticleid,id_storedarticleid_replacement, articlename, articledescription, last_innprice, id_modelids[1],
storedarticleamount, amount_in_order, amount_in_bestilling, articleid,price,amount_waiting, location, last_used.datesnap
ORDER BY id_storedarticleid_replacement, storedarticleid
Don't use string concatenation with dynamic text values to build a SQL statement.
For one, if the text values are supplied by a user, you're leaving yourself vulnerable to SQL injection attacks, allowing hackers to steal your data and delete your tables.
But you also have problems getting the text values quoted and escaped correctly.
Instead, use a PreparedStatement, where you place ? markers anywhere a dynamic value needs to go. That is the string in the clipboard.
Example: You're doing:
String name = "John";
String sql = "SELECT * FROM Person WHERE name = " + name;
That gives you this text at runtime:
SELECT * FROM Person WHERE name = John
which is wrong, because the text value needs to be quoted:
SELECT * FROM Person WHERE name = 'John'
You could try
String name = "John's Cross";
String sql = "SELECT * FROM Person WHERE name = '" + name + "'";
but that's also wrong, because this new text value has embedded quotes and would produce:
SELECT * FROM Person WHERE name = 'John's Cross'
To get it right, use a PreparedStatement:
String name = "John's Cross";
String sql = "SELECT * FROM Person WHERE name = ?";
PreparedStatement stmt = Hibernate3To4Utils.getConnection(session).prepareStatement(sql);
stmt.setParameter(1, name);
return stmt.executeQuery();
The JDBC driver will take care of any escaping needed, and thereby protects you from both SQL injection attacks and SQL syntax errors.
I want to implement JPA Projection with count. I tried this:
#Query(value = "SELECT new org.service.PaymentTransactionsDeclineReasonsDTO( id, count(id) as count, status, error_class, error_message) " +
" FROM payment_transactions " +
" WHERE terminal_id = :id AND (created_at > :created_at) " +
" AND (status != 'approved') " +
" GROUP BY error_message " +
" ORDER BY count DESC", nativeQuery = true)
List<PaymentTransactionsDeclineReasonsDTO> transaction_decline_reasons(#Param("id") Integer transaction_unique_id, #Param("created_at") LocalDateTime created_at);
But I get error: Caused by: java.sql.SQLException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '.plugin.service.PaymentTransactionsDeclineReasonsDTO( id, count(id) as c' at line 1
How I can implement proper count when I have class based Projection?
Try Interface-based Projection instead of DTO:
public interface TransactionDeclineReason {
Integer getId();
Long getCount();
Status getStatus();
ErrorClass getErrorClass(); // I suppose it's enum...
String getErrorMessage();
}
#Query(value = "select " +
"id as id, " +
"count(id) as count, " +
"status as status, " +
"error_class as errorClass, " +
"error_message as errorMessage " +
"from " +
"payment_transactions " +
"where " +
"terminal_id = ?1 " +
"and created_at > ?2 " +
"and status != 'approved' " +
"group " +
"by error_message " +
"order by " +
"2 desc", nativeQuery = true)
List<TransactionDeclineReason> getTransactionDeclineReasons(Integer transactionId, LocalDateTime createdAt);
Pay attention on aliases (i.e. id as id) - they are mandatory.
You are mixing JPQL and SQL syntax.
The constructor expression (new ...) is JPQL, but in the annotation you mark it as a nativeQuery i.e. as SQL so you have to make up your mind.
If it is to be SQL I don't think you can use aliases in the ORDER BY clause, so you might have to either repeat the expression or wrap it in a subselect as described here: Using an Alias in a WHERE clause.
If it is to be JPQL it doesn't support aliases in a constructor expression, so I guess you have to repeat the expression in the order by clause.
I have this Query in my JPA repository - and it works EXCEPT the " order by " part. Am i doing this wrong ? is it different in hql ?
#Query(value = "select wm.WagerIdentification, wm.BoardNumber, wm.MarkSequenceNumber, wm.MarkNumber," +
" pt.CouponTypeIdentification, pt.WagerBoardQuickPickMarksBoard " +
"from WagerBoard wb " +
"inner join wb.listOfWagerMarks wm " +
"inner join wb.poolgameTransaction pt " +
"where wb.WagerIdentification = wm.WagerIdentification and wb.BoardNumber = wm.BoardNumber and wb.GameIdentification = wm.GameIdentification and wm.meta_IsCurrent = 1 " +
"and wb.TransactionIdentification = pt.TransactionIdentification and pt.meta_IsCurrent = 1 " +
"and wb.meta_IsCurrent = 1 order by wm.WagerIdentification asc, wm.BoardNumber asc, wm.MarkNumber asc")
Instead of ordering result within the #Query, you can add a method parameter of type Sort, like in Spring Data JPA reference