java - 是否可以在 Spring AOP 建议中使用事务?

标签 java spring transactions spring-aop

我正在尝试使用 Spring AOP 实现数据库表中的日志记录。通过“登录表”,我的意思是在特殊日志表中写入有关在域对象的常用表中创建/更新/删除的记录的信息。

我编写了部分代码,一切正常,除了一件事 - 当事务回滚时,日志表中的更改仍然成功提交。这对我来说很奇怪,因为在我的 AOP 建议中,我的业务和 DAO 层使用了相同的事务。 (根据我的 AOP 建议,我使用事务传播 MANDATORY 调用了特殊管理器类的方法,并且我还在业务层、dao 层和 AOP 建议中检查了事务名称 TransactionSynchronizationManager.getCurrentTransactionName() ,它是相同的)。

有人尝试在实践中实现类似的事情吗?是否可以在 AOP 建议中使用与业务层中相同的事务,并在业务层发生某些错误时回滚 AOP 建议中所做的更改?

提前感谢您的回答。

编辑

我想澄清一下,只有根据 AOP 建议进行的更改才会出现回滚问题。 DAO 层中所做的所有更改均已成功回滚。我的意思是,例如,如果抛出一些异常,那么 DAO 层中所做的更改将成功回滚,但日志表信息将被保存(提交)。但我无法理解为什么会这样,因为正如我在上面的 AOP 建议中所写的,正在使用相同的事务。

编辑2

我用调试器检查了我在AOP建议中写入日志表的代码片段,在我看来,JdbcTemplate的更新方法在事务外执行,因为更改已在执行语句后直接提交到数据库,并且在事务方法完成之前。

编辑3

我解决了这个问题。事实上,那是我愚蠢的错误。我正在使用MySQL。创建日志表后,我没有更改数据库引擎,HeidySQL 默认设置 MyIsam。但 MyIsam 不支持事务,因此我将数据库引擎更改为 InnoDB(对于所有其他表),现在一切正常。

感谢大家的帮助,抱歉打扰。

如果有人感兴趣,这里是说明我的方法的简化示例。

考虑具有 save 方法的 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;
    }

}

这是我的数据库日志管理器类的样子:

@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);
        }
    }

最佳答案

这可能与建议的顺序有关 - 您希望与 @Transaction 相关的建议在与日志记录相关的建议周围(或之前和之后)生效。如果您使用 Spring AOP,您可能可以使用通知的 order 属性来控制它 - 为与事务相关的通知提供最高优先级,以便它在退出时最后执行。

关于java - 是否可以在 Spring AOP 建议中使用事务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11204390/

相关文章:

java - 在阻塞 ServerSocketChannel 上使用选择器

java - 在 Spring 中调用 Controller 方法之前基于 Cookie 的授权

java - spring boot如何使项目在eclipse中的服务器上运行

asynchronous - 在事务中使用 dapper async

php - Laravel:使用 try...catch 和 DB::transaction()

java - 同步如何工作?

java - jSerialComm : failed to open port (Arduino Mega 2560)

java - Spring Controller : Validation failed for object ='invoiceData' . 错误计数:4

java - springmvc显示页面问题

c# - 是否可以覆盖配置中的硬编码事务超时?