I am trying to call a basic stored procedure from azure sql which is just returning the number 1, which looks something like this
CREATE PROCEDURE [dbo].[testProc]
#TableName varchar(100)
AS
BEGIN
SET NOCOUNT ON
SELECT 1
END
I have a spring boot app trying call the stored procedure using the #Query annotation
#Repository
#Transactional
public interface TestDAO extends JpaRepository<TestEntity, Long> {
#Query(value = "CALL testProc(:TableName)", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
}
however, I get an exception which says
"Incorrect syntax near '#P0'"
and SQLGrammarException: could not extract ResultSet.
I am not sure how to fix this, I tried using the #Procedure with #NamedStoredProcedureQueries annotations and it threw another exception saying "Cannot mix positional parameter with named parameter registrations;"
According to the documentation for Azure Sql, you have to call the stored procedure with the following
EXECUTE HumanResources.uspGetEmployeesTest2 N'Ackerman', N'Pilar';
-- Or
EXEC HumanResources.uspGetEmployeesTest2 #LastName = N'Ackerman', #FirstName = N'Pilar';
So in your case this would be translated as
#Query(value = "EXECUTE testProc :TableName", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
or
#Query(value = "EXEC testProc #TableName = :TableName", nativeQuery = true)
Long invokeTestProc(#Param("TableName") String TableName);
So considering that you use native queries, the
exception which says "Incorrect syntax near '#P0'" and
SQLGrammarException: could not extract ResultSet.
reffers to the wrong use of sql grammar where you don't use execute or exec and you also pass the parameter in a way not expected for Azure SQL.
Related
i think the title sums it up, i tried to run same query on both pgadmin and java springboot.
here is my query annotation on springboot
#Query(value = "SELECT column1, column FROM public.v_example where start_date = ?1 and end_date = ?2", nativeQuery = true)
public List<Object[]> getExample();
i was calling from database views but it had no problem when i run the query directly on the database using pgadmin, when i tried it on java, it called nothing.
i was putting it inside a List<Object[]>.
Before, i was using a different query that works on both platform, the query was without where condition. Putting it on the same List<Object[]>.
I'm surprised the repository method doesn't cause an exception.
In the query you are referencing 2 parameters, but in the repository you are providing none, by having a method without arguments.
#Query(value = "SELECT column1, column FROM public.v_example where start_date = ?1 and end_date = ?2", nativeQuery = true)
public List<Object[]> getExample(param1, param2);
In the repo call you missed the arguments.
I am trying to execute the following query from my Java project.
I am using MySQL and data store and have configured Hikari CP as Datasource.
SELECT iv.* FROM identifier_definition id
INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id
where
id.status IN (:statuses)
AND id.type = :listType
AND iv.identifier_value IN (:valuesToAdd)
MySQL connection String:
jdbc:mysql://hostname:3306/DBNAME?useSSL=true&allowPublicKeyRetrieval=true&useServerPrepStmts=true&generateSimpleParameterMetadata=true
When I execute this same query from MySQL workbench it returns results in 0.5 sec.
However when I do the same from JPA Repository or Spring JDBC Template its taking almost 50 secs to execute.
This query has 2 IN clauses, where statuses collection has 3 only items whereas identifierValues collection has 10000 items.
When I execute raw SQL query without named params using JDBC template it got results in 2 secs. However, this approach is suseptible to SQL injection.
Both JPA and JDBC Templete under the hood makes used of Java PreparedStatement. My hunch is the underlying PreparedStatement while adding large params set is causing performance issue.
How do I improve my query performance?
Following is the JDBC template code that I am using:
#Component
public class ListValuesDAO {
private static final Logger LOGGER = LoggerFactory.getLogger(ListValuesDAO.class);
private final NamedParameterJdbcTemplate jdbcTemplate;
#Autowired
public ListValuesDAO(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public void validateListOverlap(List<String> valuesToAdd, ListType listType) {
String query = "SELECT iv.* FROM identifier_definition id " +
"INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id where " +
"id.status IN (:statuses) AND id.type = :listType AND iv.identifier_value IN (:valuesToAdd)";
List<String> statuses = Arrays.stream(ListStatus.values())
.map(ListStatus::getValue)
.collect(Collectors.toList());
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("statuses", statuses);
parameters.addValue("listType", listType.toString());
parameters.addValue("valuesToAdd", valuesToAdd);
List<String> duplicateValues = jdbcTemplate.query(query, parameters, new DuplicateListValueMapper());
if (isNotEmpty(duplicateValues)) {
LOGGER.info("Fetched duplicate list value entities");
} else {
LOGGER.info("Could not find duplicate list value entities");
}
}
EDIT - 1
I came across this post where other's faced similar issue while running select query using PreparedStatement on MS SQL Server. Is there any such property like "sendStringParametersAsUnicode" available in MySQL?
EDIT - 2
Tried enabling few MySQL Performance related properties. Still the same result.
jdbc:mysql://localhost:3306/DBNAME?useSSL=true&allowPublicKeyRetrieval=true&useServerPrepStmts=true&generateSimpleParameterMetadata=true&rewriteBatchedStatements=true&cacheResultSetMetadata=true&cachePrepStmts=true&cacheCallableStmts=true
I think should enable "show_sql" to true in JPA and then try, I think its running multiple queries because of lazy loading because of which it may be taking time.
Composite indexes to add to the tables:
id: INDEX(type, status, definition_id)
id: INDEX(definition_id, type, status)
iv: INDEX(identifier_value, definition_id)
iv: INDEX(definition_id, identifier_value)
For jdbc, the connection parameters should include something like
?useUnicode=yes&characterEncoding=UTF-8
For further discussion, please provide SHOW CREATE TABLE for each table and EXPLAIN SELECT... for any query in question.
Instead passing the list to IN clause, pass the list as comma seperated string and split it in the query using
select value from string_split(:valuesToAdd, ',')
So your query will look like this
SELECT iv.* FROM identifier_definition id
INNER JOIN identifier_list_values iv on id.definition_id = iv.definition_id
where id.status IN (:statuses) AND id.type = :listType AND iv.identifier_value
IN (select value from string_split(:valuesToAdd, ','))
string_split is a function in SQL Server, MySQL might have similar one
I am developing an application with spring boot. While using JPA, i am getting error that error in SQL syntax.
public interface ReservoirRepository extends CrudRepository<Reservoir, Integer> {
#Query(value = "From reservoir where patientID = ?1", nativeQuery = true)
public List<Reservoir> findByPatientId(Integer patientId);
}
Here is the MySQL table and data
The error description is like this
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'From reservoir where patientID = 1' at line 1
Please help me i am not getting what's wrong here. Thanks :)
Write the full sql command, as its native query. Like
#Query(value = "SELECT * FROM Reservoir res where res.patientID = ?1", nativeQuery = true)
I am trying to update the entities in a certain list using Spring Data JPA. However, I'm getting SQL Grammar Exception.
This is the method:
#Modifying
#Query("UPDATE Call c set c.locationLocked = false, c.locationLockedBy = null, c.locationLockedOn = null WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = false")
int expireTimedOutLockedCalls(#Param("timedOutLockedCallsIdentifiers") List<String> timedOutLockedCallsIdentifiers);
And this is the root cause:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
You are passing null or empty list in IN clause of the query.
Please check timedOutLockedCallsIdentifiers before calling above query.
It would probably be easier to do it more programatically then in a query
In your repository have the following
#Query("SELECT c FROM Call c WHERE c.callIdentifier IN :timedOutLockedCallsIdentifiers AND c.audit.retired = :retired")
List<Call> findByCallIdentifiers(List<String> timedOutLockedCallsIdentifiers, Boolean retired)
Then in your class
List<Call> updated = callRepo.findByCallIdentifiers(identifiers, false)
for(Call c : updated) {
c.setLocationLocked(false);
c.setLocationLockedBy(null);
c.setLocationLockedOn(null);
}
callRepo.saveAll(updated);
This will call the proper underlying updates as needed and cant let spring handle the query syntax properly
After switching from MySQL to PostgreSQL I found out that my SQL query (#Query in spring data repository interface) does not work anymore. The issue is caused by null value being sent as bytea and I'm getting following exception:
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: bigint = bytea
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Repository with #Query:
public interface WineRepository extends PagingAndSortingRepository<Wine, Long> {
#Query(value = "SELECT * FROM WINE w WHERE (?1 IS NULL OR w.id = ?1)", nativeQuery = true)
Wine simpleTest(Long id);
}
Simple test:
LOGGER.warn("test1: {}", wineRepository.simpleTest(1L)); //ok
LOGGER.warn("test2: {}", wineRepository.simpleTest(null)); //PSQLException
In the real case I have multiple parameters which can be null and I would prefer not checking them in java code but sending them to sql query. I have checked questions here on stackoverflow but found none with a good answer especially for spring data repository #query annotation.
What is a correct way of handling null values with PostgreSQL? Or do you have any hints how to fix my approach? Thanks!
Update:
Issue seems to be related to nativeQuery = true, when value is false, null values work as expected. So the question is whether it is possible to make it function even with nativeQuery enabled.
Try this.
SELECT *
FROM WINE w
WHERE ?1 IS NULL OR w.id = CAST(CAST(?1 AS TEXT) AS BIGINT)
It satisfies the type checker and should have the same properties as the original query. CAST is not a big performance hit if it happens on a constant value rather than a value from a database row.
You are trying to check whether a Java null is equal to Postgres NULL which I think is not necessary. You can rewrite as following and avoid sending null to the simpleTest function at all.
#Query(value = "SELECT * FROM WINE w WHERE (w.id IS NULL OR w.id = ?1)", nativeQuery = true)
Wine simpleTest(Long id);
I had a similar issue in an #Query where a Long was being interpreted by Postgres as a bytea. The solution for me was to unbox the #Param...by passing a long value, Postgres correctly interpreted the value as a bigint
I know it is an old issue, but you should be able to fix that bay casting the java value. Something like:
public interface WineRepository extends PagingAndSortingRepository<Wine, Long> {
#Query(value = "SELECT * FROM WINE w WHERE (?1\\:\\:bigint IS NULL OR w.id = ?1\\:\\:bigint)", nativeQuery = true)
Wine simpleTest(Long id);