Hibernate Query Parameters binding incorrectly - java

I have been trying to write a hibernate query and have been able to generate the hibernate query through the server, but the parameters in the parameterized query are not getting binded correctly i.e. if 23 is to be binded to parameter 1, but it's getting binded to paramter 3.
Any idea why this would be happening? What should be the points of errors that I should check to get to the root cause of this issue?
EDIT (Why the query is not here?)
The hibernate query is a complex one and it is not possible for me to publish it here. That's why my question is a general one. I just wanted to know, if someone else has also faced this issue, in which hibernate throws no errors and the query is generated successfully, but it's binding incorrect data to incorrect parameter.
I will try to give a rough idea through an example -->
e.g you write:
criteria.createAlias("D.A", "a", CriteriaSpecification.LEFT_JOIN, Restrictions.eq("a.delIndc", false));
criteria.createAlias("A.B", "ab", CriteriaSpecification.LEFT_JOIN,
Restrictions.sqlRestriction("sqlQuery"));
criteria.createAlias("ab.c", "abc", CriteriaSpecification.LEFT_JOIN, Restrictions.eq("c.dataEntry", "Star Wars"));
criteria.createAlias("abc.f", "f", CriteriaSpecification.LEFT_JOIN, Restrictions.eq("f.dataExit", "Star Trek"));
The query is getting generated successfully in the server (no error thrown), but while doing the parameter binding:
2017-12-09 12:09:21,396 TRACE [org.hibernate.jdbc.AbstractBatcher] (pool-14-thread-2) preparing statement
2017-12-09 12:09:21,396 TRACE [org.hibernate.type.BooleanType] (pool-14-thread-2) binding 'false' to parameter: 1
2017-12-09 12:09:21,396 TRACE [org.hibernate.type.BooleanType] (pool-14-thread-2) binding 'Star Trek' to parameter: 2
2017-12-09 12:09:21,396 TRACE [org.hibernate.type.BooleanType] (pool-14-thread-2) binding 'Star Wars' to parameter: 3
So, instead of binding 'Star Wars' to parameter 2, it's getting bind incorrectly to parameter 3.
So, in a nutshell, I want to know, why this might be happening? What are the checks I need to do to get to the root cause of this? And also if you faced this issue, how you found the issue and how you resolved it?
Hope this clarifies my query further. Please help and let me know, if you need any more info in this regard.

You can use 2 ways to bind the parameters in Hibernate - positional parameters, named parameters.
say you have query like:
select name from Emp where id=? and age=?
Here param at 0 position will represent id and param at 1 position will represent age. Here param order is important.
select name from Emp where id=:ID and age=:AGE
Here you can set the params using names ID and AGE, in this case order is not important as you are referring to params using names rather than using their positions.

Provide your code and the values you want to pass in the query.There can be a possibility that you might have modified the name of the property in the pojo/model class

Related

How to use DSL.coalesce with lists of fields?

