博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
事务,连接池,异常的使用
阅读量:2395 次
发布时间:2019-05-10

本文共 9597 字,大约阅读时间需要 31 分钟。

事务的概念

  1)每种数据库都有事务的支持,但支持强度不同
  2)以MySQL为例,
    启动事务
    start transaction;
    提交事务
    commit;
    回滚事务
    rollback;
  3)在事务范围内回滚是允许的,但如果commit后再回滚,无效   
  4)其实每条SQL都有事务存在,只是显示还隐藏而言,默认都是隐藏事务
  5)事务的操作,必须争对同一个Connection。    
  6)事务的操作,可以设置一个回滚点,便于回滚到最近的回滚点处。

JDBC显示操作事务的API

package cn.itcast.web.jdbc.dao;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.Savepoint;import cn.itcast.web.jdbc.util.JdbcUtil;//JDBC显示操作事务的APIpublic class Demo2 {	public static void main(String[] args) {		Connection conn = null;		PreparedStatement pstmt = null;		ResultSet rs = null;		String sqlA = "update account set salary=salary-1000 where name='aaa'";		String sqlB = "update account set salary=salary+1000 where name='bbb'";		String sqlC = "insert into account(name,salary) values('ccc',3000)";		Savepoint sp = null;				try {						conn = JdbcUtil.getMySqlConnection();						//设置事务显示手工提交			conn.setAutoCommit(false);						//NO1			pstmt = conn.prepareStatement(sqlA);			pstmt.executeUpdate();						//NO2			pstmt = conn.prepareStatement(sqlB);			pstmt.executeUpdate();			//设置一个回滚点        回滚点一定要再出错的点前面			sp = conn.setSavepoint();						Integer.parseInt("abc");   //这个是故意设置的出错点			//NO3			pstmt = conn.prepareStatement(sqlC);			pstmt.executeUpdate();						//事务手工提交    提交后就无法回滚了			conn.commit();					} catch (Exception e) {			e.printStackTrace();			try {				//事务回滚,默认情况下,回滚到事务开始之前的状态     也可以设置一个回滚点,然后就回滚到最近的回滚点				conn.rollback(sp);   //使用默认回到开始情况的就不用带有参数				conn.commit();			} catch (Exception e1) {							}		}finally{			JdbcUtil.close(rs);			JdbcUtil.close(pstmt);			JdbcUtil.close(conn);		}	}}
JDBCUtils争对同一个Connection的处理方案
package cn.itcast.web.jdbc.util;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.Properties;//JDBC工具类:关闭流和取得连接public final class JdbcUtil {		private static String driver;	private static String url;	private static String user;	private static String password;	private static ThreadLocal
tl = new ThreadLocal
(); //局部线程变量 //静态块:加载文件 static{ Properties props = new Properties(); InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("cn/itcast/web/jdbc/config/db.properties"); try { props.load(is); } catch (Exception e) { e.printStackTrace(); } driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); password = props.getProperty("password"); } //静态块:注册驱动 static{ try { Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } //取得连接,每个线程都取得唯一的一个Connection对象 public static Connection getMySqlConnection() { /*Connection conn = null; try { conn = DriverManager.getConnection(url,user,password); } catch (SQLException e) { e.printStackTrace(); } */ Connection conn = tl.get(); if(conn==null){ try { //第一次该线程没有绑定connection对象 conn = DriverManager.getConnection(url,user,password); //绑定到该线程中 tl.set(conn); } catch (Exception e) { e.printStackTrace(); } } return conn; } //关闭连接 public static void close(ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void close(Statement stmt){ if(stmt!=null){ try { stmt.close(); } catch (Exception e) { e.printStackTrace(); } } } public static void close(Connection conn){ if(conn!=null){ try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } //事务开始 public static void begin(){ Connection conn = getMySqlConnection(); try { conn.setAutoCommit(false); } catch (Exception e) { e.printStackTrace(); } } //事务提交 public static void commit()throws SQLException{ Connection conn = getMySqlConnection(); try { conn.commit(); } catch (SQLException e) { e.printStackTrace(); throw e; } } //事务回滚 public static void rollback() throws SQLException{ Connection conn = getMySqlConnection(); try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); throw e; } } public static void closeConnection()throws SQLException { Connection conn = getMySqlConnection(); JdbcUtil.close(conn); //将该线程与所绑定的Connection对象分离 tl.remove(); } }

