Spring_JDBCTemplate & 声明式事务

Spring的JdbcTemplate

  • JdbcTemplate是spring框架中提供的一个模板对象,是对原始繁琐的Jdbc API对象的简单封装。
  • 核心对象:JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
  • 核心方法:
int update(); 执行增、删、改语句
T queryForObject(); 查询一个
new BeanPropertyRowMapper<>(); 实现ORM映射封装
  1. 创建java项目,导入坐标
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
    </dependencies>
  1. 编写Account实体类
@Data
public class Account {
    private int id;
    private String name;
    private double money;
}

  1. 编写AccountDao接口和实现类
public interface AccountDao {
    /**
     * 转出
     *
     * @param name 转账人
     * @param money 转账金额
     */
    void out(String name, double money);

    /**
     * 转入
     *
     * @param name 入账人
     * @param money 入账金额
     */
    void in(String name, double money);
}
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void out(String name, double money) {
        String sql = "update account set money = money - ? where name = ?";
        jdbcTemplate.update(sql, money, name);
    }

    public void in(String name, double money) {
        String sql = "update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql, money, name);
    }
}
  1. 编写AccountService接口和实现类
public interface AccountService {
    /**
     * 转账
     *
     * @param outName 转账人
     * @param inName 入账人
     * @param money 金额
     */
    void transfer(String outName, String inName, double money);
}
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String outName, String inName, double money) {
        accountDao.out(outName, money);
        //int i = 1/0;
        accountDao.in(inName, money);
    }
}
  1. 编写spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
">

    <context:component-scan base-package="com.lagou"/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

</beans>
  1. 编写测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {
    @Autowired
    private AccountService accountService;

    @Test
    public void test1() {
        accountService.transfer("tom", "jerry", 1000d);
    }
}

Spring的事务

Spring中的事务控制方式

  • Spring的事务控制可以分为编程式事务控制声明式事务控制
  • 编程式:开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。
  • 声明式:开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP想。

编程式事务控制相关对象(了解)

PlatformTransactionManager

PlatformTransactionManager接口,是spring的事务管理器,里面提供了我们常用的操作事务的方法。

方法说明
TransactionStatus getTransaction(TransactionDefinition definition);获取事务的状态信息
void commit(TransactionStatus status);提交事务
void rollback(TransactionStatus status);回滚事务

注意:

  • PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。
  • Dao层技术是jdbcTemplate或mybatis时:实现类为DataSourceTransactionManager
  • Dao层技术是hibernate时:实现类为HibernateTransactionManager
  • Dao层技术是JPA时:实现类为JpaTransactionManager

TransactionDefinition

TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)

方法说明
int getIsolationLevel()获得事务的隔离级别
int getPropogationBehavior()获得事务的传播行为
int getTimeout()获得超时时间
boolean isReadOnly()是否只读
  1. 事务隔离级别
    设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。
参数说明
ISOLATION_DEFAULT使用数据库默认级别
ISOLATION_READ_UNCOMMITTED读未提交
ISOLATION_READ_COMMITTED读已提交
ISOLATION_REPEATABLE_READ可重复读
ISOLATION_SERIALIZABLE串行化
  1. 事务传播行为
    事务传播行为指的就是当一个业务方法【被】另一个业务方法调用时,应该如何进行事务控制。
参数说明
REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW新建事务,如果当前在事务中,把当前事务挂起
NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER以非事务方式运行,如果当前存在事务,抛出异常
NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED 类似的操作
  1. read-only(是否只读):建议查询时设置为只读
  2. timeout(超时时间):默认值是-1,没有超时限制。如果有,以秒为单位进行设置

TransactionStatus

TransactionStatus 接口提供的是事务具体的运行状态。

方法说明
boolean isNewTransaction()是否是新事务
boolean hasSavepoint()是否是回滚点
boolean isRollbackOnly()事务是否回滚
boolean isCompleted()事务是否完成

可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态

实现代码

  1. 配置文件
<!--事务管理器交给IOC--> 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
	<property name="dataSource" ref="dataSource"/> 
</bean>
  1. 业务层代码
@Service 
public class AccountServiceImpl implements AccountService { 
	@Autowired private AccountDao accountDao; 

	@Autowired private PlatformTransactionManager transactionManager; 

	@Override public void transfer(String outUser, String inUser, Double money) { 
		// 创建事务定义对象 
		DefaultTransactionDefinition def = new DefaultTransactionDefinition(); 
		// 设置是否只读,false支持事务 
		def.setReadOnly(false); 
		// 设置事务隔离级别,可重复读mysql默认级别 
		def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ); 
		// 设置事务传播行为,必须有事务 
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 
		// 配置事务管理器 
		TransactionStatus status = transactionManager.getTransaction(def); 
		try {
			// 转账 
			accountDao.out(outUser, money); 
			accountDao.in(inUser, money); 
			// 提交事务 
			transactionManager.commit(status); 
		} catch (Exception e) { 
			e.printStackTrace(); 
			// 回滚事务 
			transactionManager.rollback(status); 
		} 
	} 
}
  • PlatformTransactionManager 负责事务的管理,它是个接口,其子类负责具体工作
  • TransactionDefinition 定义了事务的一些相关参数
  • TransactionStatus 代表事务运行的一个实时状态

