Column missing in Spring's namedParameterJdbcTemplate query when using UNION ALL - java

Fairly normal looking query, two basically identical selects, but in the second one the RVC table is omitted, and NULLS are used. Query works fine in the console, and column headers come from the first select in the join. Fully specifying the JDBC column names for the RV.* and RVC.* did not help, nor did putting single or double quotes around aliases in the second select i.e. Null as 'RVC.GUID' for instance.
The method
public int findColumn(String columnName, boolean
useColumnNamesInFindColumn, int indexBase) {
is being invoked and what is coming in is not RV.ID, but null.ID for the column name.
#Override
public List<ReferenceVariety> getAll() {
List<ReferenceVariety> chemicals;
MapSqlParameterSource namedParameters = new MapSqlParameterSource()
.addValue("maxCropId", maxCropId);
chemicals = namedParameterJdbcTemplate.query(
REFERENCE_VARIETIES_ALL_SQL,
namedParameters,
referenceVarietyRowMapper);
return chemicals;
}
public static final String REFERENCE_VARIETIES_ALL_SQL = "( SELECT RV.*, RVC.*, "
+ "PM.ID as PM_ID, PM.LAST_UPDATE_TIMESTAMP as PM_LAST_UPDATE_TIMESTAMP, "
+ "PM.GUID as PM_GUID, PM.ORGANIZATION_ID as PM_ORGANIZATION_ID, "
+ "PM.NAME as PM_NAME, PM.PRODUCT_SOURCE_TYPE, PM.PRODUCT_SOURCE_ID "
+ "FROM REFERENCE_VARIETY RV "
+ "LEFT JOIN PRODUCT_MANUFACTURER PM ON RV.MANUFACTURER_GUID = PM.GUID "
+ "LEFT JOIN REFERENCE_VARIETY_COUNTRY RVC ON RV.GUID = RVC.REFERENCE_VARIETY_GUID "
+ "WHERE SOURCE_SYSTEM IN (2, 3) "
+ "AND CROP_TYPE <= :maxCropId "
+ ") UNION ALL ("
+ "SELECT RV.*, "
+ "Null as GUID, "
+ "Null as SOURCE_SYSTEM_PRODUCT_ID, "
+ "Null as RETIRED_TIMESTAMP, "
+ "Null as REFERENCE_VARIETY_GUID, "
+ "Null as COUNTRY_CODE, "
+ "Null as CREATION_TIMESTAMP, "
+ "Null as LAST_UPDATE_TIMESTAMP, "
+ "Null as ID, "
+ " PM.ID as PM_ID, PM.LAST_UPDATE_TIMESTAMP as PM_LAST_UPDATE_TIMESTAMP, "
+ " PM.GUID as PM_GUID, PM.ORGANIZATION_ID as PM_ORGANIZATION_ID, "
+ " PM.NAME as PM_NAME, PM.PRODUCT_SOURCE_TYPE, PM.PRODUCT_SOURCE_ID "
+ "FROM REFERENCE_VARIETY RV "
+ "LEFT JOIN PRODUCT_MANUFACTURER PM ON RV.MANUFACTURER_GUID = PM.GUID "
+ "WHERE SOURCE_SYSTEM IN (2, 3) "
+ "AND CROP_TYPE <= :maxCropId )";
Root cause:
Caused by: java.sql.SQLException: Column 'RV.ID' not found.
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.result.ResultSetImpl.findColumn(ResultSetImpl.java:584)
at com.mysql.cj.jdbc.result.ResultSetImpl.getLong(ResultSetImpl.java:862)
at com.deere.isg.products.common.repositories.referencevarieties.ReferenceVarietyRowMapper.mapRow(ReferenceVarietyRowMapper.java:23)
at com.deere.isg.products.common.repositories.referencevarieties.ReferenceVarietyRowMapper.mapRow(ReferenceVarietyRowMapper.java:18)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:94)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:61)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:723)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651)
... 78 more
Do unions not work here? Am I doing something wrong? Either half of the union select seems to work fine by itself. For now I am issuing two queries and combining the result sets by appending the objects the row mapper generates in a collection, but I would prefer one select statement.

Related

WITH Recursive query is not working when run using entityManager native query

I'm running this PostgreSQL query on Java and it is throwing an error "ERROR: syntax error at or near ":".
But the query is working on Postgresql when I run directly.
I'm thinking Array[]::integer[] is causing the issue. Can someone has any idea?
String query = "WITH RECURSIVE tree AS ( SELECT id, ARRAY[]::integer[] AS ancestors \n" +
" FROM regions \n" +
" WHERE parent_id IS NULL\n" +
" UNION ALL \n" +
" SELECT soato.id, tree.ancestors || regions.parent_id \n" +
" FROM regions, tree \n" +
" WHERE regions.parent_id = tree.id \n" +
") \n" +
" SELECT d.id FROM department d \n" +
" WHERE d.region_id IN (select id from tree where 1703 = ANY(tree.ancestors))";
Query q = entityManager.createNativeQuery(query);
q.getResultList();
Use an explicit cast to avoid the implicit PostgreSQL option :: for casting.
ARRAY[CAST(NULL AS INTEGER)] AS ancestors

