This does not seem to apply to your specific situation, but I wanted to provide another possible explanation to someone who is looking for this problem.
I had a similar problem when the query executed directly in SQL Server took 1 minute and the same query took 5 minutes through the prepared java statemnent. I tracked it to the point that it was done as a prepared expression.
When you execute a query directly in SQL Server, you provide it with a non-parameterized query in which it knows all the search criteria during optimization. In my case, the search criteria included a date range, and the SQL server was able to look at it, deciding that "this date range is huge, do not use the date index", and then he chose something much better.
When I execute the same query through a prepared java statement, while SQL Server is optimizing the query, you have not provided it with any parameter values, so it should make an assumption which index to use, In case of my date range, if it is optimized for a small range, and I give him a large range, it will work slower than he could. Similarly, if it is optimized for a large range, and I give it a small one, it will again work slower than I could.
To demonstrate that this is indeed a problem, as an experiment, I tried to give him tips on what needs to be optimized to use the SQL Server OPTIMIZE FOR option. When I told him to use a tiny date range, my Java query (which actually had a wide date range) actually took twice as much as before (10 minutes, unlike 5 minutes before, and unlike 1 minute in SQL Server). When I said that my exact dates were for optimization, the runtime was identical between the prepared Java expression.
So my solution was to hardcode the exact dates in the request. This worked for me because it was a one-time expression. PreparedStatement was not intended to be reused, but simply to parameterize values to avoid SQL injection. Since these dates came from the java.sql.Date object, I did not have to worry about my date values containing the injection code.
However, for a statement that needs to be reused, hard date encoding will not work. Perhaps the best option for this would be to create several prepared statements optimized for different date ranges (one for one day, one for a week, one for a month, one for a year, and one for a decade ... or maybe you only need 2 or 3 options ... I don’t know), and then for each request, execute one prepared statement whose time range best matches the range in the current request.
Of course, this only works when your date ranges are evenly distributed. If 80% of your records were last year and 20% were allocated over the previous 10 years, then performing "multiple queries based on range size" might not be the best. You will need to optimize your queries based on certain ranges or something else. You will need to understand that an error has occurred through the trial version.