在数据库的操作中事务的重要性相信大家都知道,能够保证数据的原子性,一致性,完整性等... 使用步骤相对蛮简单,下面就直接简要介绍 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("数据源名称"),例如下面代码:
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);
}
}
注意的是,
事务管理器可以有多个,当定义多个事务管理器时应指定名称,如:
@Bean("事务管理器名称"),以便在service中使用时明确指定哪个事务管理器;同时确保在启动类中扫描到该类所在的包;
三,在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 {
private static Log LOG = LogFactory.getLog(BizMemberServiceImpl.class);
private static final long serialVersionUID = 20180626010130L;
@Autowired
private MemberService 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 queryAllMember() {
return memberDaoService.queryAllMember();
}
@Override
public List 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中使用事务相对来说蛮简单的,可能难免有错漏之处,欢迎拍砖评论...