2012-06-26 25 views
2

我想用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); 
     } 
    } 
+0

如果您包含一個證明您是交易問題的[SSCCE](http://sscce.org/)示例,將會非常有幫助。 –

+0

好的。我會在稍後添加一個例子,因爲我有緊急任務。謝謝 – dimas

回答

0

與AOP無關,將數據源屬性autocommit設置爲false,如:

<bean id="datasource" ...> 
    <property name="autoCommit" value="false/> 
</bean> 

如果您使用XML配置

+0

可能我理解你錯了,但在我看來,將autocommit設置爲true不會對我有幫助。只有當tarnsaction成功完成時,我才需要提交更改(僅在AOP建議中進行)。但是,如果我將autoCommit設置爲true,那麼每個語句將在執行後自動自動提交。這將制約業務邏輯(在我的情況下,我只需要在一次事務中的少量操作成功完成後提交DB中的更改)。 – dimas

+0

@dimas:是的我錯了,其實你需要將它設置爲false。因爲默認情況下它是真的。編輯答案。 –

+0

我正在使用c3p0數據源,並且在那裏setAutoCommitOnClose proberty默認爲false。 (但是在dbcp BasicDataSource中,默認的自動提交值爲true,您是對的。)順便說一下,在我的情況下,回滾操作對於從業務層和DAO層運行的代碼正常工作,問題只發生在運行代碼來自AOP的建議。 – dimas

2

這可能是與建議的順序做 - 你會希望你的@Transaction相關意見,看看周圍效應(或前後)的相關記錄諮詢。如果您使用的是Spring AOP,則可以使用通知的order屬性來控制它 - 爲您的事務相關的建議提供最高的優先級,以便最後執行它。

+0

謝謝。我想到了這一點,並試圖改變建議的順序。但它沒有幫助。 – dimas

相關問題