SpringJDBC模板&Spring事务

目录

一.SpringJDBC的入门

1.1 JDBC模板使用入门

1.2 使用Spring配置连接池、JDBC模板

二.使用开源的数据库连接池 

2.1 DBCP的使用

2.2 C3P0的使用

三.jdbc模板的常见操作

3.1 增删改

3.2 查询操作

3.2.1 查询某个属性

3.2.2 查询返回对象或集合

四.Spring事务管理

4.1 事务知识的回顾

4.1.1 事务的特性

4.1.2 如果不考虑隔离性引发安全性问题

4.1.3 解决读问题

4.2 Spring事务管理的API

4.2.1 PlatformTransactionManager:平台事务管理器

4.2.2 TransactionDefinition     :事务定义信息

4.2.3 TransactionStatus:事务的状态

4.2.4 事务管理的API的关系:

4.3 Spring的事务的传播行为

4.3.1 保证多个操作在同一个事务中

4.3.2 保证多个操作不在同一个事务中

4.3.3 嵌套式事务

4.4 搭建转账环境

4.5 Spring的事务管理:一类:编程式事务(需要手动编写代码)

4.6 Spring的事务管理:二类:声明式事务管理(通过配置实现)---AOP

4.6.1 XML方式的声明式事务管理

4.7 注解方式配置事务

五.Mysql与事务


一.SpringJDBC的入门

  • Spring是EE开发的一站式的框架,有EE开发的每层的解决方案。
  • Spring对持久层也提供了解决方案:ORM模块和JDBC的模板。
  • Spring提供了很多的模板用于简化开发:

1.1 JDBC模板使用入门

①创建项目,新建XML文件引入约束、jar包:数据库驱动,Spring的JDBC模板的jar包

<?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">

</beans>

②创建数据库和表

create database spring_jdbc;
use spring_jdbc;
create table account(
	id int primary key auto_increment,
	name varchar(20),
	money double
);

③使用jdbc模板保存数据

/**
 * JDBC模板的使用
 *
 */
public class JdbcDemo1 {

	@Test
	// jdbc模板的使用类似于Dbutils.
	public void demo1(){
		// 创建连接池:
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql:///spring_jdbc");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		// 创建jdbc模板
		JdbcTemplate jdbcTemplate = new JdbcTemplate();
		jdbcTemplate.setDataSource(dataSource);
		jdbcTemplate.update("insert into account values (null,?,?)", "赵冠希",10000d);
	}
}

1.2 使用Spring配置连接池、JDBC模板

①配置xml文件

	<!-- 配置Spring的内置的连接池======================== -->
 	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql:///spring_jdbc"/>
		<property name="username" value="root"/>
		<property name="password" value="abc"/>
	</bean>

	<!-- 配置Spring的JDBC的模板========================= -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean>

②测试类

引入spring_aop的jar包

二.使用开源的数据库连接池 

2.1 DBCP的使用

引入jar包

配置DBCP连接池

	<!-- 配置DBCP连接池=============================== -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql:///spring_jdbc"/>
		<property name="username" value="root"/>
		<property name="password" value="root"/>
	</bean>

2.2 C3P0的使用

引入c3p0连接池jar包

将数据库信息抽取到文件中

① 在src下建立一个属性文件 jdbc.properties 文件内容:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_jdbc
jdbc.username=root
jdbc.password=root

② 在applicationContext.xml中引入该属性文件

	<!-- 引入属性文件================================== -->
	<!-- 第一种方式通过一个bean标签引入的(很少) -->
 	<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="classpath:jdbc.properties"/>
	</bean>
	<!-- 第二种方式通过context标签引入的 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	<!-- 配置C3P0连接池=============================== -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>

三.jdbc模板的常见操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcDemo2 {
	@Resource(name="jdbcTemplate")
	private JdbcTemplate jdbcTemplate;
	
	...操作方法...
}

3.1 增删改

	// 保存操作
	public void demo1(){
		jdbcTemplate.update("insert into account values (null,?,?)", "何菊花",10000d);
	}
	// 修改操作
	public void demo2(){
		jdbcTemplate.update("update account set name = ? ,money = ? where id = ?", "何巨涛",2000d,6);
	}
	// 删除操作
	public void demo3(){
		jdbcTemplate.update("delete from account where id = ?", 6);
	}

3.2 查询操作

3.2.1 查询某个属性

@Test
// 查询操作:
public void demo4(){
	String name = jdbcTemplate.queryForObject("select name from account where id = ?", String.class, 5);
	System.out.println(name);
}

@Test
// 统计查询
public void demo5(){
	Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
	System.out.println(count);
}

3.2.2 查询返回对象或集合

实体类