Using Jooq, I am trying to fetch from a table by id first, if no matches found, then fetch by handle again.
And I want all fields of the returned rows, not just one.
Field<?> firstMatch = DSL.select(Tables.MY_TABLE.fields())
.from(Tables.MY_TABLE.fields())
.where(Tables.MY_TABLE.ID.eq(id))
.asfield(); // This is wrong, because it supports only one field, but above we selected Tables.MY_TABLE.fields(), which is plural.
Field<?> secondMatch = DSL.select(Tables.MY_TABLE.fields())
.from(Tables.MY_TABLE.fields())
.where(Tables.MY_TABLE.HANDLE.eq(handle))
.asfield(); // Same as above.
dslContext.select(DSL.coalesce(firstMatch, secondMatch))
.fetchInto(MyClass.class);
Due to the mistake mentioned above in the code, the following error occurs:
Can only use single-column ResultProviderQuery as a field
I am wondering how to make firstMatch and secondMatch two lists of fields, instead of two fields?
I tried
Field<?>[] secondMatch = DSL.select(Tables.MY_TABLE.fields())
.from(Tables.MY_TABLE.fields())
.where(Tables.MY_TABLE.HANDLE.eq(handle))
.fields();
but the following error occurred in the line containing DSL.coalesce
Type interface org.jooq.Field is not supported in dialect DEFAULT
Thanks in advance!
This sounds much more like something you'd do with a simple OR?
dslContext.selectFrom(MY_TABLE)
.where(MY_TABLE.ID.eq(id))
// The ne(id) part might not be required...
.or(MY_TABLE.ID.ne(id).and(MY_TABLE.HANDLE.eq(handle))
.fetchInto(MyClass.class);
If the two result sets should be completely exclusive, then you can do this:
dslContext.selectFrom(MY_TABLE)
.where(MY_TABLE.ID.eq(id))
.or(MY_TABLE.HANDLE.eq(handle).and(notExists(
selectFrom(MY_TABLE).where(MY_TABLE.ID.eq(id))
)))
.fetchInto(MyClass.class);
If on your database product, a query using OR doesn't perform well, you can write an equivalent query with UNION ALL, which might perform better.

IncorrectResultSizeDataAccessException when no entity is found. First returned when multiple are found

I'm trying to find the reason for one strange behavior. Lets say I have Spring data repository which contains a few methods. One of them has a signature like this:
public Entity findByEntityKeyAndValidToDateAfterAndCorrectionDateAfter(BigInteger entityKey, Timestamp validToDate, Timestamp correctionDate);
I receive an issue with stacktrace with the following error:
org.springframework.dao.IncorrectResultSizeDataAccessException
I decided to add two records which have to be returned when I execute this query.
After calling this method instead of throwing the exception the query returns only one of the results. This was strange to my that is why I copy the query from the log and execute it on my SQL developer with the same parameters. The query return both results. I try to change the method signature to return list of entities:
public List<Entity> findByEntityKeyAndValidToDateAfterAndCorrectionDateAfter(BigInteger entityKey, Timestamp validToDate, Timestamp correctionDate);
In this case, the query returns two results.
Any idea why this may happen? And why this query does not throw this exception?
Spring data version is 1.10.1.RELEASE
When using a find method returning a singular type, the following semantics should apply:
0 elements found => return null or Optional.empty if applicable.
1 element found => return that element.
more than 1 element found => exception should be thrown.
Since this is not what you seem to see please upgrade to at least the current minor version (1.10.11), but preferably to the current GA release(1.11.7). If the problem still persists please create an issue at https://jira.spring.io/browse/DATAJPA/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel

Hibernate: column does not exist

When I try to load an Entity with Hibernate I get the following error in postgres-log:
ERROR: column appuser0_.device_token does not exist at character 35
STATEMENT: select appuser0_.id as id1_27_0_, appuser0_.device_token as device_t2_27_0_,....
The column device_token definitely exists - and if I copy-paste the whole logged statement and execute it in PGAdmin, I get the expected result.
So what do I forget? What is the difference between the Hibernate statement and the manually executed one?
This issue was caused by the multi tenant configuration so that the wrong DataSource has been chosen.
Depending on how you defined the query, the problem might be located somewhere else: For example, HQL Queries are using the "property-names" of the class, not the column names.
And if you have something like:
#Column("device_token")
private String deviceToken;
Then your HQL-Query should target "deviceToken" and not "device_token". We also encountered a similar error once: Hibernate was reporting "user_id" is missing, because we named the property "userId" with the underscored version for the column name only.
This might be not the problem for you but worth double checking it.

Spring Batch Paging with sortKeys and parameter values

I have a Spring Batch project running in Spring Boot that is working perfectly fine. For my reader I'm using JdbcPagingItemReader with a MySqlPagingQueryProvider.
#Bean
public ItemReader<Person> reader(DataSource dataSource) {
MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider()
provider.setSelectClause(ScoringConstants.SCORING_SELECT_STATEMENT)
provider.setFromClause(ScoringConstants.SCORING_FROM_CLAUSE)
provider.setSortKeys("p.id": Order.ASCENDING)
JdbcPagingItemReader<Person> reader = new JdbcPagingItemReader<Person>()
reader.setRowMapper(new PersonRowMapper())
reader.setDataSource(dataSource)
reader.setQueryProvider(provider)
//Setting these caused the exception
reader.setParameterValues(
startDate: new Date() - 31,
endDate: new Date()
)
reader.afterPropertiesSet()
return reader
}
However, when I modified my query with some named parameters to replace previously hard coded date values and set these parameter values on the reader as shown above, I get the following exception on the second page read (the first page works fine because the _id parameter hasn't been made use of by the paging query provider):
org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter '_id': No value registered for key '_id'
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:336)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.getPreparedStatementCreator(NamedParameterJdbcTemplate.java:374)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:192)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:199)
at org.springframework.batch.item.database.JdbcPagingItemReader.doReadPage(JdbcPagingItemReader.java:218)
at org.springframework.batch.item.database.AbstractPagingItemReader.doRead(AbstractPagingItemReader.java:108)
Here is an example of the SQL, which has no WHERE clause by default. One does get created automatically when the second page is read:
select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id) from person p
On the second page, the sql is modified to the following, however it seems that the named parameter for _id didn't get supplied:
select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id) from person p WHERE id > :_id
I'm wondering if I simply can't use the MySqlPagingQueryProvider sort keys together with additional named parameters set in JdbcPagingItemReader. If not, what is the best alternative to solving this problem? I need to be able to supply parameters to the query and also page it (vs. using the cursor). Thank you!
I solved this problem with some intense debugging. It turns out that MySqlPagingQueryProvider utilizes a method getSortKeysWithoutAliases() when it builds up the SQL query to run for the first page and for subsequent pages. It therefore appends and (p.id > :_id) instead of and (p.id > :_p.id). Later on, when the second page sort values are created and stored in JdbcPagingItemReader's startAfterValues field it will use the original "p.id" String specified and eventually put into the named parameter map the pair ("_p.id",10). However, when the reader tries to fill in _id in the query, it doesn't exist because the reader used the non-alias removed key.
Long story short, I had to remove the alias reference when defining my sort keys.
provider.setSortKeys("p.id": Order.ASCENDING)
had to change to in order for everything to work nicely together
provider.setSortKeys("id": Order.ASCENDING)
I had the same issue and got another possible solution.
My table T has a primary key field INTERNAL_ID.
The query in JdbcPagingItemReader was like this:
SELECT INTERNAL_ID, ... FROM T WHERE ... ORDER BY INTERNAL_ID ASC
So, the key is: in some conditions, the query didn't return results, and then, raised the error above No value supplied for...
The solution is:
Check in a Spring Batch decider element if there are rows.
If it is, continue with chunk: reader-processor-writer.
It it's not, go to another step.
Please, note that they are two different scenarios:
At the beginning, there are rows. You get them by paging and finally, there are no more rows. This has no problem and decider trick is not required.
At the beginning, there are no rows. Then, this error raised, and the decider solved it.
Hope this helps.

