事务管理
数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以确保非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS的事务管理子系统负责事务的处理。
ACID:
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
DEMO:事务
package com.sxt.test;
public class Test {
private String driverClassName = "com.mysql.jdbc.Driver";
private String password = "tang123";
private String userName = "root";
private String url = "jdbc:mysql://localhost:3306/login?characterEncoding=utf-8";
private Connection conn;
/**
* 事务:保证多个DML操作要么同时成功要么同时失败
*
* 不满足事务的情况
* @throws Exception
*/
@org.junit.Test
public void test() throws Exception {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, userName, password);
String sql1 = "insert into t_user(username,password)1values('a3','123')";
Statement s = conn.createStatement();
s.executeUpdate(sql1);
String sql2 = "insert into t_user(username,password)values('a4','1234')";
s.executeUpdate(sql2);
s.close();
conn.close();
}
/**
* 事务:保证多个DML操作要么同时成功要么同时失败
*
* 满足事务的情况
* @throws Exception
*/
public void test1() throws Exception {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, userName, password);
// 将自动提交关闭
conn.setAutoCommit(false);
Statement s = null;
try {
String sql1 = "insert into t_user(username,password)values('a4','123')";
s = conn.createStatement();
s.executeUpdate(sql1);
String sql2 = "insert into t_user(username,password)values('a5','1234')";
s.executeUpdate(sql2);
// 提交数据
conn.commit();
}catch(Exception e){
// 抛异常 回滚
conn.rollback();
} finally {
s.close();
conn.close();
}
}
}
实现:
AOP 代理模式:
Dao:
package com.sxt.dao;
import java.sql.Connection;
public interface IUserDao {
public void insert(Connection conn) throws Exception;
public void update(Connection conn)throws Exception;
public void delete(Connection conn)throws Exception;
}
DaoImpl:
package com.sxt.dao.impl;
import java.sql.Connection;
import java.sql.Statement;
import com.sxt.dao.IUserDao;
public class UserDaoImpl implements IUserDao {
private String sql;
@Override
public void insert(Connection conn) throws Exception {
sql="insert into t_user(username,password)values()";
Statement s=conn.createStatement();
s.executeUpdate(sql);
}
@Override
public void update(Connection conn) throws Exception{
sql = "update t_user set username='aaa',password='bbb' where id = 11 ";
Statement s = conn.createStatement();
s.executeUpdate(sql);
}
@Override
public void delete(Connection conn) throws Exception{
sql = "delete from t_user where id = 10";
Statement s = conn.createStatement();
s.executeUpdate(sql);
}
}
Service
package com.sxt.service;
public interface IUserService {
public void dml() throws Exception;
}
ServiceImpl
package com.sxt.service;
import java.sql.Connection;
import com.sxt.dao.IUserDao;
import com.sxt.dao.impl.UserDaoImpl;
public class UserServiceImpl implements IUserService {
private Connection conn;
public UserServiceImpl(Connection conn) {
super();
this.conn = conn;
}
private IUserDao dao = new UserDaoImpl();
public void dml() throws Exception{
dao.insert(conn);
dao.update(conn);
}
}
代理
package com.sxt.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import org.junit.Before;
import org.junit.Test;
import com.sxt.service.IUserService;
import com.sxt.service.UserServiceImpl;
public class test {
private String driverClassName = "com.mysql.jdbc.Driver";
private String password = "tang123";
private String userName = "root";
private String url = "jdbc:mysql://localhost:3306/login?characterEncoding=utf-8";
private Connection conn;
@Before
public void before() throws Exception {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, userName, password);
}
/**
* 事务:保证多个DML操作要么同时成功要么同时失败
*
* 不满足事务的情况
*
* @throws Exception
*/
@Test
public void test() throws Exception {
// 目标类
IUserService target = new UserServiceImpl(conn);
// 获取代理类
IUserService proxy = (IUserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
conn.setAutoCommit(false);
try {
object = method.invoke(target, args);
conn.commit();
} catch (Exception e) {
// TODO: handle exception
System.out.println("出现异常...回滚撤销");
conn.rollback();
} finally {
conn.close();
}
return object;
}
});
proxy.dml();
}
}
实现:
配置xml文件的方式:
pojo:
package com.sxt.pojo;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
}
Dao:
package com.sxt.dao;
public interface UserDao {
public void add();
public void update();
public void delete();
public void query1();
public void query2();
}
DaoImpl:
package com.sxt.dao.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.sxt.dao.UserDao;
import com.sxt.pojo.User;
@Repository
public class UserDaoImpl implements UserDao {
@Resource
private JdbcTemplate template;
@Override
public void add() {
String sql="insert into t_user(username,password)values(?,?)";
int i=template.update(sql,"tang","1234");
System.out.println("影响的行数"+i);
}
@Override
public void update() {
String sql="update t_user set username=?,password=? where id=?";
int i=template.update(sql,"蛇蛇蛇","abcd","2");
System.out.println("影响的行数"+i);
}
@Override
public void delete() {
String sql="delete from t_user where id=?";
int i=template.update(sql,3);
System.out.println("影响的行数"+i);
}
@Override
public void query1() {
String sql = "select * from t_user";
List<User> list = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
for (User user : list) {
System.out.println(user);
}
}
@Override
public void query2() {
String sql="";
int i=template.update(sql);
System.out.println("影响的行数"+i);
}
}
Service:
package com.sxt.service;
public interface IUserService {
public void fun1();
}
ServiceImpl:
package com.sxt.service;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.sxt.dao.UserDao;
@Service
public class UserServiceImpl implements IUserService {
@Resource
private UserDao dao;
@Override
public void fun1() {
dao.add();
dao.update();
}
}
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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.*"></context:component-scan>
<!-- 配置数据源 -->
<!-- 配置数据源 -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
<!-- 配置数据库的相关信息 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/login?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="tang123" />
</bean>
<!-- 配置JdbcTemplate -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="template">
<!-- 构造注入关联数据源 -->
<constructor-arg ref="dataSource"/>
</bean>
<!-- 配置事务 -->
<!-- 配置事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务的行为 -->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="fun*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- AOP配置 -->
<aop:config>
<aop:pointcut expression="execution(* com.sxt.service.impl.*.*(..))" id="myPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>
</beans>
测试:
基于注解的方式:
修改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-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 开启扫描 -->
<context:component-scan base-package="com.sxt.*"></context:component-scan>
<!-- 配置数据源 -->
<!-- 配置数据源 -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
<!-- 配置数据库的相关信息 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/login?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="tang123" />
</bean>
<!-- 配置JdbcTemplate -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="template">
<!-- 构造注入关联数据源 -->
<constructor-arg ref="dataSource"/>
</bean>
<!-- 配置事务 -->
<!-- 配置事务管理器 -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<!-- 关联数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注释事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
修改ServiceImpl:
实现:
总结:
好处:
简单、灵活。
坏处:
跟业务的耦合性加强了