How to create a dynamic query using collection-valued named parameters? - sql

As the title suggests, i'm currently trying to add parts to the JPQL-query using collection-valued named parameters (:queryLst).
Function call:
List<PanelSet> psetLst = setRepository.getMaxZchnrGroupByLeftEight(p.getCustomerNumber(), p.getDrawingNumber(), queryLst);
queryLst:
// Is used to store values from scanned and convert them into parts of a query
ArrayList<String> queryLst = new ArrayList<>();
for (int i = 0; i < size1; i++) {
scanEdvRev = scanned.get(i).toString();
queryLst.set(i, "and left(a.drawingnumber, 8) != left('" + scanEdvRev + "', 8)");
}
SetRepository:
public interface SetRepository extends CrudRepository<PanelSet, Integer> {
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber (:queryLst) "
+ "group by left(a.drawingNumber, 8)")
List<PanelSet> getMaxZchnrGroupByLeftEight(#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("queryLst") ArrayList<String> queryLst);
}
When i run the project i get the following exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 159 [select distinct max(a.drawingNumber) from com.asetronics.qis2.model.PanelSet a where a.customerNumber = :customerNumber and a.drawingNumber != :drawingNumber (:queryLst) group by left(a.drawingNumber, 8)]
I'm unsure whether my approach to this problem is the correct way of doing this and whether this exception is caused by a simple syntax error or by my usage of collection-valued named parameters.
I've followed this guide trying to solve the problem.
EDIT: I'm basically trying to add each String from ArrayList<String> queryLst to the parametrized query inside setRepository.
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber (:queryLst) "
+ "group by left(a.drawingNumber, 8)")
If successful, the query behind the function
List<PanelSet> getMaxZchnrGroupByLeftEight(#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("queryLst") ArrayList<String> queryLst);
should look like this:
queryStr = "select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = " + customerNumber + ""
+ "and a.drawingNumber != " + drawingNumber + "";
for (String s : queryLst) {
queryStr = queryStr + s;
}
queryStr = queryStr + " group by left(a.drawingNumber, 8)";
I hope this clarifies what i'm trying to do with queryLst.

It can't be done using your approach of passing in a list of query chunks.
The closest you'll get is by adding every possible condition to the query and provide values for all those conditions in a way that allows conditions to be ignored, typically by providing a null.
You code might look like this:
#Query("select distinct max(a.drawingNumber) from PanelSet a "
+ "where a.customerNumber = :customerNumber "
+ "and a.drawingNumber != :drawingNumber "
+ "and a.myTextColumn = coalesce(:myTextColumn, a.myTextColumn) "
+ "and a.myIntegerColumn = coalesce(:myIntegerColumn, a.myIntegerColumn) "
// etc for all possible runtime conditions
+ "group by left(a.drawingNumber, 8)")
List<PanelSet> getMaxZchnrGroupByLeftEight(
#Param("customerNumber") String customerNumber,
#Param("drawingNumber") String drawingNumber,
#Param("myTextColumn") String myTextColumn,
#Param("myIntegerColumn") Integer myIntegerColumn);
Passing null for myTextColumn or myIntegerColumn will allow that column to be any value (except null).
You'll have to find SQL that works for the type of conditions you have and the data type of the columns involved and whether nulls are allowed.
If passing nulls doesn't work, use a special value, perhaps blank for text columns and some "impossible" date like 2999-01-01 fir date columns etc and code the condition like:
and (a.myCol = :myCol or :myCol = '2999-01-01')

Related

How to pass multiple values in a native query and retrieve that data in a Map<String, List<Data>>?

Here I want to pass a list of Ids and date as a parameter to the database query and I want the query to return data in a Map so the key is the id and List is the data against that id.
Query<Data> query = rdsSession.createNativeQuery(DBQueries.DATA_QUERY,
Data.class).setParameterList("performance_id", listOfIds).setParameter("fromdate", fromDate);
List<Data> listDBRecords = null;
Map<String, List<Data> records=listDBRecords.stream().collect(Collectors.groupingBy(Data::getId));
So how can I achieve this result by passing multiple values(ie. ids) in the query and return that data? This is the query I have written
public static final String SELECT_PORTFOLIO_SECURITY_DATA = "select capl.corporate_action_portfolio_level_id, " +
"capl.performance_id, capl.forwardlooking_sequence_no, " +
"capl.as_of_date, capl.price, casl.currency, capl.exchange_rate, " +
"capl.gross_dividend, capl.net_dividend, capl.sub_portfolio_guid, capl.effective_date, capl.adjustment_weighted_factor, capl.price_adjustment_factor, " +
"casl.share_adjustment_factor, casl.float_adjustment_factor, capl.index_shares, capl.input_tos_live, casl.input_float_live, capl.prediction_type from " +
ConfigurationManager.getProperties().getProperty("rdsConnection.schema") +
".corporate_action_portfolio_level capl inner join " +
ConfigurationManager.getProperties().getProperty("rdsConnection.schema") +
".corporate_action_security_level casl " +
"on capl.performance_id = casl.performance_id and capl.as_of_date = casl.as_of_date and capl.prediction_type = casl.prediction_type and " +
"capl.forwardlooking_sequence_no = casl.forwardlooking_sequence_no " +
"where capl.performance_id = :performance_id and capl.as_of_date = :as_of_date and capl.prediction_type = 'PREDICTED' or capl.prediction_type = 'LIVE'";
Can someone tell what is the right way?

Joining unrelated tables via JPQL

I have the following query which works. All the tables in this query have relations in some way.
#Repository(value = "ARepository")
public interface ARepository extends JpaRepository<CardEntity, String> {
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode"
)
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);
}
But I also wish to join another table (Entity name -> UrlCountEntity) to this query but that table has no relation to the other tables in this query. Is there a way I could do this?
Based on reading a blog, I tried the following but throws errors.
Added this line to the query:
AND EXISTS (SELECT referCount FROM UrlCountEntity referCount WHERE
referCount.url.urlTx = xref.shortUtlTx)
#Repository(value = "ARepository")
public interface ARepository extends JpaRepository<CardEntity, String> {
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode " +
"AND EXISTS (SELECT referCount FROM UrlCountEntity referCount WHERE referCount.url.urlTx = xref.shortUtlTx)"
)
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);
}
Error as follows:
Method threw
'org.springframework.dao.InvalidDataAccessResourceUsageException'
exception.
could not extract ResultSet; SQL [n/a]
Based on this article:
Using Hibernate 5.1 or newer you can join two unrelated tables via JQPL same way you would do it in SQL:
SELECT first
FROM First first JOIN
Second second ON first.property = second.property
WHERE first.property = :param
So you would need to change your query to something like this:
#Query("SELECT xref.shortUtlTx FROM CardEntity card " +
"JOIN card.apexUrlCrossRef xref " +
"JOIN xref.sampleProdOffer offer " +
"JOIN UrlCountEntity referCount ON referCount.url.urlTx = xref.shortUtlTx" +
"WHERE xref.apexCard.cardNumber = :cardNum " +
"AND offer.apexeeOfferId = :myCode")
List<String> getAllValues(#Param("cardNum") String cardNum, #Param("myCode") String myCode);

Eclipselink NamedNativeQuery pass column name as parameter and not a value

Trying to pass column name as parameter but JPA sets it as a value surrounding it with single quotes.
#NamedNativeQueries({
#NamedNativeQuery(
name = "Genre.findAllLocalized",
query = "SELECT "
+ " CASE "
+ " WHEN ? IS NULL THEN genre_default"
+ " ELSE ? "
+ " END localized_genre "
+ "FROM genre ORDER BY localized_genre")
})
Then:
List<String> res = em.createNamedQuery("Genre.findAllLocalized")
.setParameter(1, colName)
.setParameter(2, colName)
.getResultList();
The problem is that the column names being passed are taken as values so the result will return result list with repeated values of "col_name" instead of selecting the value of the column passed as parameter.
Is this achievable?
Basically it makes no sense to create a prepared query like this, how would you name that query anyway: "*"? So the short answer is: no.
But you could create named queries dynamically if this matches your requirement:
String colName = "colName";
String query = "SELECT WHEN " + colName + " IS NULL THEN genre_default";
Query query = entitymanager.createQuery(query);
Probably using a criteria builder is more the way you want to use JPA (code from https://en.wikibooks.org/wiki/Java_Persistence/Criteria):
// Select the employees and the mailing addresses that have the same address.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root employee = criteriaQuery.from(Employee.class);
Root address = criteriaQuery.from(MailingAddress.class);
criteriaQuery.multiselect(employee, address);
criteriaQuery.where( criteriaBuilder.equal(employee.get("address"), address.get("address"));
Query query = entityManager.createQuery(criteriaQuery);
List<Object[]> result = query.getResultList();

native query with spring data jpa doesn't return result

Hello I have written the following method in spring data jpa repo:
#RestResource(exported = false)
#Query(
value = "select q.* " +
"from question q " +
"left join question_program qp " +
"on q.question_id = qp.question_id " +
"where " +
"q.difficulty_level_id = ?1 " +
"and " +
"q.paid = false " +
"and " +
"qp.program_id = ?2 " +
"order by random() " +
"limit ?3",
nativeQuery = true
)
List<Question> getFreeRandomQuestions(
#Param("df") Integer df,
#Param("programId") Integer programId,
#Param("qs") Integer qs);
It is returning an empty list whereas when I run the same query in pgadmin I get all the desired rows. It also works in spring data jpa if I remove the join with question_program. What am I doing wrong?
UPDATE:
I have tried select q ... and select * .... Both have the same problem
UPDATE:
I kind of solved this by implementing a service which fetches that data using sessionFactory and raw sql query!.

Pass a SQL condition as a OleDb parameter

I have following code:
Dim executedCmd As OleDb.OleDbCommand = m_dbMgr.GetCommand()
executedCmd.CommandText = "select * from [Parameters] where "
Dim SQLcondition As String = String.Empty
For i As Integer = 0 To ParameterName.Count - 1
executedCmd.CommandText += "ParameterName = #parametername" + i.ToString() + " and ParameterValue #ParamaterCondition" + i.ToString() + " #ParameterValue" + i.ToString()
executedCmd.Parameters.AddWithValue("#parametername" + i.ToString(), ParameterName(i))
executedCmd.Parameters.AddWithValue("#ParameterValue" + i.ToString(), ParameterValue(i))
executedCmd.Parameters.AddWithValue("#ParamaterCondition" + i.ToString(), Condition(i))
Next
ParameterName, ParameterValue, ParameterCondition all are same length ArrayList, but the code does not work properly. I have verified all the variables have values.
When I run the code it reports a syntax error: "missing operations in query expression"
The problem is that ParameterCondition has values like ('>', '<', '=',.... some logical SQL operators).
Edit: How can I include conditions in parameters?
Add 1 = 1 after where to simplify building logical expression. Notice that all conditions added by AND logical operator. You can generate parameter name from columns name.
executedCmd.CommandText = "select * from [Parameters] where 1 = 1"
....
executedCmd.CommandText += " AND " + ParameterName(i) + " " + Condition(i) + " #" + ParameterName(i)
executedCmd.Parameters.AddWithValue("#" + ParameterName(i), ParameterValue(i))
ParameterCondition must all be binary operators.
The database engine would check the syntax of the SQL statement before it replaces the parameters with values. So somethinhg like "WHERE #p1 #p2 #p3" will not be parsed correctly.
Replace your condition with a string-expression e.g.
commandtext = parameterName + condition + " #p1"