import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
* 註解聲明式事務
* @author 胡漢三
*
* 2018年7月11日 下午2:52:49
*/
@Configuration
public class TxConfig {
/*事務攔截類型*/
@Bean("txSource")
public TransactionAttributeSource transactionAttributeSource(){
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
/*只讀事務,不做更新操作*/
RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
readOnlyTx.setReadOnly(true);
readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
/*當前存在事務就使用當前事務,當前不存在事務就創建一個新的事務*/
RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED,
Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
requiredTx.setTimeout(5);
Map<String, TransactionAttribute> txMap = new HashMap<>();
txMap.put("add*", requiredTx);
txMap.put("save*", requiredTx);
txMap.put("insert*", requiredTx);
txMap.put("update*", requiredTx);
txMap.put("edit*", requiredTx);
txMap.put("del*", requiredTx);
txMap.put("get*", readOnlyTx);
txMap.put("load*", readOnlyTx);
txMap.put("find*", readOnlyTx);
txMap.put("query*", readOnlyTx);
source.setNameMap( txMap );
return source;
}
/**切面攔截規則 參數會自動從容器中注入*/
@Bean
public AspectJExpressionPointcutAdvisor pointcutAdvisor(TransactionInterceptor txInterceptor){
AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
pointcutAdvisor.setAdvice(txInterceptor);
pointcutAdvisor.setExpression("execution (* com.deeps.gzsti.service.*.impl.*.*(..))");
return pointcutAdvisor;
}
/*事務攔截器*/
@Bean("txInterceptor")
TransactionInterceptor getTransactionInterceptor(PlatformTransactionManager tx){
return new TransactionInterceptor(tx , transactionAttributeSource()) ;
}
}
上面是事務管理的配置,而後在我一個service.impl類中進行了一條正確的插入,一條錯誤的插入,發現每次都會往數據庫中插入數據,明明報錯了就是不回滾。
public boolean saveRole(Role role) throws Exception{
// TODO Auto-generated method stub
roleMapper.saveRole(role);
return roleMapper.saveRole(role);
}
因爲編碼是唯一鍵,重複插入會報錯。剛剛開始以爲是tx切面沒有切到,後來搜索了一下才發現是shiro在啓動配置的時候Spring還沒啓動。
解決辦法:
新建一個spring的監聽來設置realm。
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* spring初始化完成後設置自己的realm
* @author 胡漢三
*
* 2018年7月13日 下午1:49:37
*/
@Component
public class SpringEventListener {
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
DefaultWebSecurityManager manager = (DefaultWebSecurityManager) context.getBean("securityManager");
AuthorizingRealm realm = (AuthorizingRealm) context.getBean("authRealm");
realm.setCredentialsMatcher(new CredentialsMatcher());
manager.setRealm(realm);
}
}
並且一定要註釋掉原來shiro配置裏面的realm:
@Bean(name="securityManager")
public SecurityManager securityManager() {
System.err.println("--------------shiro已經加載----------------");
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
//manager.setRealm(authRealm);
// 自定義緩存實現 使用redis
manager.setCacheManager(cacheManager());
// 自定義session管理 使用redis
manager.setSessionManager(SessionManager());
//記住我
manager.setRememberMeManager(rememberMeManager());
return manager;
}
一定要註釋shiro配置類中securityManager裏面的manager.setRealm。這樣事務就正常了!當然數據庫要注意一些數據庫引擎是否支持事務!