目录
1.主要内容
2.Spring整合JDBC环境
Spring框架出路提供IOC与AOP核心功能外,同样提供了基于JDBC的数据访问功能,使得访问持久层数据更加方便。使用SpringJDBC环境,首先需要一套Spring整合JDBC的环境。
2.1.添加依赖坐标
<!-- spring 框架坐标依赖添加-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- spring 测试环境 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.4.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- spring jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- spring 事务-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
<!-- mysql 驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- c3p0-->
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
2.2.添加jdbc文件
# 驱动名
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库连接
jdbc.url=jdbc:mysql://localhost:3306/(数据库名称)?
useUnicode=true&characterEncoding=utf8&serverTime
zone=GMT%2B8&useSSL=false
# 数据库用户名称
jdbc.user=(数据库账号)
# 数据库用户密码
jdbc.password=(数据库密码)
以下为可选配置
# 指定连接池的初始化连接数。取值应在minPoolSize 与
maxPoolSize 之间.Default:3
initialPoolSize=20
# 指定连接池中保留的最大连接数. Default:15
maxPoolSize=100
# 指定连接池中保留的最小连接数
minPoolSize=10
# 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢
弃。 Default:0
maxIdleTime=600
# 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.
Default:3
acquireIncrement=5
# JDBC的标准,用以控制数据源内加载的PreparedStatements数量。
maxStatements=5
# 每60秒检查所有连接池中的空闲连接.Default:0
idleConnectionTestPeriod=60
2.3.修改spring文件
<!-- 加载properties 配置文件,用来读取jdbc.properties
文件中的数据 -->
<context:property-placeholder
location="jdbc.properties" />
spring.xml
<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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Spring自动扫描注解的配置 -->
<context:component-scan base-package="com.liuxidong"/>
<!-- 加载properties 配置文件 -->
<context:property-placeholder location="jdbc.properties"/>
</beans>
2.4.配置数据源
2.4.1.C3P0数据源配置
<!-- 配置 c3p0 数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- property标签的value属性对应的是jdbc.properties中的值 -->
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
2.4.2.DBCP数据源配置
<!-- 配置dbcp数据源-->
<bean id="myDataSource"
class="org.apache.commons.dbcp2.BasicDataSource"
>
<property name="driverClassName"
value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}"/>
<property name="username"
value="${jdbc.user}"/>
<property name="password"
value="${jdbc.password}"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以将
已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --
>
<property name="maxIdle" value="2"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就
会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 --
>
<property name="minIdle" value="1"/>
</bean>
2.5.模板类配置
Spring把JDBC中重复的操作建立成了一个模板类:org.springframework.jdbc.core.JdbcTemplate
<!-- 配置JdbcTemplate实例,并注入一个dataSource数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"> </property>
</bean>
2.6.JDBC测试
2.6.1.创建指定数据库
2.6.2.创建数据表
2.6.3.使用JUnit测试
通过junit测试jdbcTemplate bean是否获取到
2.6.3.1.JUnit测试
public class SpringJdbcTest01 {
@Test
public void test01(){
// 得到上下文环境
ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("spring.xml");
// 得到模版类
JdbcTemplate jdbcTemplate = (JdbcTemplate)c.getBean("jdbcTemplate");
// 定义sql
String sql = "select count(1) from tb_account where user_id = 1";
// 执行查询操作
Integer total = jdbcTemplate. queryForObject(sql, Integer.class);
System.out.println("总记录数: " + total);
}
}
2.6.3.2.简单封装
public class SpringJdbcTest02 {
private JdbcTemplate jdbcTemplate;
@Before
public void init(){
// 得到上下文环境
ClassPathXmlApplicationContext c = new ClassPathXmlApplicationContext("spring.xml");
// 得到模版类
jdbcTemplate = (JdbcTemplate)c.getBean("jdbcTemplate");
}
@Test
public void test01(){
// 定义sql
String sql = "select count(1) from tb_account where user_id = 1";
// 执行查询操作
Integer total = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("总记录数: " + total);
}
}
2.6.3.3.注解封装
@RunWith
就是一个运行器
@RunWith(JUnit4.class) 就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class) 让测试
运行于Spring测试环境
@ContextConfiguration
Spring整合JUnit4测试时,使用注解引入多个配置文件
@RunWith(SpringJUnit4ClassRunner.class) // 将测试运行在spring测试环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的配置文件
public class SpringJdbcTest03 {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void test01(){
// 定义sql
String sql = "select count(1) from tb_account where user_id = 1";
// 执行查询操作
Integer total = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("总记录数: " + total);
}
}
2.6.3.4.通用封装
定义一个父类,设置通用的配置信息
@RunWith(SpringJUnit4ClassRunner.class) // 将测试运行在spring测试环境中
@ContextConfiguration(locations = {"classpath:spring.xml"}) // 设置要加载的配置文件
public class BaseTest {
}
继承通用的测试类
public class SpringJdbcTest04 extends BaseTest {
@Resource
private JdbcTemplate jdbcTemplate;
@Test
public void test01(){
// 定义sql
String sql = "select count(1) from tb_account where user_id = 1";
// 执行查询操作
Integer total = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("总记录数: " + total);
}
}
3.持久层账户模块操作
当完成Spring JDBC环境集成后,这里使用Spring JDBC完成账户单表crud操作。
3.1.账户接口方法定义
3.1.1.定义实体类
package com.liuxidong.po;
import java.util.Date;
/*
* 用户账户类
* */
public class Account {
private Integer accountID; // 账户ID 主键
private String accountName; // 账户名称
private String accountType; // 账户类型
private Double money; // 账户金额
private String remark; // 账户备注
private Date createTime; // 创建时间
private Date updateTime; // 修改时间
private Integer userId; // 用户ID
public Account() {
}
public Account(String accountName, String accountType, Double money, String remark, Integer userId) {
this.accountName = accountName;
this.accountType = accountType;
this.money = money;
this.remark = remark;
this.userId = userId;
}
public Integer getAccountID() {
return accountID;
}
public void setAccountID(Integer accountID) {
this.accountID = accountID;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
@Override
public String toString() {
return "Account{" +
"accountID=" + accountID +
", accountName='" + accountName + '\'' +
", accountType='" + accountType + '\'' +
", money=" + money +
", remark='" + remark + '\'' +
", createTime=" + createTime +
", updateTime=" + updateTime +
", userId=" + userId +
'}';
}
}
3.1.2.定义接口类
package com.liuxidong.dao;
import com.liuxidong.po.Account;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author lxd
* #Description IAccountDao
* #Date: 2023/4/12 20:15
*/
/**
* 账户模块
* 1.添加账户
* 添加账户记录 返回受影响的行数
* 添加账户记录 返回主键
* 批量添加账户记录 返回受影响的行数
* 2.修改账户
* 修改账户记录 返回受影响的行数
* 批量修改账户记录 返回受影响的行数
* 3.删除账户
* 删除账户记录 返回受影响的行数
* 批量删除账户记录 返回受影响的行数
* 4.查询账户
* 查询指定账户的总记录数 返回总记录数
* 查询指定账户的账户详情 返回账户对象
* 多条件查询指定用户的账户列表 返回账户集合
* */
public interface IAccountDao {
/**
* 添加账户
* 添加账户记录 返回受影响的行数
* @Author lxd
* @Param account
* @return int
**/
public int addAccount(Account account);
/**
* 添加账户
* 添加账户记录 返回主键
* @Author lxd
* @Param account
* @return int
**/
public int addAccountHasKey(Account account);
/**
* 添加账户
* 批量添加账户记录 返回受影响的行数
* @Author lxd
* @Param accounts
* @return int
**/
public int addAccountBatch(List<Account> accounts);
/**
* 查询账户
* 查询指定用户的账户的总记录数 返回总记录对象
* @Author lxd
* @Param userId
* @return
**/
public int queryAccountCount(int userId);
/**
* 查询用户
* 查找指定账户的账户详情 返回账户对象
* @Author lxd
* @Param account
* @return
**/
public Account queryAccountById(int accountId);
/**
* 查询账户
* 多条件查询指定用户的账户列表,返回账户集合
* @param userId 指定用户的ID
* @param accountName 账户名称(模糊查询)
* @param accountType 账户类型
* @param createTime 创建时间(大于当前时间)
* @return
*/
public List<Account> queryAccountByParams(Integer userId, String accountName,String accountType, String createTime);
/**
* 修改账户
* 修改账户记录,返回受影响的行数
* @param account
* @return
*/
public int updateAccount(Account account);
/**
* 修改账户
* 批量修改账户记录,返回受影响的行数
* @param accounts
* @return
*/
public int updateAccountBatch(List<Account> accounts);
/**
* 删除账户
* 删除账户记录,返回受影响的行数
* @param accountId
* @return
*/
public int deleteAccount(int accountId);
/**
* 删除账户
* 批量删除账户记录,返回受影响的行数
* @param ids
* @return
*/
public int deleteAccountBatch(Integer[] ids);
/**
* 支出
* @param accountId
* @param money
* @return
*/
public int outAccount(Integer accountId, Double money);
/**
* 收入
* @param accountId
* @param money
* @return
*/
public int inAccount(Integer accountId, Double money);
}
3.1.3.定义接口实现类
AccountDaoImpl.java
package com.liuxidong.dao.impl;
import com.liuxidong.dao.IAccountDao;
import com.liuxidong.po.Account;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* @author lxd
* #Description AccountDaoImpl
* #Date: 2023/4/12 20:45
*/
@Repository
public class AccountDaoImpl implements IAccountDao {
// 注入JdbcTemplate模板
@Resource
private JdbcTemplate jdbcTemplate;
/** @Author lxd
* 添加账户记录返回受影响的行数
* @Param account
* @return
**/
@Override
public int addAccount(Account account) {
// 定义sql语句
String sql = "insert into tb_account (account_name,account_type,money,remark," +
" create_time,update_time,user_id) values (?,?,?,?,now(),now(),?)";
// 设置参数
Object[] objs = {account.getAccountName(),account.getAccountType(),account.getMoney(),
account.getRemark(),account.getUserId()};
int row = jdbcTemplate.update(sql,objs);
return row;
}
/**
* 添加账户记录,返回主键
* @param account
* @return
*/
@Override
public int addAccountHasKey(Account account) {
// 定义sql语句
String sql = "insert into tb_account (account_name,account_type,money,remark," +
" create_time,update_time,user_id) values (?,?,?,?,now(),now(),?)";
// 定义KeyHolder对象 获取记录的主键值
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
// 预编译sql语句,并设置返回主键
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 设置参数
ps.setString(1,account.getAccountName());
ps.setString(2,account.getAccountType());
ps.setDouble(3,account.getMoney());
ps.setString(4,account.getRemark());
ps.setInt(5,account.getUserId());
// 返回预编译对象
return ps;
}, keyHolder);
// 得到返回的主键
int key = keyHolder.getKey().intValue();
return key;
}
/**
* 批量添加账户记录,返回受影响的行数
* @param accounts
* @return
*/
@Override
public int addAccountBatch(List<Account> accounts) {
// 定义sql语句
String sql = "insert into tb_account (account_name,account_type,money,remark," +
" create_time,update_time,user_id) values (?,?,?,?,now(),now(),?)";
int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Account account = accounts.get(i);
// 设置参数
ps.setString(1,account.getAccountName());
ps.setString(2,account.getAccountType());
ps.setDouble(3,account.getMoney());
ps.setString(4,account.getRemark());
ps.setInt(5,account.getUserId());
}
@Override
public int getBatchSize() {
return accounts.size();
}
}).length;
return rows;
}
/**
* 查询指定用户的账户总记录数,返回总数量
* @param userId
* @return
*/
@Override
public int queryAccountCount(int userId) {
// 定义sql语句
String sql = "select count(1) from tb_account where user_id = ? ";
// 查询方法
int count = jdbcTemplate.queryForObject(sql, Integer.class, userId);
return count;
}
/**
* 查询指定账户记录详情,返回账户对象
* @param accountId
* @return
*/
@Override
public Account queryAccountById(int accountId) {
// 定义sql语句
String sql = "select * from tb_account where account_id = ? ";
// 查询对象
Account account = jdbcTemplate.queryForObject(sql, (ResultSet rs, int i) -> {
Account acc = new Account();
acc.setAccountID(accountId);
acc.setAccountName(rs.getString("account_name"));
acc.setAccountType(rs.getString("account_type"));
acc.setMoney(rs.getDouble("money"));
acc.setRemark(rs.getString("remark"));
acc.setUserId(rs.getInt("user_id"));
acc.setCreateTime(rs.getDate("create_time"));
acc.setUpdateTime(rs.getDate("update_time"));
return acc;
},accountId);
return account;
}
/**
* 多条件查询指定用户的账户记录列表,返回账户集合
* @param userId 指定用户的ID
* @param accountName 账户名称(模糊查询)
* @param accountType 账户类型
* @param createTime 创建时间(大于当前时间)
* @return
*/
@Override
public List<Account> queryAccountByParams(Integer userId, String accountName, String accountType, String createTime) {
// 定义sql语句
String sql = "select * from tb_account where user_id = ? ";
// 定义参数列表
List<Object> params = new ArrayList<>();
params.add(userId);
// 判断参数是否为空,如果不为空,拼接sql语句及设置对应的参数
// 账户名称
if (StringUtils.isNotBlank(accountName)){
// 拼接sql语句
sql += " and account_name like concat('%',?,'%') ";
// 设置参数
params.add(accountName);
}
// 账户类型
if (StringUtils.isNotBlank(accountType)){
// 拼接sql语句
sql += " and account_type = ? ";
// 设置参数
params.add(accountType);
}
// 创建时间
if (StringUtils.isNotBlank(createTime)){
// 拼接sql语句
sql += " and create_time < ? ";
// 设置参数
params.add(createTime);
}
// 将集合转换为数组
Object[] objs = params.toArray();
// 查询集合
List<Account> accountList = jdbcTemplate.query(sql, objs, (ResultSet rs, int i) -> {
Account acc = new Account();
acc.setAccountID(rs.getInt("account_id"));
acc.setAccountName(rs.getString("account_name"));
acc.setAccountType(rs.getString("account_type"));
acc.setMoney(rs.getDouble("money"));
acc.setRemark(rs.getString("remark"));
acc.setUserId(rs.getInt("user_id"));
acc.setCreateTime(rs.getTimestamp("create_time"));
acc.setUpdateTime(rs.getTimestamp("update_time"));
return acc;
});
return accountList;
}
/**
* 修改账户,返回受影响的行数
* @param account
* @return
*/
@Override
public int updateAccount(Account account) {
// 定义sql
String sql = "update tb_account set account_name = ? , account_type = ? , money = ? , remark = ? , " +
" update_time = now(), user_id = ? where account_id = ?";
// 设置参数
Object[] objs = {account.getAccountName(),account.getAccountType(),account.getMoney(),
account.getRemark(),account.getUserId(),account.getAccountID()};
int row = jdbcTemplate.update(sql, objs);
return row;
}
/**
* 批量修改账户记录,返回受影响的行数
* @param accounts
* @return
*/
@Override
public int updateAccountBatch(List<Account> accounts) {
// 定义sql语句
String sql = "update tb_account set account_name = ? , account_type = ? , money = ? , remark = ? , " +
" update_time = now(), user_id = ? where account_id = ?";
int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Account account = accounts.get(i);
// 设置参数
ps.setString(1,account.getAccountName());
ps.setString(2,account.getAccountType());
ps.setDouble(3,account.getMoney());
ps.setString(4,account.getRemark());
ps.setInt(5,account.getUserId());
ps.setInt(6,account.getAccountID());
}
@Override
public int getBatchSize() {
return accounts.size();
}
}).length;
return rows;
}
/**
* 删除账户记录,返回受影响的行数
* @param accountId
* @return
*/
@Override
public int deleteAccount(int accountId) {
// 定义SQL语句
String sql = "delete from tb_account where account_id = ?";
int row = jdbcTemplate.update(sql,accountId);
return row;
}
/**
* 批量删除账户记录,返回受影响的行数
* @param ids
* @return
*/
@Override
public int deleteAccountBatch(Integer[] ids) {
// 定义SQL语句
String sql = "delete from tb_account where account_id = ?";
int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, ids[i]);
}
@Override
public int getBatchSize() {
return ids.length;
}
}).length;
return rows;
}
/**
* 支出
* @param accountId
* @param money
* @return
*/
@Override
public int outAccount(Integer accountId, Double money) {
String sql = "update tb_account set money = money - ? where account_id = ? ";
Object[] objs = {money, accountId};
return jdbcTemplate.update(sql,objs);
}
/**
* 收入
* @param accountId
* @param money
* @return
*/
@Override
public int inAccount(Integer accountId, Double money) {
String sql = "update tb_account set money = money + ? where account_id = ? ";
Object[] objs = {money, accountId};
return jdbcTemplate.update(sql,objs);
}
}
4.Spring事务控制
4.1.转账场景模拟实现
4.1.1.接口方法定义
/**
* 支出
* @param accountId
* @param money
* @return
*/
public int outAccount(Integer accountId, Double money);
/**
* 收入
* @param accountId
* @param money
* @return
*/
public int inAccount(Integer accountId, Double money);
4.1.2.实现对应接口
/**
* 支出
* @param accountId
* @param money
* @return
*/
@Override
public int outAccount(Integer accountId, Double money) {
String sql = "update tb_account set money = money - ? where account_id = ? ";
Object[] objs = {money, accountId};
return jdbcTemplate.update(sql,objs);
}
/**
* 收入
* @param accountId
* @param money
* @return
*/
@Override
public int inAccount(Integer accountId, Double money) {
String sql = "update tb_account set money = money + ? where account_id = ? ";
Object[] objs = {money, accountId};
return jdbcTemplate.update(sql,objs);
}
4.1.3.转账方法实现
@Service
public class UserService {
@Resource
private IAccountDao accountDao;
@Transactional(propagation = Propagation.REQUIRED)
public int transferAccounts(Integer outId , Integer inId , Double money){
int code = 0; // 1 成功 0 失败
// 账户A 支出,修改账户金额,返回受影响的行数
int outRow = accountDao.outAccount(outId, money);
int i=1/0;
// 账户B 收入,修改账户金额,返回受影响的行数
int inRow = accountDao.inAccount(inId,money);
if (outRow == 1 && inRow == 1){
code = 1;
}
return code;
}
}
4.2.Spring事务概念
4.2.1.事务的四大特性
- 原子性(Atomicity)
共生死,要么全部成功,要么全部失败
- 一致性(Consistency)
事务在执行前后,数据库中数据要保持一致性状态。(如转账的过程 账户操作后数据必须保持一致)
- 隔离性(Isolation)
事务与事务之间的执行应当是相互隔离互不影响的。(多个角色对统一记录进行操作必须保证没有任何干扰),当然没有影响是不可能的,为了让影响级别降到最低,通过隔离级别加以限制:
- 持久性(Durability)
事务提交完毕后,数据库中的数据的改变是永久的
4.2.2.Spring事务核心接口
Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利我们理解事务,下面通过讲解Spring的事务接口来了解Spring实现事务的具体策略。
public interface PlatformTransactionManager(){
// 由 TransactionDefinition 得到TransactionStatus 对象
TransactionStatus
getTransaction(TransactionDefinition definition)
throws TransactionException;
// 提交
void commit(TransactionStatus status) throws
TransactionException;
// 回滚
void rollback(TransactionStatus status)
throws TransactionException;
}
4.2.2.1.JDBC事务
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSo
urceTransactionManager">
<property name="dataSource" ref="dataSource"
/>
</bean
4.2.2.2.Hibernate事务
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.Hiberna
teTransactionManager">
<property name="sessionFactory"
ref="sessionFactory" />
</bean>
4.2.2.3.JAVA持久化API事务
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransaction
Manager">
<property name="sessionFactory"
ref="sessionFactory" />
</bean>
4.2.2.4.Java原生API事务
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTra
nsactionManager">
<property name="transactionManagerName"
value="java:/TransactionManager" />
</bean>
4.3.Spring事务控制配置
通过jdbc持久化事务,对于事务配置实现由两种方式:XML配置,注解配置
4.3.1.XML配置
4.3.1.1.添加命名空间
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://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">
</beans>
4.3.1.2.设置aop代理
<!-- 开启AOP代理 -->
<aop:aspectj-autoproxy />
4.3.1.3.配置事务管理器
<!-- 事务管理器定义 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataS
ourceTransactionManager">
<!--数据源 -->
<property name="dataSource" ref="dataSource">
</property>
</bean>
4.3.1.4.配置事务相关通知
<!-- 配置事务通知 transaction-manager属性表示这个事
务通知是哪个事务管理器管理的-->
<!--
tx:method的属性:
name
是必须的,表示与事务属性关联的方法名(业务方
法名),对切入点进行细化。
通配符(*)可以用来指定一批关联到相同的事务
属性的方法。
如:'get*'、'handle*'、'on*Event'等等.
propagation
不是必须的,默认值是REQUIRED
表示事务传播行为, 包括:
REQUIRED,SUPPORTS,MANDATORY,NEVER
REQUIRES_NEW,NOT_SUPPORTED,NESTED
isolation
不是必须的,默认值DEFAULT
表示事务隔离级别(数据库的隔离级别)
timeout
不是必须的,默认值-1(永不超时)
表示事务超时的时间(以秒为单位)
read-only
不是必须的,默认值false不是只读的
表示事务是否只读
rollback-for
不是必须的
表示将被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException
'
no-rollback-for
不是必须的
表示不被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException
'
任何 RuntimeException 将触发事务回滚
-->
<tx:advice id="txAdvice" transactionmanager="txManager">
<!--对以add update delete query开头的所有方法进
行事务处理-->
<tx:attributes>
<!--定义什么方法需要使用事务 name代表的是方法
名(或方法匹配)-->
<!-- 匹配以 add 开头的所有方法均加入事务 -->
<tx:method name="add*propagation="REQUIRED" />
<!-- 匹配以 update 开头的所有方法均加入事务 -->
<tx:method name="update*propagation="REQUIRED" />
<!-- 匹配以 delete 开头的所有方法均加入事务 -->
<tx:method name="delete*propagation="REQUIRED" />
<!-- 匹配以 query 开头的所有方法均加入事务 -->
<tx:method name="query*" readonly="true" />
</tx:attributes>
</tx:advice>
4.3.1.5.配置aop
<!-- 配置AOP-->
<aop:config>
<aop:pointcut id="cut" expression="execution(* com.liuxidong.service..*.*(..))"/>
<!-- 设置通知-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="cut"/>
</aop:config>
4.3.2.注解配置
4.3.2.1.配置事务管理器
<!-- spring 注解式事务声明 -->
<!-- 事务管理器定义 -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSo
urceTransactionManager">
<property name="dataSource" ref="dataSource">
</property>
</bean>
4.3.2.2.配置注解支持
<tx:annotation-driven transactionmanager="txManager"/>
4.3.2.3.方法上加入事务注解
@Override
@Transactional(propagation=Propagation.REQUIRED)
public void saveUser(String userName,String
userPwd){
User user1=new User();
user1.setUserName(userName);
user1.setUserPwd(userPwd);
userDao.saveUser(user1);
userDao.delUserById(2);
}