当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > 数据库事务,Connection连接池

数据库事务,Connection连接池 时间:2018-09-27      来源:未知

一 事务

   (一)事务的概念

          事务指逻辑上一组操作,组成这组操作的各个单元,要么全部成功要么全部不成功。  例如:A--B转账,对应如下的两条sql语句:

Update account set money = money-100 where name=’a’;;

Update account set money = money+100 where name=’b’;

数据库默认事务时自动提交的,也就是发一条sql它就执行一条,如果想多条sql放在一个事务中,需要如下。

   (二)数据库开启事务cmd命令

开启事务:start transaction

回退事务:rollback

提交事务:commit

   (三)JDBC开启事务

         1.一般事务

         conn.setAutoCommit(false);//设置事务不自动提交。

Conn.commit();//后提价事务。

Conn.rollback();//出现异常,回退事务。 

Connection conn=null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            conn = DriverManager.getConnection
            ("jdbc:mysql://localhost:3306/mytransation", "root", "root");
            //设置不自动提交
            conn.setAutoCommit(false);
            ps = conn.prepareStatement("update account set money=money-100 where name=?");
            ps.setString(1, "a");
            ps.executeUpdate();
            //假设异常点
            /*int a=1/0;*/
            ps = conn.prepareStatement("update account set money=money+100 where name=?");
            ps.setString(1,"b");
            ps.executeUpdate();
            //在这里提交事务
            conn.commit();
        }
        catch(Exception e){
            try {
//出现异常回退事务
if(conn!=null)
                conn.rollback();
                System.out.println("出现异常,事务回退");
            } catch (SQLExcep tion e2) {
                e2.printStackTrace();
            }
            System.out.println(e.getMessage());
            if(conn!=null){
                try {
                    conn.close();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                    System.out.println("Connection异常");
                } finally{
                    conn = null;
                }

        2.设置回滚点提交事务,提交异常之前的sql语句 
Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Savepoint sp = null;
       
        try{
            conn = DriverManager.getConnection("jdbc:mysql:///mytransation", "root", "root");
           
            conn.setAutoCommit(false);//设置不自动提交事务
            ps = conn.prepareStatement("update account set money=money-100 where name=?");
            ps.setString(1, "a");
            ps.executeUpdate();
           
            ps = conn.prepareStatement("update account set money=money+100 where name=?");
            ps.setString(1,"b");
            ps.executeUpdate();
           
            sp = conn.setSavepoint();//设置回滚点
           
            ps = conn.prepareStatement("update account set money=money-100 where name=?");
            ps.setString(1, "a");
            ps.executeUpdate();
           
            int a=1/0;//模拟异常
           
            ps = conn.prepareStatement("update account set money=money+100 where name=?");
            ps.setString(1,"b");
            ps.executeUpdate();
           
            conn.commit();//提交事务
        } catch(Exception e){
            try{
                if(conn !=null){//如果sp为空,则在回滚点之前回退模式
                    if(sp == null){
                        conn.rollback();
                    }else{//否则回滚到回滚点的数据,让其提交。
                        conn.rollback(sp);
                        conn.commit();
                    }
                }
            }
            catch(SQLException e1){
            }
        }

3.事务的四大特性(ACID,很重要)。

          原子性(Atomicity):事务时一个不可分割的工作单位,事务中操作 要么发生,要么都不发生。

          一致性(Consistency):事务前后数据的完整性必须保持一致。

          隔离性(Isolation):多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰。多个并发事务之间数据要相互隔离。

          持久性(Durability):事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

          如果不考虑事务的隔离性,会出现的问题:

          (1)脏读:一个事务读取到另外一个事务的未提交的数据

          (2)不可重复读: 同一个数据(表)不可重复读(针对于update更新数据)

          (3)虚读:同一数据(表)不可重复读(强调insert)

          (4)丢失更新:

         4.隔离级别以及隔离级别的设置

            (1)隔离级别

                 Read uncommitted   数据库不能防止脏读、不可重复读、虚读。

                 Read committed(oracle)  能防止脏读,不能防止不可重复读、虚读。

                 Repeatable read(mysql) 能防止脏读、不可重复读,不能防止虚读。

                 Serializable  单线程数据库,能防止所有的问题。

                 性能分析:read uncommitted>read committed>repeatable read>serializable

                 安全性能:serializable>repeatable read>read committed>read uncommitted

            (2)隔离级别的设置

                 1)cmd操作命令

                    Select  @@tx_isolation 查询当前事务隔离级别

                    Set session transaction isolation level 设置的事务级别

                 2)JDBC中操作隔离级别

 