public class Account {
	private Integer id;
	private String name;
	private Double money;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getMoney() {
		return money;
	}
	public void setMoney(Double money) {
		this.money = money;
	}
	@Override
	public String toString() {
		return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
	}
	
}

// 封装到一个对象中
public void demo6(){
	Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new MyRowMapper(), 5);
	System.out.println(account);
}

class MyRowMapper implements RowMapper<Account>{

	@Override
	public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
		Account account = new Account();
		account.setId(rs.getInt("id"));
		account.setName(rs.getString("name"));
		account.setMoney(rs.getDouble("money"));
		return account;
	}
	
}
// 查询多条记录
public void demo7(){
	List<Account> list = jdbcTemplate.query("select * from account", new MyRowMapper());
	for (Account account : list) {
		System.out.println(account);
	}
}

四.Spring事务管理

4.1 事务知识的回顾

  • 事务:逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败。

4.1.1 事务的特性

  • 原子性:事务不可分割
  • 一致性:事务执行前后数据完整性保持一致
  • 隔离性:一个事务的执行不应该受到其他事务的干扰
  • 持久性:一旦事务结束,数据就持久化到数据库

4.1.2 如果不考虑隔离性引发安全性问题

  • 读问题
    • 脏读                 :一个事务读到另一个事务未提交的数据
    • 不可重复读    :一个事务读到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致
    • 虚读、幻读    :一个事务读到另一个事务已经提交的insert的数据,导致一个事务中多次查询结果不一致。
  • 写问题
    • 丢失更新

4.1.3 解决读问题

  • 设置事务的隔离级别
  • Read uncommitted         :未提交读,任何读问题解决不了。
  • Read committed     :已提交读,解决脏读,但是不可重复读和虚读有可能发生。
  • Repeatable read     :重复读,解决脏读和不可重复读,但是虚读有可能发生。
  • Serializable               :解决所有读问题。

4.2 Spring事务管理的API

4.2.1 PlatformTransactionManager:平台事务管理器

  • 平台事务管理器:接口,是Spring用于管理事务的真正的对象。
    • DataSourceTransactionManager :底层使用JDBC管理事务
    • HibernateTransactionManager      :底层使用Hibernate管理事务

4.2.2 TransactionDefinition     :事务定义信息

  • 事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读

4.2.3 TransactionStatus:事务的状态

  • 事务状态:用于记录在事务管理过程中,事务的状态的对象。

4.2.4 事务管理的API的关系:

  • Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。

4.3 Spring的事务的传播行为

Spring中提供了七种事务的传播行为,A代表被调用者,B代表调用者

4.3.1 保证多个操作在同一个事务中

  • PROPAGATION_REQUIRED    :默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
  • PROPAGATION_SUPPORTS    :支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
  • PROPAGATION_MANDATORY  :如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。

4.3.2 保证多个操作不在同一个事务中

  • PROPAGATION_REQUIRES_NEW    :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
  • PROPAGATION_NOT_SUPPORTED  :如果A中有事务,将A的事务挂起。不使用事务管理。
  • PROPAGATION_NEVER          :如果A中有事务,报异常。

4.3.3 嵌套式事务

  • PROPAGATION_NESTED         :嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

4.4 搭建转账环境

①创建表

create table account(
	id int primary key auto_increment,
	name varchar(20),
	money double
);

②创建Service的接口和实现类

/**
 * 转账的业务层的接口
 *
 */
public interface AccountService {

	public void transfer(String from,String to,Double money);
	
}
/**
 * 转账的业务层的实现类
 *
 */
public class AccountServiceImpl implements AccountService {

	// 注入DAO:
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	/**
	 * from:转出账号
	 * to:转入账号
	 * money:转账金额
	 */
	public void transfer( String from,  String to,  Double money) {
		
		accountDao.outMoney(from, money);
//		int d = 1/0;
		accountDao.inMoney(to, money);
		
	}

}

③创建DAO的接口和实现类,这里的实现类继承了JdbcDaoSupport,需要在配置中增加datasoure属性即可获得jdbc模板

/**
 * 转账的DAO的接口
 *
 */