Spring Boot Mybatis Dynamic query

Hello im pretty new to mybatis and is the first time im trying to use with annotations in spring boot.
My code is something like this :
#Select("<script>"
+ "SELECT t.something, s.somewhat, "
+ "FROM t.table1 t "
+ "LEFT JOIN table2 s ON t.id = s.id "
+ "WHERE s.delete_date IS NULL "
+ "<if test=\"isnew\"> "
+ "AND t.insert_date = t.update_date "
+ "AND (t.score >= s.min_score AND t.score <= s.max_score) "
+ "</if>"
+ "union "
+ "ANOTHER SIMILAR QUERY WITH ANOTHER <IF> + "</script>")
List<Map<String, Object>> methodName(#Param("isnew") Boolean isNew);
This is the error.
Caused by: java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Could not find value method on SQL annotation. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 822;
Im almost sure there is an error of syntax very predictable but I cant find it.
Here are some examples of what I tried, none of them works:
"<if test="isnew = true">"
"<if test="isnew == true">"
"<if test="isnew != false">"
"<if test="isnew"> "
"<if test=#{isnew}"> "
"<if #{isnew} = true> "
Thanks in advance
For those who may have the same problem this is the solution:
You have to escape the character < , because mybatis takes it as an unopened tag, &lt will work :
+ "SELECT t.something, s.somewhat, "
+ "FROM t.table1 t "
+ "LEFT JOIN table2 s ON t.id = s.id "
+ "WHERE s.delete_date IS NULL "
+ "<if test=\"isnew\"> "
+ "AND t.insert_date = t.update_date "
+ "AND (t.score >= s.min_score AND t.score lt;= s.max_score) "
+ "</if>"
+ "union "
+ "ANOTHER SIMILAR QUERY WITH ANOTHER <IF> + "</script>")
List<Map<String, Object>> methodName(#Param("isnew") Boolean isNew);```

Map a result of an SQL to a field using Hibernate

I am looking for a way of mapping an SQL result of type bool to a field of an entity.
Here's the code I need to work with:
#Formula("next_erds_id is null \n" +
"and exists(\n" +
" select * from processes p\n" +
" where p.message_uuid = message_uuid \n" +
" and exists(select * from process_tasks t\n" +
" where t.process_uuid = p.process_uuid and t.task_name = 'MP_MESSAGE_PROCESSED')\n" +
" and not exists(select * from process_tasks t\n" +
" where t.process_uuid = p.process_uuid and t.task_name = 'CH_NOTIFICATION_CREATED') \n" +
")")
private boolean canNotifyOnStartDeliveryAfter;
(Please ignore the field name. I've got no idea what it is supposed to mean...)
Of course, it does not work because it's not a valid MSSQL syntax.
Any idea how to fix it?

Implement JPA Projection with count

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.

Sqlite Android Optimization

I have an application that works well. But I suspect that I can improve it if I optimize queries to the database. And I need suggestions.
This is part of my "select query":
private static final String SELECT = "SELECT " +
"dz.first_id AS first_id, " +
"dz._id AS _id, " +
"dz.att1 AS att1, " +
"dz.att2 AS att2, " +
"dz.att3 AS att3, " +
"dz.att4 AS att4, " +
"dz.att5 AS att5, " +
"d.var1 AS var1, " +
"d.name AS name, " +
"d.last_update AS last_update, " +
"d.image_url AS image_url, " +
"d.image_highlighted_url AS image_highlighted_url, " +
"d.var2 AS var2, " +
"d.type AS type, " +
"d.state AS state, " +
"d.sync AS sync, " +
"d.var3 AS var3 " +
"FROM table1 dz INNER JOIN table2 d " +
"ON d._id = dz.first_id ";
Cursor result = conn.rawQuery(SELECT, null);
*table1 and table2 have simple creation: only one _id integer PRIMARY KEY AUTOINCREMENT NOT NULL
It is useful to use views? Any other suggestion?
Thanks.
This query looks as cut and dry and they can get, I think your options are really either to see if you can somehow leave some unnecessary columns out of your select or alternatively to see that both dz.first_id and d._id have indexes setup. Perhaps add a index to dz with the following
CREATE INDEX index1 ON table1 (first_id);

Categories

Resources