@Override
            protected void doInTransactionWithoutResult(
                @Nonnull
                final TransactionStatus __) {
                val principal = auditActionContext.getPrincipal();
                val userId = columnLength <= 0 || principal.length() <= columnLength
                    ? principal
                    : principal.substring(0, columnLength);
                val resourceOperatedUpon = auditActionContext.getResourceOperatedUpon();
                val resource = columnLength <= 0 || resourceOperatedUpon.length() <= columnLength
                    ? resourceOperatedUpon
                    : resourceOperatedUpon.substring(0, columnLength);
                val actionPerformed = auditActionContext.getActionPerformed();
                val action = columnLength <= 0 || actionPerformed.length() <= columnLength
                    ? actionPerformed
                    : actionPerformed.substring(0, columnLength);

                val sql = String.format(INSERT_SQL_TEMPLATE, tableName);
                val clientInfo = auditActionContext.getClientInfo();
                val locale = Optional.ofNullable(clientInfo.getLocale())
                    .map(Locale::toLanguageTag)
                    .orElseGet(Locale.US::toLanguageTag);

                val parameterMap = new HashMap<String, Object>();
                parameterMap.put(AuditTableColumns.USER.getColumnName(), userId);
                parameterMap.put(AuditTableColumns.CLIENT_IP.getColumnName(), clientInfo.getClientIpAddress());
                parameterMap.put(AuditTableColumns.SERVER_IP.getColumnName(), clientInfo.getServerIpAddress());
                parameterMap.put(AuditTableColumns.RESOURCE.getColumnName(), resource);
                parameterMap.put(AuditTableColumns.APPLIC_CD.getColumnName(), auditActionContext.getApplicationCode());
                parameterMap.put(AuditTableColumns.DATE.getColumnName(), auditActionContext.getWhenActionWasPerformed());
                parameterMap.put(AuditTableColumns.GEOLOCATION.getColumnName(), clientInfo.getGeoLocation());
                parameterMap.put(AuditTableColumns.TENANT.getColumnName(), clientInfo.getTenant());
                parameterMap.put(AuditTableColumns.USERAGENT.getColumnName(), clientInfo.getUserAgent());
                parameterMap.put(AuditTableColumns.LOCALE.getColumnName(), locale);
                parameterMap.put(AuditTableColumns.ACTION.getColumnName(), action);

                val converter = new MapToJsonAttributeConverter();
                parameterMap.put(AuditTableColumns.HEADERS.getColumnName(), converter.convertToDatabaseColumn(clientInfo.getHeaders()));
                parameterMap.put(AuditTableColumns.EXTRA_INFO.getColumnName(), converter.convertToDatabaseColumn(clientInfo.getExtraInfo()));

                val namedTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
                namedTemplate.update(sql, parameterMap);
            }
Instance of TransactionTemplate to manually execute a transaction since
threads are not in the same transaction.
support/cas-server-support-audit-jdbc/src/main/java/org/apereo/inspektr/audit/support/JdbcAuditTrailManager.java