事务的特性

    1)原子性(A)事务是的各个操作是一个不可分割的子操作。必须将其看成一个整体,即原子操作
    2)一致性(C)事务前后,由一个一致状态转移到另一个一致状态
   *3)隔离性(I)事务中,每个线程操作同张表同记录时,相互分割
    4)持久性(D)事务一旦生效,在没有操作该记录时情况下,永远保持不变
三个缺点(违背隔离性)
   1)脏读:一个线程看到了另一个线程未提交的数据,叫脏读
   2)不可重复读:一个线程多次做查询操作,多次结果都不一致,叫不可重复读
    上述二项,强调的是查询,内容变,但数量不变
    
   3)幻读/虚读:
    上述一项,强调的是插入,数量变

事务的隔离级别(解药)       都是针对事务来说的

   *static int TRANSACTION_READ_COMMITTED
          指示不可以发生脏读的常量;不可重复读和虚读可以发生。
   *static int TRANSACTION_REPEATABLE_READ
          指示不可以发生脏读和不可重复读的常量;虚读可以发生。
    static int TRANSACTION_SERIALIZABLE
          指示不可以发生脏读、不可重复读和虚读的常量。     效率是最低的
      一般来说优先考虑前面两种,这种不怎么使用,因为这种在虚读的时候是在insert那个页面没有commit之前把所有数据的锁死的,
               这样子在web开发上是很不科学的,如果访问人数比较多,而进行操作的人操作又比较久,那么后面的人到底要到什么时候才能操作自己想要的数据啊
   总结:
    项目中,对于select操作不需要事务,对于其它操作(update/delete/insert)操作需要事务。

JDBC设置事务的隔离级别

package cn.itcast.web.jdbc.dao;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import cn.itcast.web.jdbc.util.JdbcUtil;//JDBC设置事务的隔离级别public class Demo3 {	//我(serializable)先执行	public static void main(String[] args) {		Connection conn = null;		PreparedStatement pstmt = null;		ResultSet rs = null;		String sql = "select * from account";		try {			conn = JdbcUtil.getMySqlConnection();			//设置事务的隔离级别			conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);			conn.setAutoCommit(false);			pstmt = conn.prepareStatement(sql);			rs = pstmt.executeQuery();			//休息			Thread.sleep(20*1000);			conn.commit();		} catch (Exception e) {			e.printStackTrace();			try {				conn.rollback();				conn.commit();			} catch (Exception e1) {			}		}finally{			JdbcUtil.close(rs);			JdbcUtil.close(pstmt);			JdbcUtil.close(conn);		}	}}

关于异常的处理(具体参照转账案例)

   1)关于分层结构中,处理异常的规则,参见<<关于异常的处理规则.JPG>>
   2)异常在项目中,往往替代boolean值,作为成功与否的标志
   除了service抛出自己包装的异常,其他都是抛出最原始的异常,而servlet则是用try语句捕获异常

连接池

   1)传统方式找DriverManager要连接,数目是有限的。   也就是允许在线的人数是有很大的限制的,通过连接池可以连接无数个,而且速度还快
   2)传统方式的close(),并没有将Connection重用,只是切断应用程序和数据库的桥梁,即无发送到SQL命令到数据库端执行
   3)项目中,对于Connection不说,不会直接使用DriverManager取得,而使用连接池方式。
   4)DBCP和C3P0,都是Java开源的,都必须直接或间接实现javax.sql.DataSource接口
   5)DBCP连接池需要dbcp.properties文件,同时需加入3个对应的jar包
  *6)C3P0连接池需要在/WEB-INF/classes/目录下存放c3p0-config.xml文件,该类ComboPooledDataSource在创建时会自动在指定的目录下找xml文件,并加载默认设置,也可以不要配置文件,在代码中通过一系列的set语句来添加进去.
     hibernate默认使用的就是这个连接池
 

dbcp连接池:

