java数据库事务管理原理
1、事务概念:
是对数据库的一组操作中,要不全部成功,要不全部失败。(比如说转账的功能,转账是执行一组SQL语句从A账户中减,从B账户中加);
2、事务运用的SQL语句:
一组的SQL语句只是包括 insert(插入),update(更新),delete(删除),不包括select(查询)语句,查询语句不修改数据。
3、事务本质:
事务的本质就是数据的落地,就是对数据库操作持久化操作,数据库操作系统实际上就是把从程序中获取的数据库语句进行执行,然后再把数据读写到指定文件中,
在数据库安装目录中的data目录中是保存用户所建的数据库的文件其中最主要的就是.frm和.ibd文件数据库把用户输的指令进行执行然后再操作这两个后缀名的文件进行数据的
持久化操作。
4、SQL语句的执行机制: 1.获取程序返回的SQL语句。
2.把语句置换到内存中,在内存中进行操作,如果是select语句:假设有两个用户都对同一张表进行相同的查询操作,则用户一执行操作语句的数据会在内存中暂时保存
到缓存区,第二个用户再次执行语句时直接在缓存区获取数据,以便提高查询效率。
3.对硬盘上储存的数据文件进行持久化操作。
4.返回执行结果。
所以,事务的原理就是在执行读写数据时,先让其在内存中进行读写,其没有真正的去持久化数据修改硬盘中的文件,如果一组数据执行完后没有出现异常,再提交事务,进而修改数据库文件,
达到数据持久化。一般的SQL默认是自动提交事务,从一条非select语句开始进行事务处理,执行完语句后自动提交事务。
5、事务的方式:
就有两种方式: 1、自动提交(执行一句非select的SQL语句开始,执行结束后自动提交事务。)
2、手动提交(在数据库中:用srart transaction手动开启事务;
在java程序中,用connection.setAutoCommit(false)来开启事务)
数据库中使用 1、commit; 提交事务
2、rollback;回滚事务
java程序中使用 1、conn.commit;提交事务
2、conn.rollback;回滚事务
6、事务实际案例:此案例异常没有写
------------------------------------------------------------------------------------------
JDBCUtlis:
public class JDBCUtlis{
private static String url="jdbc:mysql://localhost:3306/test";
private static String username="root";
private static String password="root";
static{
//获取数据库驱动
Class.forname("com.mysql.jdbc.Driver");
}
//获取数据库连接
public static Connection getConnection(){
Connection conn=DriverManager.getConnection(url,username,password);
return conn;
}
}
------------------------------------------------------------------------------------------
dao层:
public class AffairDao{
//获取connection数据连接
private Connection connection=JDBCUtlis.getConnection();
//操作数据库减钱方法
public void Outmoney(int money,String name){
PreparedStatement preparedstatement=connection.prepareStatement("update t_money set money=money-? where name=?");
preparedstatement.setInt(1,money);
preparedstatement.setString(2,name);
preparedstatement.executeUpdate();
}
//操作数据库加钱方法
public void Inmoney(int money,String name){
PreparedStatement preparedstatement=connection.prepareStatement("update t_money set money=money+? where name=?");
preparedstatement.setInt(1,money);
preparedstatement.setString(2,name);
preparedstatement.executeUpdate();
}
}
------------------------------------------------------------------------------------------
service层:
public class AffairService{
private AffairDao affairdao=new AffairDao();
public static void main(String[] args){
Connection.setAutoCommit(false);----------------------> 在这手动开启事务是错误的,dao层的减钱和加钱的方法和此connection不属于同一个connection
affairdao.Outmoney(10,"aaa");
//int a=10/0;
affairdao.Inmoney(10,"bbb");
}
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
注意:以上方法 事务开启是失败的,因为所有的Connection数据连接不是同一个数据库连接,是不在一个线程中的,所以我们要换一种方式去执行代码。
方法一:可以修改dao层的减钱和价签方法:传递同一个Connection连接。
------------------------------------------------------------------------------------------
修改dao层:
public class AffairDao{
//获取connection数据连接
//private Connection connection=JDBCUtlis.getConnection();---------------------------------->connection连接不在此处获取
//操作数据库减钱方法
public void Outmoney(Connection connection,int money,String name){ -------------------------------------------------------->传入Connection
PreparedStatement preparedstatement=connection.prepareStatement("update t_money set money=money-? where name=?");
preparedstatement.setInt(1,money);
preparedstatement.setString(2,name);
preparedstatement.executeUpdate();
}
//操作数据库加钱方法
public void Inmoney(Connection connection,int money,String name){
PreparedStatement preparedstatement=connection.prepareStatement("update t_money set money=money+? where name=?");
preparedstatement.setInt(1,money);
preparedstatement.setString(2,name);
preparedstatement.executeUpdate();
}
}
------------------------------------------------------------------------------------------
修改service层:
public class AffairService{
private AffairDao affairdao=new AffairDao();
public static void main(String[] args){
Connection connection= JDBCUtlis.getConnection();----------------------------->使用同一个Connection
try{
Connection.setAutoCommit(false);
affairdao.Outmoney(Connection,10,"aaa");
//int a=10/0;
affairdao.Inmoney(connection,10,"bbb");
connection.commit;---------------------->提交事务;
}catch{
connection.rollback;-------------------->回滚事务;
}
}
}
------------------------------------------------------------------------------------------
方法二:修改JDBCUtlis 使用数据库连接池方式 使用ThreadLocal(使用这个类可以共享同一个数据,此类实质是map和线程的一种结合,用map中的k标识,v是线程)
------------------------------------------------------------------------------------------
修改JDBCUtlis:
public class JDBCUtlis{
private static ComboPooledDataSource dataSource=new CombopooledDataSource();
private static ThreadLocal<Connection> local=new ThreadLocal<Connection>();
//获取数据库连接
public static Connection getConnection(){
//从当前线程中获取connection
Connection conn=local.get();
//判断connection是否存在,不存在就创建。
if(conn==null){
//创建Connection
conn=dataSource.getConnection();
//添加到ThreadLocal中
local.set(conn);
}
return conn;
}
}
------------------------------------------------------------------------------------------
这样的话,必须使用c3p0数据连接池,从数据连接池中获取线程连接,只需修改JDBCUtlis即可,把获取的Connection给ThreadLocal就行了,
dao层和service层正常通过JDBCUtlis正常获取connection就可以了,不用向dao层传递connection参数了(推荐使用此方法)