Connection的实例方法setTransactionIsolation(int level)

可以的值是(Connection的静态常量):

 TRANSACTION_READ_UNCOMMITTED(不能防止脏读,不可重复读,虚读)

 TRANSACTION_READ_COMMITTED(能防止脏读,不能解决不可重复读,虚读)

 TRANSACTION_REPEATABLE_READ(能防止脏读,不可重复读,不能解决虚读)

 TRANSACTION_SERIALIZABLE(可以解决所有的问题)

        

          5.脏读、不可重复读、虚读案例演示。

    

            (1)脏读演示

                 一开始状态:A账户:1000元

                             B 账户:1000元

                 A账户向B账户汇款的情况:

                    start  transaction

                    Update account set money=money-100 where name=’A’;

                    Update account set money=money+100 where name=’B’;

                    -------此时B查询,设置隔离级别为read uncommitted

                         Select  * from account where name=’B’;结果为1100;

                    Rollback;回滚事务,数据并未修改成功。

(2)不可重复读的

 a 账户三个月工资为:1000,1000,1000

   Start transaction

Select sum(money) from account where name=’a’;

-------此时 财务b,将第一个月工资修改为1300,并且立即提交,即

Update account set money=1300 where name=’a’;

Select sum(money)/count(*)  from account where name=’a’;提交,发现报表不对。

(3)虚读

         6.转账小案例,事务控制。

                1)逻辑层(service)调用dao层的两个方法(一个转入方法,一个转出方法),解决其中一个方法失败,因此需要事务控制,事务回滚等。

                2)用到事务,那么就应该使用同一个Connection对象,因此,在service层的方法里,应该共用一个Connection对象。

                3)mysql,修改不存在的用户名的账户值返回值代表影响的行数,由此可以找到解决该方法的问题。

               4)学会使用同一个ThreadLocal,可以理解为同一个线程的容器,只要在同一个线程,就可以取出来使用。示例:

Private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

Public static Connection getCon(){

   If(tl.get==null){

   Tl.set(DriverManager.getConnection(“”,””,””));

 }

Return tl.get();

}

    7.数据丢失更新。

1)数据丢失更新示意图(多个事务对同一行数据进行操作,后提交的事务将覆盖先提交的事务)。

 

        

   2)数据更新丢失的解决方法

         (1)悲观锁(假设丢失更新一定发生,利用数据库内部锁机制)

              共享锁:

              Select  * from account lock in share mode(读锁,共享锁)

                       排它锁(被锁中的可以是一行数据,也可以是整张表):

  Select * from account for update(写锁,排它锁)。

Update 默认是排他锁。

(2)乐观锁(加锁丢失更新不会发生,采用程序中添加版本字段解决丢失更新问题)

     create table product (

  id int,

  name varchar(20),

  updatetime timestamp

);

 

insert into product values(1,'冰箱',null);

update product set name='洗衣机' where id = 1;

 

解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,与修改时版本字段不一致,说明别人进行修改过数据 (重改)

8.数据库连接池

   (1)定义:创建一个容器,这个容器来装多个Connection对象,在使用该对象的时候,从容器获取一个Connection对象,使用完毕后,再把这个Connection对象重新装到容器当中。那么这个容器就叫做数据库连接池。

(2)自定义连接池

     步骤:1.创建一个MyDataSource类,在这个类中创建一个LinkedList<Connection>

2.在构造器方法中初始化List集合,并向其中装入5个Connection对象

3.创建一个public Connection getConnection();从List集合中获取一个连接对象返回

4.创建一个Public void readdd(Connetion)这个方法是将使用完成后的Connection对象重新装到List集合中。

   代码问题:

                        1.连接池的创建时有标准的,在javax.sql包下定义一个接口DataSource,我们必须实现

  2.改变Connection的close()方法,不是销毁它,而是将它重新装入到连接池中。解决这      

                          个问题本质就是将Connection的close()行为改变。

  三种方式改变方法行为:继承;装饰者模式;动态代理   

               (3)使用开源连接池

   1.dbcp(DataBase Connection Pool)

