springBoot中啟用事務管理
在資料庫的操作中事務的重要性相信大家都知道,能夠保證資料的原子性,一致性,完整性等... 使用步驟相對蠻簡單,下面就直接簡要介紹 springBoot 中應用事務功能;
一,定義/配置資料來源Bean並加入spring容器
如果使用預設的JDBC資料庫連線池則只需要配置資料庫的連線資訊(當然相應的資料庫依賴jar要引入pom.xml)即可;如果使用其它資料庫連線池,如Druid,則需要定義或配置相應資料來源,為後面方便使用,故@Bean("dataSource")指定名稱,同時@Primary指定以此資料來源為優先,如下程式碼:
package com.qyh.pro01.common; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter; import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import com.alibaba.druid.pool.DruidDataSource; /** * 配置啟用Druid連線池,定義 * @author shenzhenNBA * @since 2018.06.28 */ @Configuration //該註解類似於spring配置檔案 public class MyDataSourceConfig extends WebMvcAutoConfiguration { public MyDataSourceConfig() { super(); } private Logger logger = LoggerFactory.getLogger(MyDataSourceConfig.class); @Autowired private Environment env; @Value("${spring.datasource.type}") //連線資訊配置見application.properties private String dbType; @Value("${spring.datasource.url}") private String dbUrl; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${spring.datasource.driverClassName}") private String driverClassName; @Value("${spring.datasource.initialSize}") private int initialSize; @Value("${spring.datasource.minIdle}") private int minIdle; @Value("${spring.datasource.maxActive}") private int maxActive; @Value("${spring.datasource.maxWait}") private int maxWait; @Value("${spring.datasource.timeBetweenEvictionRunsMillis}") private int timeBetweenEvictionRunsMillis; @Value("${spring.datasource.minEvictableIdleTimeMillis}") private int minEvictableIdleTimeMillis; @Value("${spring.datasource.validationQuery}") private String validationQuery; @Value("${spring.datasource.testWhileIdle}") private boolean testWhileIdle; @Value("${spring.datasource.testOnBorrow}") private boolean testOnBorrow; @Value("${spring.datasource.testOnReturn}") private boolean testOnReturn; @Value("${spring.datasource.poolPreparedStatements}") private boolean poolPreparedStatements; @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}") private int maxPoolPreparedStatementPerConnectionSize; @Value("${spring.datasource.filters}") private String filters; @Value("{spring.datasource.connectionProperties}") private String connectionProperties; /** * 建立資料來源,並載入進spring容器,跟在spring的配置檔案中的載入bean一樣; */ @Bean("dataSource") //產生bean例項載入進srping容器中,指定Bean名稱 @Primary //當有多個實現時以此為優先為準 public DataSource getDataSource() throws Exception{ DruidDataSource datasource = new DruidDataSource(); //datasource.setDbType(dbType); //有些版本不支援該屬性 datasource.setUrl(dbUrl); //連線資訊配置見application.properties datasource.setDriverClassName(driverClassName); datasource.setUsername(username); datasource.setPassword(password); //configuration datasource.setInitialSize(initialSize); datasource.setMinIdle(minIdle); datasource.setMaxActive(maxActive); datasource.setMaxWait(maxWait); datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); datasource.setValidationQuery(validationQuery); datasource.setTestWhileIdle(testWhileIdle); datasource.setTestOnBorrow(testOnBorrow); datasource.setTestOnReturn(testOnReturn); datasource.setPoolPreparedStatements(poolPreparedStatements); datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); try { datasource.setFilters(filters); } catch (SQLException e) { logger.error("druid configuration initialization filter", e); } datasource.setConnectionProperties(connectionProperties); return datasource; } /** * 根據資料來源建立SqlSessionFactory,並載入進spring容器, * 同時配置MyBatis(庫表到Java實體的對映關係)用到的實體,實體別名和XML等 */ @Bean public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception{ SqlSessionFactoryBean sqlSFB = new SqlSessionFactoryBean(); sqlSFB.setDataSource(ds); //指定自定義的資料來源,這個必須用 sqlSFB.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage")); //指定對應的實體包,多個包之間逗號隔開,配置資訊見application.properties Resource configLocationResource = new PathMatchingResourcePatternResolver().getResource( env.getProperty("mybatis.configLocations")); sqlSFB.setConfigLocation(configLocationResource); //指定mybatis的本地配置檔案資源,目的是定義實體等別名,可以不用,如果不用對應配置檔案應註釋掉 Resource[] mapperLocations = new PathMatchingResourcePatternResolver().getResources( env.getProperty("mybatis.mapperLocations")); sqlSFB.setMapperLocations(mapperLocations); //指定mybatis的庫表到實體的對映xml檔案的mapper資源 return sqlSFB.getObject(); } }
確保在啟動類中掃描到該類所在的包;
二,定義/配置事務管理器Bean並加入spring容器
要啟用事務當然少不了定義事務管理器,需要在類前面使用註解 @EnableTransactionManagement 開啟事務並告知spring,同時作為配置類加註解 @Configuration 注入IOC容器,定義事務管理器 DataSourceTransactionManager 型別的bean,其引數中需要指定名稱的資料來源,如前一步所見,引數前可指定 @Qualifier("資料來源名稱"),例如下面程式碼:
注意的是,事務管理器可以有多個,當定義多個事務管理器時應指定名稱,如:@Bean("事務管理器名稱"),以便在service中使用時明確指定哪個事務管理器;同時確保在啟動類中掃描到該類所在的包;package com.qyh.pro01.common; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * 配置事務管理器 * @author shenzhenNBA * @since 2018.06.28 */ //開啟事務管理,確保在啟動類中@component掃描到該類 @EnableTransactionManagement @Configuration public class TransactionManagementConfig { //注意: @Qualifier 按名稱在IOC容器中找指定名稱的bean, @Bean //或者 @Bean("myTransactionManager") public PlatformTransactionManager platformTransactionManager( @Qualifier("dataSource") DataSource myDataSource) { return new DataSourceTransactionManager(myDataSource); } }
三,在service層中使用 @Transactional 註解應用事務
事務的應用相對比較簡單,加個 @Transactional 註解即可完成事務應用,一般使用在資料庫操作的service層的類中使用,使用的位置分兩種,一種是在類前,這個時候類中所有的方法預設都具有這種型別的事務;另一種應用在方法名稱前面,這時的事務會覆蓋類前面使用的事務,@Transactional註解只能應用到 public 方法才有效 (外部經過spring容器呼叫service的方法事務才生效,service類內部方法間相互呼叫事務不生效),@Transactional的使用格式如下
@Transactional(
value="可自定義的事務管理器", //等同於 transactionManager="",
propagation=Propagation.REQUIRED, //根據需要修改
timeout = -1, //時間大於零時,超過時間未完成則事務回滾
isolation = Isolation.DEFAULT, //隔離級別
readOnly = false, //是否只讀 true or false
rollbackFor = Exception.class, //或者它異常類
noRollbackFor = OutOfMemoryError.class //或者它異常類
)
事務應用例子,如下:
package com.qyh.pro01.service.impl;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import com.qyh.pro01.dao.MemberService;
import com.qyh.pro01.model.Member;
import com.qyh.pro01.param.MemberParam;
import com.qyh.pro01.service.BizMemberService;
/**
* 事務在service層中的使用
* @author shenzhenNBA
* @since 2018.06.28
*/
@Transactional(readOnly = true) //類中所有方法預設的事務
@Service("bizMemberService")
public class BizMemberServiceImpl implements BizMemberService<Member, Long> {
private static Log LOG = LogFactory.getLog(BizMemberServiceImpl.class);
private static final long serialVersionUID = 20180626010130L;
@Autowired
private MemberService<Member, Long> memberDaoService;
@Override
public Member getByUserRecId(Long recId) {
return memberDaoService.getByUserRecId(recId);
}
@Override
public Member getByRecId(Long recId) {
return memberDaoService.getMemberByRecId(recId);
}
/*
//完整格式
@Transactional(transactionManager="", //等同於 value="可自定義的事務管理器"
propagation=Propagation.REQUIRED, //根據需要修改
timeout = -1, //時間大於零時,超過時間未完成則事務回滾
isolation = Isolation.DEFAULT,
readOnly = false, //true or false
rollbackFor = Exception.class, //或者它異常類
noRollbackFor = OutOfMemoryError.class //獲取它異常類
)
*/
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int insert(Member t) throws Exception {
return memberDaoService.insert(t);
}
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int save(Member t) throws Exception {
// TODO Auto-generated method stub
return 0;
}
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int update(Member t) throws Exception {
return memberDaoService.update(t);
}
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int delete(Member t) throws Exception {
return memberDaoService.delete(t);
}
@Override
public Member getMemberByRecId(Long recId) {
return memberDaoService.getMemberByRecId(recId);
}
@Override
public Member getMemberByMemberId(String memberId) {
return memberDaoService.getMemberByMemberId(memberId);
}
@Override
public List<Member> queryAllMember() {
return memberDaoService.queryAllMember();
}
@Override
public List<Member> queryByMember(MemberParam memberParam) {
return memberDaoService.queryByMember(memberParam);
}
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int updateUserMoney(MemberParam memberParam) {
return memberDaoService.updateUserMoney(memberParam);
}
@Transactional(propagation=Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
public int deleteByMemberId(String memberId) {
return memberDaoService.deleteByMemberId(memberId);
}
}
同時確保在啟動類中掃描到該類所在的包;
四,啟動類中掃描到定義資料來源類,事務管理器類,service層的類所在的包
例子中可見很多地方使用到註解,所有有個共同特點,就是要使相應註解生效,則在啟動類中必須確保@ComponentScan掃描到各個相應的工作類所在的包;
後記,相對來說得益於springboot中各種強大的註解,使得在springboot中使用事務相對來說蠻簡單的,可能難免有錯漏之處,歡迎拍磚評論...