You are doing it with needless complexity.
Firstly, you have a complex SQL query, which should be avoided. Your business logic should not be written in SQL. I would use SQL just for queries and implement the logic in Java. Essentially, your code looks like you are unwilling to implement the logic in Java, and thus do SQL changes to maintain the Java code as unmodified as possible. Why is that the case?
Secondly, you are wasting precious CPU cycles.
Add a random sleep with suitably defined approximate time interval, approximately the same time it would take to verify the hash. Do this if the user isn't found. This makes it impossible for the client to get timing information, and also saves precious CPU cycles by genuinely sleeping instead of spin-looping. You may want to add some randomness common to the "not found" and "found" code paths as well to make it even more secure, to prevent people from logging in too fast.
How the randomness should be done? For the "not found" code path, get the standard deviation and mean of your password hash calculation times for lots of samples (e.g. 1000), and then approximate this with a Gaussian distribution. For the common code path, you should add more standard deviation than the "not found" code path has.
Also, you could take the role of the attacker and try to do statistical analysis for how hard it's via timing information possible to verify whether one email address is there. If it takes more than a minute, I'm sure nobody will enumerate your email addresses, but just attack isolated ones. If it takes more than an hour, even isolated ones won't probably be queried.
Also, you might want to somehow limit requests coming from a single IP address. I would use the token bucket algorithm for this to allow login bursts, but to restrict the rate of logins in the long term. This would also make it harder to enumerate the e-mail addresses or even query isolated ones.