One factor that usually complicates the maintenance of an application is unnecessary complexity in the design of the solutions. Wrong abstractions or the lack of them are among the things that more complexity adds to the source code. Complexity I would dare to call unnecessary because it's likely there will be simpler alternatives.
Do or don't, there's no try.
Note the emphasis on or the lack of them (abstractions)
. When you said if we want to reuse some piece of SQL (...) we keep them as string variables
sounds like you tried but failed at solving the real problem. The solution scratches the surface of an underlying problem but it doesn't address it. It's a palliative solution that might have worked for a while, but it doesn't pass well the test of the time.
Times and changes challenge the designs and their implementation. As the changes happen, designs should prove to be still valid, resilient and profitable. Otherwise, they should be reviewed and changed according to present needs.
Keep things simple. Avoid implementing in-expresso or one-fits-all solutions, unless you have the time, the resources and the understanding required for it. Keep in mind your goal which is, most of the time, making the business work. Don't waste resources on half-solutions. Rather solve the immediate problem first, make the solution sophisticated and reusable (if possible) later, as the problem shows you its patterns.
Code is read more times than it's written
This might sound controversial but consider undoing your "not so generic" solutions. Don't try saving a couple of lines of code (or strings) at expenses of readability. Making the code easy to reason about and easy to read is more valuable. It's likely to save more time-money than your variables. Don't let DRY principle fool you. DRY urges you to don't repeat decisions, to don't repeat solutions. In your case, I'm reluctant to believe in the capacity of variables to satisfy your needs for DRY.
Don't reinvent the wheel
Consider existing solutions based on row-mapping. For example MyBatis. Compared with other ORMs, MyBatis and the likes are reasonably simpler, easy to integrate with and yet functional. It works with a sort of prepared statements. I say "a sort of" because these can also be dynamic.
Alternatively, I have implemented the approach described here* (with some customizations). It works like a charm, balancing reusability and parameterization. Some times I end up with a couple of lines that look very similar, but it's irrelevant for my goals.
* Unfortunately, the Specification is considered by some as an anti-pattern. Here the reference but in my opinion, it depends if it passes the test of the time or not.