基于XML的声明式事务控制【重点】

在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。
声明式事务控制明确事项:

  1. 核心业务代码(目标对象) (切入点是谁?)
  2. 事务增强代码(Spring已提供事务管理器))(通知是谁?)
  3. 切面配置(切面如何配置?)

核心文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
">

    <context:component-scan base-package="com.lagou"/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <!--通知增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--定义事务的属性-->
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--aop配置-->
    <aop:config>
        <!--切面配置-->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.service.impl.AccountServiceImpl.*(..))"/>
    </aop:config>

    <tx:annotation-driven/>
</beans>

事务参数的配置详解

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
  • name:切点方法名称
  • isolation:事务的隔离级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读

CRUD常用配置

<tx:attributes> 
	<!--方法名以save开头-->
	<tx:method name="save*" propagation="REQUIRED"/> 
	<!--方法名以delete开头-->
	<tx:method name="delete*" propagation="REQUIRED"/> 
	<!--方法名以update开头-->
	<tx:method name="update*" propagation="REQUIRED"/> 
	<!--方法名以find开头-->
	<tx:method name="find*" read-only="true"/> 
	<!--任意方法-->
	<tx:method name="*"/> 
</tx:attributes>

增删改的传播行为一般都设置事务,查询一般只读

基于注解的声明式事务控制【重点】

增加事务注解

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
    public void transfer(String outName, String inName, double money) {
        accountDao.out(outName, money);
        int i = 1/0;
        accountDao.in(inName, money);
    }
}

修改spring核心配置文件,开启事务注解支持

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
">

    <context:component-scan base-package="com.lagou"/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

	<!--事务的注解支持--> 
	<tx:annotation-driven/>
</beans>

纯注解

核心配置类

@Configuration // 声明为spring配置类 
@ComponentScan("com.lagou") // 扫描包 
@Import(DataSourceConfig.class) // 导入其他配置类 
@EnableTransactionManagement // 事务的注解驱动 
public class SpringConfig { 
	@Bean 
	public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource) { 
		return new JdbcTemplate(dataSource);
	}

	@Bean("transactionManager") 
	public PlatformTransactionManager getPlatformTransactionManager(@Autowired DataSource dataSource) { 
		return new DataSourceTransactionManager(dataSource); 
	} 
}

数据源配置类

@PropertySource("classpath:jdbc.properties") 
public class DataSourceConfig { 
	@Value("${jdbc.driver}") 
	private String driver; 
	@Value("${jdbc.url}") 
	private String url; 
	@Value("${jdbc.username}") 
	private String username; 
	@Value("${jdbc.password}") 
	private String password; 
	
	@Bean 
	public DataSource getDataSource() { 
		DruidDataSource dataSource = new DruidDataSource(); 
		dataSource.setDriverClassName(driver); 
		dataSource.setUrl(url); 
		dataSource.setUsername(username); 
		dataSource.setPassword(password); 
		return dataSource; 
	} 
}

Spring集成web环境

ApplicationContext应用上下文获取方式

  • 应用上下文对象是通过 new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。
  • 在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

Spring提供获取应用上下文的工具

  • Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。

  • 所以我们需要做的只有两件事:

  1. 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
  2. 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

实现:
导入Spring集成web的坐标

	<dependency> 
		<groupId>org.springframework</groupId> 
		<artifactId>spring-context</artifactId> 
		<version>5.1.5.RELEASE</version> 
	</dependency> 
	<dependency> 
		<groupId>org.springframework</groupId> 
		<artifactId>spring-web</artifactId> 
		<version>5.1.5.RELEASE</version> 
	</dependency>

在web.xml配置ContextLoaderListener监听器

<!--全局参数--> 
<context-param> 
	<param-name>contextConfigLocation</param-name> 
	<param-value>classpath:applicationContext.xml</param-value>
</context-param> 
<!--Spring的监听器--> 
<listener> 
	<listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> 
</listener>

通过工具获得应用上下文对象

public class AccountServlet extends javax.servlet.http.HttpServlet {

    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        doGet(request,response);
    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        // classPathXmlApplicationContext对象存到servletContext域中
     /*   ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Account account = (Account) classPathXmlApplicationContext.getBean("account");
        System.out.println(account);
*/
        ApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
        Account account = (Account) webApplicationContext.getBean("account");
        System.out.println(account);

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值