我想用Spring AOP實現DB表中的日誌記錄。通過「登錄表」我的意思是在特殊的日誌表中寫入關於在域對象的通常表中創建/更新/刪除的記錄的信息。是否可以在Spring AOP建議中使用事務?
我寫了代碼的一部分,除了一件事情之外,所有的工作都很好 - 當事務回滾時,日誌表中的更改仍然成功提交。這對我來說很奇怪,因爲在我的AOP建議中,同樣的事務正在使用在我的業務和DAO層中。 (從我的AOP建議中,我使用事務傳播MANDATORY調用了特殊管理器類的方法,並且我在業務層,dao層和AOP建議中檢查了事務名稱TransactionSynchronizationManager.getCurrentTransactionName(),它們是相同的)。
有沒有人試圖在實踐中實現類似的東西?如果在業務層出現一些錯誤,是否可以在AOP建議中使用與業務層相同的事務並在AOP建議中進行回滾更改?
預先感謝您對unwers。
編輯
我想澄清,問題回滾僅適用於從AOP的建議所做的更改發生。在DAO層中所做的所有更改都成功回滾。我的意思是,例如,如果拋出一些異常,那麼在DAO層中所做的更改將成功回滾,但在日誌表中,信息將被保存(提交)。但我不明白爲什麼它是這樣的,因爲正如我在AOP建議中所寫的那樣,相同的交易正在使用。
EDIT 2
我與調試檢查片而言,我書面方式在AOP通知日誌表的代碼,並在我看來,由於變化已經致力於爲客戶的JdbcTemplate的update方法執行外交易數據庫直接執行完語句之後和事務處理方法完成之前。
編輯3
我解決了這個問題。其實,那是我愚蠢的錯。我正在使用MySQL。創建日誌表後,默認情況下,我沒有更改數據庫引擎和HeidySQL設置MyIsam。但MyIsam不支持事務,因此我將數據庫引擎更改爲InnoDB(與所有其他表一樣),現在所有工作都完美無缺。
謝謝大家的幫助和歉意。
如果有人有興趣,這裏是一個簡化的例子,說明我的方法。
考慮一個保存方法DAO類:
@Repository(value="jdbcUserDAO")
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true, rollbackFor=Exception.class)
public class JdbcUserDAO implements UserDAO {
@Autowired
private JdbcTemplate jdbcTemplate;
@LoggedOperation(affectedRows = AffectedRows.ONE, loggedEntityClass = User.class, operationName = OperationName.CREATE)
@Transactional(propagation=Propagation.REQUIRED, readOnly=false, rollbackFor=Exception.class)
@Override
public User save(final User user) {
if (user == null || user.getRole() == null) {
throw new IllegalArgumentException("Input User object or nested Role object should not be null");
}
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
PreparedStatement ps = connection.prepareStatement(SQL_INSERT_USER, new String[]{"ID"});
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getFullName());
ps.setLong(4, user.getRole().getId());
ps.setString(5, user.geteMail());
return ps;
}
}, keyHolder);
user.setId((Long) keyHolder.getKey());
VacationDays vacationDays = user.getVacationDays();
vacationDays.setId(user.getId());
// Create related vacation days record.
vacationDaysDAO.save(vacationDays);
user.setVacationDays(vacationDays);
return user;
}
}
這是一方面的樣子:
@Component
@Aspect
@Order(2)
public class DBLoggingAspect {
@Autowired
private DBLogManager dbLogManager;
@Around(value = "execution(* com.crediteuropebank.vacationsmanager.server.dao..*.*(..)) " +
"&& @annotation(loggedOperation)", argNames="loggedOperation")
public Object doOperation(final ProceedingJoinPoint joinPoint,
final LoggedOperation loggedOperation) throws Throwable {
Object[] arguments = joinPoint.getArgs();
/*
* This should be called before logging operation.
*/
Object retVal = joinPoint.proceed();
// Execute logging action
dbLogManager.logOperation(arguments,
loggedOperation);
return retVal;
}
}
這裏是怎麼我的數據庫日誌管理器類LooksLike:
@Component("dbLogManager")
public class DBLogManager {
@Autowired
private JdbcTemplate jdbcTemplate;
@InjectLogger
private Logger logger;
@Transactional(rollbackFor={Exception.class}, propagation=Propagation.MANDATORY, readOnly=false)
public void logOperation(final Object[] inputArguments, final LoggedOperation loggedOperation) {
try {
/*
* Prepare query and array of the arguments
*/
jdbcTemplate.update(insertQuery.toString(),
insertedValues);
} catch (Exception e) {
StringBuilder sb = new StringBuilder();
// Prepare log string
logger.error(sb.toString(), e);
}
}
如果您包含一個證明您是交易問題的[SSCCE](http://sscce.org/)示例,將會非常有幫助。 –
好的。我會在稍後添加一個例子,因爲我有緊急任務。謝謝 – dimas