package cn.itcast.web.jdbc.datasource;import java.io.InputStream;import java.sql.Connection;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSourceFactory;//测试连接池DBCP的用法public class Demo2 {	public static void main(String[] args) throws Exception {		long begin = System.currentTimeMillis();		//加载属性文件		InputStream is = Demo2.class.getClassLoader().getResourceAsStream("cn/itcast/web/jdbc/config/dbcp.properties");		Properties props = new Properties();		props.load(is);		//创建DBCP连接池工厂		BasicDataSourceFactory factory = new BasicDataSourceFactory(); 		//创建数据源,即连接池		DataSource ds = factory.createDataSource(props);		for(int i=1;i<=50000;i++){			//从连接池中取得一个空闲的连接对象			Connection conn = ds.getConnection();			if(conn!=null){				System.out.println(i+":取得连接");			}			//将连接对象还回给连接池			conn.close();		}		long end = System.currentTimeMillis();		System.out.println("共用" + (end-begin)/1000+"秒");	}}
c3p0连接池:
package cn.itcast.web.jdbc.datasource;import java.sql.Connection;import com.mchange.v2.c3p0.ComboPooledDataSource;//测试连接池C3P0的用法public class Demo3 {	public static void main(String[] args) throws Exception {		long begin = System.currentTimeMillis();		//创建C3P0连接池		ComboPooledDataSource dataSource = new ComboPooledDataSource();		for(int i=1;i<=100000;i++){			Connection conn = dataSource.getConnection();			if(conn!=null){				System.out.println(i+":取得连接");				conn.close();			}		}		long end = System.currentTimeMillis();		System.out.println("共用" + (end-begin)/1000+"秒");	}}

JNDI和在tomcat中配置DBCP连接池

  a)JNDI是Java命名和目录接口,不同的Web服务器有着不同的实现
  b)不同进程之间,可不同机器之间访问,叫远程访问     这种情况就使用JNDI,可以实现跨线程访问两一个线程的数据。。例如我开的这个线程肯定和tomcat连接池不是同一个线程,这样子就需要通过这个技术来连接到tomcat的线程从而获取connection
  c)JNDI和JDBC一样,都属于JavaEE规则之一
  d)基于tomcat如何配置DBCP连接池    /day15/src/cn/itcast/web/jdbc/web/JndiServlet.java
    >>修改tomcat/conf/context.xml文件    C:\Program Files\Apache Software Foundation\Tomcat 6.0\conf
        >>加入DB相关的jar包到tomcat/lib目录下     其实就是mysql的jar包
    >>重新启动tomcat服务器
  e)访问tomcat服务器的JNDI代码如下,是固定的:
    Context tomcatContext = (Context) context.lookup("java:comp/env");

package cn.itcast.web.jdbc.web;import java.io.IOException;import java.sql.Connection;import javax.naming.Context;import javax.naming.InitialContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.sql.DataSource;//通过JNDI远程访问Tomcat服务器中的DBCP连接池public class JndiServlet extends HttpServlet {	public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {		try {			//创建具体Web服务器的JNDI对象			Context context = new InitialContext();			//远程查找Web服务器			Context tomcatContext = (Context) context.lookup("java:comp/env");			//在Web服务器内远程查找DBCP连接池服务			//tomcatDS是在tomcat/conf/context.xml文件中配置的名字			DataSource ds = (DataSource) tomcatContext.lookup("tomcatDS"); 			//从DBCP连接池中取得一个空闲的连接			Connection conn = ds.getConnection();			//显示结果			if(conn!=null){				response.setContentType("text/html;charset=UTF-8");				response.getWriter().write("取得连接");				conn.close();			}		} catch (Exception e) {			e.printStackTrace();		} 	}}

转载于:https://my.oschina.net/u/2356176/blog/467371

你可能感兴趣的文章
Ubuntu 12.04安装Jetty
查看>>
Ubuntu 12.04安装OpenCV
查看>>
K-D树 C++实现
查看>>
搜索引擎的预料库 —— 万恶的爬虫
查看>>
网易工程师 Ruheng 一文教你轻松学会 Git
查看>>
文字与编码的奥秘(下)
查看>>
阿里分布式事务框架 GTS 全解析
查看>>
轻量级 Web 框架 Gin 结构分析
查看>>
一个字节的网络漫游故事独白
查看>>
RabbitMQ 消息可靠性、延时队列以及高可用集群
查看>>
分布式系统的可靠性指的是什么 —— 你可能从来就没有认真思考过
查看>>
布隆过滤器过时了,未来属于布谷鸟过滤器?
查看>>
面试题 —— 数字幻方
查看>>
5折抢购最后一天 | 戴尔顶级配置电脑,限时秒!
查看>>
SpringBoot 究竟是如何跑起来的?
查看>>
阿里开源限流组件 Sentinel 集群流控全解析
查看>>
深度解密HTTP通信细节
查看>>
日活亿级用户的服务器架构要怎么搭?
查看>>
MySQL 是怎样运行的:从根儿上理解 MySQL
查看>>
开源搜索技术的核心引擎 —— Lucene
查看>>