Dbcp是Apache下的一个开源连接池,两个关键.jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)

1)手动配置

BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("com.mysql.jdbc.Driver");
        bds.setUrl("jdbc:mysql:///mytransation");
        bds.setUsername("root");
        bds.setPassword("root");
        Connection con = bds.getConnection();
        ResultSet rs = con.prepareStatement("select * from account").executeQuery();
        while(rs.next()){
            System.out.println("账户:"+rs.getString("name")+",余额:"+rs.getDouble("money"));
        }
        JDBCUtils.closeRS(rs);
        con.close();//不是关闭连接,而是放回连接池

  2)自动配置

  Properties p = new Properties();

        p.load(new FileInputStream("D:\\Myeclipse java程序\\AccountTransaction\\src\\dbcp.properties"));
/*      p.setProperty("url", "jdbc:mysql:///mytransation");
        p.setProperty("username", "root");
        p.setProperty("password", "root");
        p.setProperty("driverClassName", "com.mysql.jdbc.Driver");*/
        DataSource ds = BasicDataSourceFactory.createDataSource(p);
        Connection con = ds.getConnection();
        ResultSet rs = con.prepareStatement("select * from account").executeQuery();
        while(rs.next()){
            System.out.println("账户:"+rs.getString("name")+",余额:"+rs.getDouble("money"));
        }
        JDBCUtils.closeRS(rs);
        con.close();//不是关闭连接,而是放回连接池

2.c3p0 (重点,是一个开源数JDBC连接池,实现数据源和JNDI绑定,支持JDBC3和JDBC2的标准扩展。使用它的开源项目有Hibernate,Spring等。性能更强,拥有自动回收空闲连接功能)    

  1)手动配置

ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver");
        cpds.setJdbcUrl("jdbc:mysql:///mytransation");
        cpds.setUser("root");
        cpds.setPassword("root");
        Connection con = cpds.getConnection();
        ResultSet rs = con.prepareStatement("select * from account").executeQuery();
        while(rs.next()){
            System.out.println("账户:"+rs.getString("name")+",余额:"+rs.getDouble("money")+"c3p0hand");
        }
        JDBCUtils.closeRS(rs);
        con.close();//不是关闭连接,而是放回连接池

        2)自动配置

C3p0的配置文件可以是properties也可以是xml,对应的文件名是c3p0.properties或者c3p0-config.xml,要求放在classpath路径下(也就是web应用的classes目录),我们放在src目录下即可。

使用:ComboPooledDataSource cpds = new ComboPooledDataSource();

   Xml文件形式:

       <?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///mytransation</property>
        <property name="user">root</property>
        <property name="password">root</property>
    </default-config>
</c3p0-config>

Properties形式:

 

                 (4)tomcat内置连接池(测试出来!!)

Tomcat连接池用的是dbcp。

1)tomcat怎样管理连接池

   要想将一个dbcp连接池让tomcat管理,只需要创建一个context.xml配置文件,在配置文件中配置相关信息。

<Context>

<Resource  name=”jdbc/EmployeeDB” auth=”Container”

        Type=”javax.sql.DataSource” username=”root” password=”root”

     driverClassName=”com.mysql.jdbc.Driver” url=”jdbc:mysql:///mytransat”

                                   maxActive=”8” maxIdle=”4”

/>

</Context>

问题:context.xml文件的配置位置:

      (1)在tomcat/conf/context.xml 这个连接池是给整个服务器用的(全局)

      (2)在tomcat/conf/Catalina/localhost 连接池只给localhost虚拟主机使用(全局)。

(3)将其配置在web应用的META-INF下。

注意:如果是全局设置,那么需要将数据库驱动放置在tomcat/lib目录下。

问题:怎么从tomcat获取链接池对象?

Context context = new InitialContext();

Context envCtx = (Context) context.lookup(“java:comp/env”);//固定路径

DataSource ds = (DataSource)envCtx.lookup(“jdbc/EmployeeDB”);

.......操作Connection对象

上一篇:Android图形图像(三)绘图

下一篇:JavaScript与HTML的结合方式

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部