世界万事无简单一说, 每一个事情基本上由多个小的事情来完成。有的事情会存在若小的事情不能同时完成的状况就取消全部的小的事情,直至都完成达到预期的效果才算完成!这样就用到了事务操做。在全部的sql语句完成以前,若发生异常,则让事务回滚到开始事务的时候,让事务结束;而且让已执行的sql语句做废。可是链接数据库的connnection与开启事务的connnection必须是一个。这样原来在dao层开启链接必须到service层一块儿执行了开启事务和处理异常。html
1、事务:java
一件事情有n个组成单元 :要不这n个组成单元同时成功, 要不n个单元就同时失败。就是将n个组成单元放到一个事务中!mysql
2、mysql事务:sql
一、默认的事务:一条sql语句就是一个事务 。默认就开启事务并提交事务!数据库
二、手动事务:并发
①、显示的开启一个事务:start transaction spa
此时进行的全部的修改都是内存里修改的,具体数据库内到底修改了没仍是由是否提交来决定的!!!code
当提交了就表明数据库修改了,当回滚了则没有修改!htm
可是须要注意的是不管数据库到底修改了没有,只要执行了SQL语句,在库内的表里的自增的序号会自动被占用了!!!对象
②、事务提交:commit
表明从开启事务到事务提交中间的全部的sql都认为有效! 真正的更新数据库!
③、事务的回滚:rollback
表明事务的回滚--在其以前的全部的操做都做废了!回滚到start的地方,同时当前的事务结束了!
3、JDBC事务操做:
一、默认是自动事务:
执行sql语句:executeUpdate() ---- 每执行一次executeUpdate方法 表明 事务自动提交(默认)<执行一句sql就是一个提交>
二、经过jdbc的API手动事务:
①、开启事务:conn.setAutoCommit(false);
此为设置为自动提交(改成false,也就是改成手动提交)
②、提交事务:conn.commit();
③、回滚事务:conn.rollback(); (都被封装成了方法)
注意:
控制事务的connnection必须是同一个!
执行sql的connection与开启事务的connnection必须是同一个才能对事务进行控制!!!
public static void main(String[] args) { Connection conn = JDBCUtils.getConn(); Statement sta = null; // Statement sta = conn.createStatement(); String sql = "insert into account(aname,money) values('wangwu',1000)"; // 手动开启事务 try { sta = conn.createStatement(); conn.setAutoCommit(false); sta.executeUpdate(sql); } catch (SQLException e) { // 回滚 try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } finally {//注意好位置 try { // 提交事务 conn.commit(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }
注:
Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。
4、DBUtils事务操做:
QueryRunner:
一、有参构造:QueryRunner runner = new QueryRunner(DataSource dataSource);
有参构造将数据源(链接池)做为参数传入QueryRunner,QueryRunner会从连 接池中得到一个数据库链接资源操做数据库,因此直接使用无Connection参数 的update方法便可操做数据库
二、无参构造:QueryRunner runner = new QueryRunner();-
无参的构造没有将数据源(链接池)做为参数传入QueryRunner,那么咱们在使 用QueryRunner对象操做数据库时要使用有Connection参数的方法
<由于有参没法保证connection的惟一性,因此须要无参构造---而且须要手动获取链接>
public class MyDBUtils { public static final String DRIVER = "com.mysql.jdbc.Driver"; public static final String URL = "jdbc:mysql://localhost:3306/java0603?useUnicode=true&characterEncoding=UTF-8"; public static final String USERNAME = "root"; public static final String PASSWORD = "123456"; /* * 建立链接池BasicDataSource */ public static BasicDataSource dataSource = new BasicDataSource(); //静态代码块(优先只执行一次) static { //对链接池对象 进行基本的配置 dataSource.setDriverClassName(DRIVER); // 这是要链接的数据库的驱动 dataSource.setUrl(URL); //指定要链接的数据库地址 dataSource.setUsername(USERNAME); //指定要链接数据的用户名 dataSource.setPassword(PASSWORD); //指定要链接数据的密码 } /* * 返回链接池对象 */ public static DataSource getDataSource(){ return dataSource; } //返回一个链接对象 public static Connection getConn(){ Connection conn=null; try { conn= dataSource.getConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } }
public static void main(String[] args) { //获取链接池对象 QueryRunner qr = new QueryRunner(); //获取链接对象 Connection conn = MyDBUtils.getConn(); String sql = "update account set money = money - ? where aname = ?"; try { //开启事务 conn.setAutoCommit(false); qr.update(conn,sql,100,"zhangsan"); } catch (SQLException e) { //回滚事务 try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // TODO Auto-generated catch block e.printStackTrace(); }finally{ //提交事务 try { conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
5、转帐实例:
public class AccountDao { // 转出--须要和service层同一个conn 因此须要传值 public void Moneyout( Connection conn,String out, double money) throws SQLException { QueryRunner qr = new QueryRunner(); String sql = "update account set money = money - ? where aname = ?"; qr.update(conn, sql, money, out); } // 转入 public void Moneyin(Connection conn,String in, double money) throws SQLException { QueryRunner qr = new QueryRunner(); String sql = "update account set money = money + ? where aname = ?"; qr.update(conn, sql, money, in); } }
public class AccountService { private AccountDao accountDao = new AccountDao(); // 转帐--须要在此层处理事务,因此在此层得到CONN,再把这个参数传给dao层 public boolean transfer(String out, String in, double money) { // 定义变量 boolean flag = true; Connection conn = MyDBUtils.getConn(); try { // 开启事务 conn.setAutoCommit(false); accountDao.Moneyout(conn, out, money); accountDao.Moneyin(conn, in, money); } catch (SQLException e) { flag = false; // 若是出现异常,则flag返回一个false // 回滚(当try出现异常后会执行catch,而后回滚到开启事务以前) try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } e.printStackTrace(); } finally { // 提交 try { conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return flag; } }
public class TransferServlet extends HttpServlet { private AccountService accountService =new AccountService(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取页面上值 //解决中文乱码 request.setCharacterEncoding("UTF-8"); //获取转出帐户 String out = request.getParameter("out"); //获取转入帐户 String in = request.getParameter("in"); //获取转帐金额(从前台获取的都是String类型) String moneyStr = request.getParameter("money"); //将字符串金额转成double double money = Double.parseDouble(moneyStr); //调用 Service层的转帐方法 boolean flag = accountService.transfer(out, in, money); //解决response乱码 response.setContentType("text/html;charset=utf-8"); if(flag){ response.getWriter().write("转帐成功"); }else{ response.getWriter().write("转帐失败"); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
5、事务的特性和隔离级别:事务的特性ACID
一、原子性(Atomicity)
原子性是指事务是一个不可分割的工做单位,事务中的操做 要么都发生,要么都不发生。
二、一致性(Consistency)
一个事务中,事务先后数据的完整性必须保持一致。
三、隔离性(Isolation)
多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
四、持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变 就是永久性的,接下来即便数据库发生故障也不该该对其有任何影响。