public interface AccountDao {
	public void outMoney(String from ,Double money);
	public void inMoney(String to ,Double money);
}
/**
 * 转账的DAO的实现类
 *
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

	@Override
	public void outMoney(String from, Double money) {
		this.getJdbcTemplate().update("update account set money = money - ? where name = ?", money,from);
	}

	@Override
	public void inMoney(String to, Double money) {
		this.getJdbcTemplate().update("update account set money = money + ? where name = ?", money ,to);
	}

}

④配置 jdbc.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mytest
jdbc.username=root
jdbc.password=root

⑤配置 applicationContext.xml 

<?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标签引入的 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 配置C3P0连接池=============================== -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
		<!-- 配置Service============= -->
	<bean id="accountService" class="spring.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>
		<!-- 配置DAO================= -->
	<bean id="accountDao" class="spring.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	
</beans>

⑥测试类

/**
 * 测试转账的环境
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class test {

	@Resource(name="accountService")
	private AccountService accountService;
	
	@Test
	public void demo1(){
		accountService.transfer("张三", "李四", 1000d);
		System.out.println("OK");
	}
}

如上只是实现了转账的业务逻辑,如果直接应用还会存在问题,比如转账过程中遇到了停电断网等问题导致整个
业务只完成了一半(完成了扣钱),加钱操作还没完成。
解决问题方式:为转账业务添加事务,出现异常进行回滚操作

4.5 Spring的事务管理:一类:编程式事务(需要手动编写代码)

① 修改转账环境中的业务实现类

/**
 * 转账的业务层的实现类
 *
 */
public class AccountServiceImpl implements AccountService {

	// 注入DAO:
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	// 注入事务管理的模板
	private TransactionTemplate trsactionTemplate;

	public void setTrsactionTemplate(TransactionTemplate trsactionTemplate) {
		this.trsactionTemplate = trsactionTemplate;
	}

	@Override
	/**
	 * from:转出账号
	 * to:转入账号
	 * money:转账金额
	 */
	public void transfer(final String from, final String to, final Double money) {
		
		trsactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				accountDao.outMoney(from, money);
				int d = 1/0;
				accountDao.inMoney(to, money);
			}
		});
		
	}

}

⑤配置平台事务管理器

	<!-- 配置平台事务管理器============================= -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>	

⑥ Spring提供了事务管理的模板类

4.6 Spring的事务管理:二类:声明式事务管理(通过配置实现)---AOP

4.6.1 XML方式的声明式事务管理

① 还原AccountService的转账环境

/**
 * 转账的业务层的实现类
 * @author jt
 *
 */
public class AccountServiceImpl implements AccountService {

	// 注入DAO:
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	/**
	 * from:转出账号
	 * to:转入账号
	 * money:转账金额
	 */
	public void transfer( String from,  String to,  Double money) {
		
		accountDao.outMoney(from, money);
//		int d = 1/0;
		accountDao.inMoney(to, money);
		
	}

}

② 配置xml

<?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标签引入的 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 配置C3P0连接池=============================== -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
		<!-- 配置Service============= -->
	<bean id="accountService" class="spring.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>
		<!-- 配置DAO================= -->
	<bean id="accountDao" class="spring.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"/>
	</bean>
		<!-- 配置平台事务管理器============================= -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<!-- 配置事务的增强=============================== -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 事务管理的规则 -->
			<!-- <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="find*" read-only="true"/> -->
			<tx:method name="*" propagation="REQUIRED" read-only="false"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- aop的配置 -->
	<aop:config>
		<aop:pointcut expression="execution(* spring.service.impl.AccountServiceImpl.*(..))" id="pointcut1"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
	</aop:config>
		
	
</beans>

4.7 注解方式配置事务

①还原AccountServiceImpl并添加注解

/**
 * 转账的业务层的实现类
 *
 */
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {

	// 注入DAO:
	private AccountDao accountDao;
	
	public void setAccountDao(AccountDao accountDao) {
		this.accountDao = accountDao;
	}
	
	@Override
	/**
	 * from:转出账号
	 * to:转入账号
	 * money:转账金额
	 */
	public void transfer( String from,  String to,  Double money) {
		
		accountDao.outMoney(from, money);
		int d = 1/0;
		accountDao.inMoney(to, money);
		
	}

}

②配置xml文件

<?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标签引入的 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 配置C3P0连接池=============================== -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
		<!-- 配置Service============= -->
	<bean id="accountService" class="spring.service.impl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>
		<!-- 配置DAO================= -->
	<bean id="accountDao" class="spring.dao.impl.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"/>
	</bean>
		<!-- 配置平台事务管理器============================= -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<!-- 开启注解事务================================ -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
		
	
</beans>

五.Mysql与事务

MySQL默认的存储引擎是MyISAM,不支持事务和外键,需要改为支持事务和外键的InnoDB

  • 查看mysql 提供了哪些存储引擎: show engines;
  • 看某个表用了什么引擎(在显示结果里参数engine后面的就表示该表当前用的存储引擎): show create table 表名
  • 查看当前存储引擎:show engines;
  • 查看你的mysql当前默认的存储引擎: show variables like '%storage_engine%';

解决方式①:

将my-small.ini另存为my.ini,在[mysqld]最后添加为上default-storage-engine=InnoDB,

重启服务,数据库默认的引擎修改为InnoDB

解决方式②:

更改表的存储引擎:alter table 表名 engine = InnoDB;

猜你喜欢

转载自blog.csdn.net/qq_30162219/article/details/86647638