Day21- JDBC事务(JDBC加强)
来源:互联网 发布:淘宝reebonz海外旗舰店 编辑:程序博客网 时间:2024/06/05 09:01
事物:
事物指的是逻辑上的一组操作,组成这组操作的各个单元要么全部成功,要么全部失败 简而言之,一组操作要么都成功,要么都失败; 一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败。
mysql的事务
1)默认的事务:
一条sql语句就是一个事务,默认就开启事务并提交事务
MySql默认自动提交,即执行一条sql语句就提交一次事务;
2)手动事务:
1)显示的开启一个事务:start transaction;
2)事务提交:commit代表从开启事务到事务提交 中间的所有的sql都认为有效 真正的更新数据库
3)事务的回滚:rollback 代表事务的回滚 从开启事务到事务回滚 中间的所有的 sql操作都认为无效数据库没有被更新
MySql中可以有两种方式对事务进行管理:
方式一:关闭mysql的自动事务
set autocommit = 0;
注意:这个设置只对当前的连接有效,新开的窗口,依然是自动事务
方式二:手动开启一个事务:
start transaction; -- 手动开启一个事务-- 假装做了一系列的sql操作commit; -- 提交事务 : 相当于把数据持久保存到数据据库中rollback; -- 回滚事务 : 相当于撤销刚才所有的操作
注意:
1)事务开启之后,最终必须提交或者回滚
2)oracle中的默认是手动事务,mysql默认是自动事务
3)控制事务的connnection必须是同一个
执行sql的connection与开启事务的connnection必须是同一个才能对事务进行控制
jdbc中的事务操作:
Connection接口的API(jdk api中搜connection )
- setAutoCommit(false) : 设置默认不自动提交事务
- commit(); 提交事务。将数据持久保存在数据库中
- rollback(); 事务的回滚。撤销事物中的操作
- rollback(savepoint sp); 事务的回滚至保存点
转账案例:
事务的控制需要在同一个connection之中,所以下述案例中统一传了一个对象connection,保证是在同一个连接之中。
package com.itheima.test;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;import org.junit.Test;import com.itheima.domain.Transfer;public class Test01 { @Test public void test01() throws Exception{ //注册驱动 Class.forName("com.mysql.jdbc.Driver"); //连接数据库 String url = "jdbc:mysql://localhost:3306/day21"; String user = "root"; String password = "123456"; Connection connection = DriverManager.getConnection(url, user, password); //关闭自动事务 connection.setAutoCommit(false); //封装一个javaBean Transfer transfer = new Transfer(); transfer.setFromUser("jack"); transfer.setToUser("rose"); transfer.setMoney(500); try { //转出钱 transferOut(connection, transfer); //如果发生意外 //int i = 5/0; //转入钱 transferIn(connection, transfer); //提交事务 connection.commit(); } catch (Exception e) { e.printStackTrace(); //事务回滚 connection.rollback(); System.out.println("转账失败"); }finally{ //关闭资源 connection.close(); } } /** * @Title: Test01.java * @Description: TODO(资金从转账人账户转出) * @param connection * @author jjizh * @date 2017年6月30日 下午8:33:00 * @version V1.0 * @throws SQLException */ public void transferOut(Connection connection,Transfer transfer) throws SQLException{ //获得转出金额和从谁转出 double money = transfer.getMoney(); String name = transfer.getFromUser(); //创建执行sql语句的对象 String sql = "update user set money = money - ? where name = ? "; PreparedStatement prepareStatement = connection.prepareStatement(sql); prepareStatement.setDouble(1, money); prepareStatement.setString(2, name); //执行sql语句 prepareStatement.executeUpdate(); //关闭资源 prepareStatement.close(); } /** * @Title: Test01.java * @Description: TODO(资金转入给收款人) * @param connection * @param transfer * @throws SQLException * @author jjizh * @date 2017年6月30日 下午8:48:22 * @version V1.0 */ public void transferIn(Connection connection,Transfer transfer) throws SQLException{ //获得转入金额和转入给谁 double money = transfer.getMoney(); String name = transfer.getToUser(); //创建执行sql语句的对象 String sql = "update user set money = money + ? where name = ? "; PreparedStatement prepareStatement = connection.prepareStatement(sql); prepareStatement.setDouble(1, money); prepareStatement.setString(2, name); //执行sql语句 prepareStatement.executeUpdate(); //关闭资源 prepareStatement.close(); }}
DBUtils事务操作
DBUtils中的api
* commitAndClose(Connection conn);* commitAndCloseQuietly(Connection conn); 不抛异常* rollbackAndClose(Connction conn);* rollbackAndCloseQuietly(Connection conn);不抛异常
queryRunner操作sql:
1)new QueryRunner(DataSource ds): 默认就是自动事务(这是我们之前一直用的方法)
query(String sql,ResultSetHandler rsh,Object…params);
update(String sql,Object…params);
使用上述方法的时候,我们没有调用过setAutoCommit(false),最后也没有提交过事务,也没有关闭资源
2)new QueryRunner() : 手动事务
query(Connection conn,String sql,ResultSetHandler rsh,Object…params);
update(Connection conn,String sql,Object…params);
使用query和update方法的时候,我们需要传入一个connection对象,最后需要手动提交或者回滚事务,还需要手动释放conn;
案例补充知识点:
1)主动抛异常
throw new Exception("")if(!flag2){ throw new RuntimeException("对不起,转入失败");}
2)获取异常中的抛出信息
Exception.getMessage();catch (Exception e) { e.printStackTrace(); request.setAttribute("msg", e.getMessage());}
代码:
Transferservlet:
public class TransferServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); try { //创建转账对象 Transfer t = new Transfer(); //接收参数 Map<String, String[]> map = request.getParameterMap(); //将参数传入到对象中去 BeanUtils.populate(t, map); //调用service完成转账 TransferService ts = new TransferService(); ts.transfer(t); request.setAttribute("msg", "转账成功"); request.setAttribute("transfer", t); request.getRequestDispatcher("/index.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); request.setAttribute("msg", e.getMessage()); request.getRequestDispatcher("/index.jsp").forward(request, response); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
TransferService:
public class TransferService { public void transfer(Transfer t) throws Exception { Connection connection = null; try { //获得连接 connection = JDBCUtils.getConnection(); //开启手动事务 connection.setAutoCommit(false); TransferDao td = new TransferDao(); //转出 boolean flag1 = td.moneyOut(connection,t); if(!flag1){ throw new RuntimeException("对不起,转出失败"); } //转入 boolean flag2 = td.moneyIn(connection,t); if(!flag2){ throw new RuntimeException("对不起,转入失败"); } //提交commit connection.commit(); } catch (Exception e) { JDBCUtils.rollbackQietly(connection); throw e; } finally{ //释放资源 JDBCUtils.closeQuietly(connection); } }}
转账案例终极版:
ThreadLocal
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
例如:
private static ThreadLocal tl = new ThreadLocal<T>();private static ThreadLocal tl = new ThreadLocal<Connection>();
threadLocal相当于一个map,以当前线程的id作为一个key,对应存入一个value
api中:
set(T value)直接传入一个value,设置一个值
get():不需要传入任何参数,key就是当前线程
remove():不需要传入任何一个参数, key就是当前线程
所以转账的优化流程可以优化成这个样子:
所以:
service层的代码优化成为:
TransferService_thrl:
public class TransferService_thrl { public void transfer(Transfer t) throws Exception { try { //开启手动事物 JDBCUtils.startTransaction(); TransferDao_thrl td = new TransferDao_thrl(); //转出 boolean flag1 = td.moneyOut(t); if(!flag1){ throw new RuntimeException("转出失败!!!!"); } //转入 boolean flag2 = td.moneyIn(t); if(!flag2){ throw new RuntimeException("转入失败!!!!"); } //提交事务 JDBCUtils.commitAndClose(); } catch (Exception e) { JDBCUtils.rollbackAndClose(); e.printStackTrace(); } }}
JDBCUtils中的函数优化为:
public class JDBCUtils { private static DataSource ds = new ComboPooledDataSource(); private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); public static DataSource getDataSource(){ return ds; } public static Connection getConnection() throws SQLException{ //从当前线程中去获取connection; Connection connection = tl.get(); if(connection == null){ connection = ds.getConnection(); tl.set(connection); } return connection; } //开启一个事务 public static void startTransaction() throws SQLException { Connection connection = getConnection(); connection.setAutoCommit(false); } //提交事务 public static void commitAndClose() { try { Connection connection = getConnection(); connection.commit(); connection.close(); } catch (SQLException e) { // quiet// e.printStackTrace(); } } public static void rollbackAndClose() { try { Connection connection = getConnection(); connection.rollback(); connection.close(); } catch (SQLException e) { // quiet// e.printStackTrace(); } }}
备注:servlet中的service每次是在一个新的线程中去执行的
事务特性ACID
1)原子性:Atomicity
- 事务不可切分,事务中可以包含多个操作,这些操作要么都成功,要么都失败
2)一致性:Consistency
- 事务执行前后的业务状态要和其它业务状态保持一致
3)隔离性:isolation
- 一个事务的执行,不受其它事务的影响
4)持久性:Durability
- 事务一旦提交,数据就持久保存到数据库中
并发访问问题:
如果不考虑隔离性,事务存在3种并发访问问题:
- 脏读: 一个事务读取到了另一个事务没有提交的数据
- 不可重复读:在一个事务中,两次查询的结果不一致(针对update)
- 虚读(幻读):在一个事务中,两次查询的结果不一致(针对insert)
设置隔离级别,避免问题产生
- read uncommitted 读未提交
- 上面所有的问题全都不可避免
- read committed 读取已提交
- 可以避免脏读的发生,不可重复读和幻读会发生
- repeatable read 可重复读
- 可以避免脏读和不可重复读的发生,不能避免幻读的发生
- serializable 串行化
- 可以避免所有的问题,一个事务在执行,另外一个事务等待
- 安全性: serializable > repeatable read > read committed > read uncommitted
效率刚好和上面的相反
mysql的默认隔离级别: repeatable read
oracle的默认隔离级别: read commttied – read only – serializable
java代码修改数据库的隔离级别(了解):
- conn.setTransactionIsolation(int level)
MySql中:
1)将数据库的隔离级别设为read uncommitted
set session transaction isolation level read uncommitted;
2)查看隔离级别
select @@tx_isolation;
3)将数据库的隔离级别设为read committed
set session transaction isolation level read committed;
不能避免不可重复读和幻读的发生
4)将数据库的隔离级别设为repeatable read
set session transaction isolation level repeatable read;
5)将数据库的隔离级别设为serializable
set session transaction isolation level serializable;
jsp中输出菱形
在控制台中输出:
@Test public void test01(){ int size = 10; for(int x=-size;x<=size;x++){ for(int y=-size;y<=size;y++){ if(y+x<=size && y+x>=-size && y-x<=size && y-x>=-size ){ System.out.print("*"); }else{ System.out.print(" "); } } System.out.println(); } }}
在jsp页面输出
因为jsp页面中使用jstl,循环是只能从0开始,不能够从负数开始的,所以需要手工调整坐标系位置
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>输出菱形</title></head><body> <c:forEach var="x" begin="0" end="5" step="1" varStatus="xStatus"> <c:forEach var="y" begin="0" end="5" step="1" varStatus="yStatus"> <c:choose> <c:when test="${x+y<=6 && x+y>=2 && x-y<=2 && x-y>=-2}"> * </c:when> <c:otherwise> </c:otherwise> </c:choose> </c:forEach> <br/> </c:forEach></body></html>
JSP三层架构
包名:
- Day21- JDBC事务(JDBC加强)
- JDBC 其二(加强)批处理,大数据处理,事务
- JDBC加强
- JDBC加强
- JDBC事务(转载)
- JDBC-事务
- jdbc事务
- JDBC事务
- JDBC事务
- JDBC事务
- jdbc事务
- jdbc事务
- jdbc事务
- JDBC事务
- JDBC事务
- jdbc 事务
- JDBC事务
- JDBC 事务
- 在线JS转C#代码
- Activity页面之间的数据传递
- 博客里《DSAA》相关文章的代码
- ArcPy批量掩膜裁剪栅格/图像
- SharedPreferences的简单用法
- Day21- JDBC事务(JDBC加强)
- 【GDOI2018模拟7.7】暴力大神hxx 树形dp
- javascript里的"类"
- OpenCV中CV_Assert函数和C++中assert()函数
- CSDN日报20170706——《屌丝程序员的逆袭之旅》
- 你是否真的了解编程语言?
- 类和对象基础
- MAKEINTRESOURCE的作用
- One-Hot encoder独热编码