I normally just configure all that stuff in the applicationContext.xml and skip the
java code.
There are 2 likely causes.
1. You've got so many concurrent users that the pool is being exhausted. You need to enlarge the pool and possibly the number of connection channels available on both client and database server machines.
2. OR you're not releasing connections fast enough, and that includes possibly leaking them. If you're using Spring Data properly, you shouldn't be leaking connections - that's one of the reasons why I
use Spring Data, in fact. But if your transactions are taking a long time to run, then even without leakage, you can run out. In which case, first consider whether you can optimize the code running while in a transaction and if at all possible move the long-running code outside of the transaction environment. If that doesn't work, see item #1, above.
The secret of how to be miserable is to constantly expect things are going to happen the way that they are "supposed" to happen.
You can have faith, which carries the understanding that you may be disappointed. Then there's being a willfully-blind idiot, which virtually guarantees it.