#!/usr/bin/env bash # The six ways @Transactional silently does nothing. set -euo pipefail set +m cd "$(dirname "$0")/.." source scripts/env.sh { echo "== six pieces of code carrying @Transactional that are not transactional ==" echo echo "Row 0 is the control: the SAME annotated method, reached through the proxy." echo start_app > /dev/null curl -s "http://127.0.0.1:${APP_PORT}/tx/silent" | python3 -m json.tool | sed 's/^/ /' echo echo "Reading it:" echo echo " 0 control actualTransactionActive=true. The mechanism works." echo " 1 self-invocation entryPoint() is not annotated and calls this.annotated...()," echo " so the proxy is never involved. Same class, same annotation," echo " no transaction." echo " 2 private method a CGLIB proxy advises by overriding, and private methods" echo " cannot be overridden. Legal Java, no effect." echo " 3 checked exception the default rollback rule is RuntimeException or Error. A" echo " checked exception propagates AND the transaction commits." echo " Fix: @Transactional(rollbackFor = Exception.class)." echo " 4 swallowed nothing propagates, so the interceptor sees a normal return" echo " and commits. The write survives the failure it 'handled'." echo " 5 @PostConstruct the proxy does not exist yet during initialisation." echo " 6 new no container, no proxy, no transaction." echo echo "Note what rows 3 and 4 have in common: the row is still there afterwards. These two" echo "do not merely fail to start a transaction -- they start one and COMMIT work that the" echo "code was trying to abandon." stop_app } > docs/output/04-silent-failures.txt 2>&1 cat docs/output/04-silent-failures.txt