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("数据源名称"),例如下面代码:

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中使用事务相对来说蛮简单的,可能难免有错漏之处,欢迎拍砖评论...


你可能感兴趣的:(管理者/开发者,数据库,事务,aop,事务管理,@Transactional)