What is the correct usage of zxjdbc to call stored procedures?

I am attempting to use zxJDBC to connect to a database running on SQL Server 2008 R2 (Express) and call a stored procedure, passing it a single parameter. I am using jython-standalone 2.5.3 and ideally do not want to have to install additional modules.
My test code is shown below.
The database name is CSM
Stored Procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE dbo.DUMMY
-- Add the parameters for the stored procedure here
#carrierId VARCHAR(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO dbo.carrier (carrierId, test)
VALUES (#carrierId, 'Success')
END
GO
Jython Script:
from com.ziclix.python.sql import zxJDBC
conn = None
try :
conn = zxJDBC.connect('jdbc:sqlserver://localhost\SQLEXPRESS', 'sa', 'password', 'com.microsoft.sqlserver.jdbc.SQLServerDriver')
cur = conn.cursor()
cur.callproc(('CSM','dbo','DUMMY'), ['carrier1'])
conn.commit()
except Exception, err :
print err
if conn:
conn.rollback()
finally :
if conn :
conn.close()
By using cur.execute() I have been able to verify that the above is successfully connecting to the database, and that I can query against it. However, I have thus far been unable to successfully call a stored procedure with parameters.
The documentation here(possibly out of date?) indicates that callproc() can be called with either a string or a tuple to identify the procedure. The example given -
c.callproc(("northwind", "dbo", "SalesByCategory"), ["Seafood", "1998"], maxrows=2)
When I attempt to use this method, I receive the following error
Error("Could not find stored procedure 'CSM.DUMMY'. [SQLCode: 2812], [SQLState: S00062]",)
It would appear that zxJDBC is neglecting to include the dbo part of the procedure identifier.
If I instead call callproc with "CSM.dbo.DUMMY" as the first argument then I receive this error
Error('An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as "" or [] are not allowed. Change the alias to a valid name. [SQLCode: 1038], [SQLState: S0004]',)
Using a profiler on the database whilst running my script shows that in the second case the following SQL is executed:
use []
go
So it would seem that when using a single string to identify the procedure, the database name is not correctly parsed out.
One of my trial and error attempts to fix this was to call callproc as follows:
cur.callproc(('CSM', '', 'dbo.DUMMY'), ['carrier1'])
This got me only as far as
Error("Procedure or function 'DUMMY' expects parameter '#carrierId', which was not supplied. [SQLCode: 201], [SQLState: S0004]",)
In this case what I think is happening is that zxJDBC attempts to call a system stored procedure (sp_proc_columns) to determine the required parameters for the stored procedure I want to call. My guess is that with the procedure identifier in the incorrect format above, zxJDBC does not get a valid/correct return and assumes no parameters are required.
So basically I am not a bit stuck for ideas as to how to get it to
Use the correct database name
Correctly determine the required parameters using sp_proc_columns
Call my stored procedure with the correct name
all at the same time.
I do have a workaround, which is to use something like
cur.execute('EXEC CSM.dbo.DUMMY ?', ['carrier1'])
However I feel like callproc() is the correct solution, and would likely produce cleaner code when I come to call stored procedures with large numbers of parameters.
If anyone can spot the mistake(s) that I am making, or knows that this is not ever going to work as I think then any input would be much appreciated.
Thanks
Edit
As suggested by i-one, I tried adding cur.execute('USE CSM') before calling my stored procedure (also removing the database name from the procedure call). This unfortunately produces the same Object or Column missing error as above. The profiler shows USE CSM being executed, followed by USE [] so it seems that callproc() always fires a USE statement before the procedure itself.
I have also experimented with turning on/off autocommit, to no avail.
Edit 2
Further information following comments/suggested solutions:
"SQLEXPRESS" in my connection string is the database instance name.
Using double quotes instead of single has no effect.
Including the database name in the connection string (via ;databaseName=CSM; as specified here) and omitting it from the callproc() call leads to the original error with a USE [] statement being fired.
Using callproc(('CSM', 'dbo', 'dbo.DUMMY'), ['carrier1']) gives me some progress but results in the error
Error("Procedure or function 'DUMMY' expects parameter '#carrierId', which was not supplied. [SQLCode: 201], [SQLState: S0004]",)
I'll attempt to investigate this further
Edit 3
Based on the queries I could see zxJDBC firing, I manually executed the following against my database:
use CSM
go
exec sp_sproc_columns_100 N'dbo.DUMMY',N'dbo',N'CSM',NULL,N'3'
go
This gave me an empty results set, which would seem to explain why zxJDBC isn't passing any parameters to the stored procedure - it doesn't think it needs to. I have yet to figure out why this is happening though.
Edit 4
To update the above, the empty result set is because the call should be
exec sp_sproc_columns_100 N'DUMMY',N'dbo',N'CSM',NULL,N'3'
This unfortunately brings me full circle as I can't remove the dbo owner from the stored procedure name in my callproc() call or the procedure won't be found at all.
Edit 5
Table definition as requested
CREATE TABLE [dbo].[carrier](
[carrierId] [varchar](50) NOT NULL,
[test] [varchar](50) NULL
) ON [PRIMARY]
Though completely unaware of the technologies used here (unless some minor knowledge of SQL Server), I will attempt an answer (please forgive me if my jython syntax is not correct. I am trying to outline possibilities here not exact code)
My first approach (found at this post) would be to try:
cur.execute("use CSM")
cur.callproc(("CSM","dbo","dbo.DUMMY"), ["carrier1"])
This must have to do with the fact that sa users always have the dbo as a default schema (described at this SO post)
If the above does not work I would also try to use the CSM database name in the JDBC url (this is very common when using JDBC for other databases) and then simply call one of the two below.
cur.callproc("DUMMY", ["carrier1"])
cur.callproc("dbo.DUMMY", ["carrier1"])
I hope this helps
Update: I quote the relevant part of the link that you can't view
>> Program calls a Stored Procedure - master.dbo.xp_fixeddrives on MS SQL Server
from com.ziclix.python.sql import zxJDBC
def getConnection():
url = "${DBServer.Url}"
user= "${DBServer.User}"
password = "${DBServer.Password}"
driver = "${DBServer.Driver}"
con = zxJDBC.connect(url, user, password, driver)
return con
try:
conn = getConnection()
print 'Connection successful'
cur = conn.cursor()
cur.execute("use master")
cur.callproc(("master", "dbo", "dbo.xp_fixeddrives"))
print cur.description
for a in cur.fetchall():
print a
finally:
cur.close()
conn.close()
print 'Connection closed'
The error you get when you specified the call function like above suggests that the parameter is not passed correctly. So please modify your stored procedure to take a default value and try to call with passing params = [None]. If you see that the call succeeds we must have done something right as far as specifying the database is concerned.
Btw: the most recent documentation suggests that you should be able to access it with your syntax.
As outlined in comments callproc will work only with SELECT. Try this approach instead:
cur.execute("exec CSM.dbo.DUMMY #Param1='" + str(Param1) + "', #carrierId=" + str(carrierID))
Please see this link for more detail.

Categories