美文网首页
事务&数据库连接池&DBUtils

事务&数据库连接池&DBUtils

作者: hgzzz | 来源:发表于2019-04-27 14:06 被阅读0次

    事务

    1. Transaction 其实指的一组操作,里面包含许多个单一的逻辑。只要有一个逻辑没有执行成功,那么都算失败。 所有的数据都回归到最初的状态(回滚)

    2. 事务的作用:为了确保逻辑的成功。 例子: 银行的转账。

    3. 使用命令行方式演示事务。

      • 关闭自动提交功能。


        关闭自动提交
      • 开启事务 start transaction;

        开启事务
      • 提交或者回滚事务:事务提交后或者回滚就结束了

        1. commit; 提交事务, 数据将会写到磁盘上的数据库


          commit
        2. rollback ; 数据回滚,回到最初的状态


          rollback
    4. 使用代码方式演示事务

    public void testTransaction() throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
    
        try {
            conn = JDBCUtil.getConn();
            String sql = "update bank set money = money - ? where id = ?";
            ps = conn.prepareStatement(sql);
    
            // 关闭提交 事务只是针对连接连接对象,如果再开一个连接对象,那么还是默认的提交。
            conn.setAutoCommit(false);
    
            ps.setInt(1, 100);
            ps.setInt(2, 1);
            ps.executeUpdate();
    
            int a = 10 / 0;
    
            ps.setInt(1, -100);
            ps.setInt(2, 2);
            ps.executeUpdate();
    
            // 两段代码都执行成功 提交事务
            conn.commit();
        } catch (SQLException e) {
            // 出现异常,回滚事务
            conn.rollback();
            e.printStackTrace();
        } finally {
            JDBCUtil.release(conn, ps);
        }
    
    }
    
    1. 事务的特性
      • 原子性:指的是 事务中包含的逻辑,不可分割。
      • 一致性:指的是 事务执行前后。数据完整性
      • 隔离性:指的是 事务在执行期间不应该受到其他事务的影响
      • 持久性:指的是 事务执行成功,那么数据应该持久保存到磁盘上。
    2. 事务的安全隐患 :不考虑隔离级别设置,那么会出现以下问题
        1. 脏读:一个事务读到另外一个事务还未提交的数据
        2. 不可重复读 :一个事务读到了另外一个事务提交的数据 ,造成了前后两次查询结果不一致。
        3. 幻读:一个事务读到了另一个事务insert的数据 ,造成前后查询结果不一致 。
      • 写:丢失更新


        丢失更新
        1. 悲观锁(认为一定会丢失更新):可以在查询的时候,加入 for update(数据库锁机制,排他锁),有点类似序列化


          悲观锁
        2. 乐观锁(认为一定不会丢失更新):要求程序员自己控制。


          乐观锁
    3. 事务的隔离级别:mySql 默认的隔离级别是 可重复读,Oracle 默认的隔离级别是 读已提交
      • 读未提交(Read Uncommitted):可以读到其他事务未提交的数据。引发问题: 脏读
      • 读已提交(Read Committed):只能读到已提交的数据。解决: 脏读 , 引发: 不可重复读
      • 可重复读(Repeatable Read):事务中读取的数据不受其他事务提交数据的影响,前后读取的数据一致。解决: 脏读 、 不可重复读 , 未解决: 幻读
      • 可串行化(Serializable) :如果有一个连接的隔离级别设置为了串行化 ,那么谁先打开了事务, 谁就有了先执行的权利, 谁后打开事务,谁就只能得着,等前面的那个事务,提交或者回滚后,才能执行。 但是这种隔离级别一般比较少用。 容易造成性能上的问题。 效率比较低。解决: 脏读、 不可重复读 、 幻读。
      • 按效率划分,从高到低 读未提交 > 读已提交 > 可重复读 > 可串行化
      • 按拦截程度 ,从高到底 可串行化 > 可重复读 > 读已提交 > 读未提交

    数据库连接池

    1. 数据库的连接对象创建工作,比较消耗性能。 一开始先在内存中开辟一块空间(集合) , 先往池子里面放置 多个连接对象。 后面需要连接的话,直接从池子里面去。不要去自己创建连接了。 使用完毕, 要记得归还连接,确保连接对象能循环利用。Sun公司定义了一个连接池接口DataSource
    2. 自定义连接池
      • 连接池类
      **
      * 实现连接池,一开始在连接池中有十个连接对象
      */
      public class MyDataSource implements DataSource {
      
      List<Connection> list = new ArrayList<>(); // 连接池集合
      
      public MyDataSource() { // 构造函数 在连接池中初始化十个连接对象
          for (int i = 0; i < 10; i++) {
              list.add(JDBCUtil.getConn());
          }
      }
      
      /**
       * 该连接池对外公布获取连接池的方法
       * @return
       * @throws SQLException
       */
      @Override
      public Connection getConnection() throws SQLException {
          if(list.size() == 0 ) { // 连接池中没有连接对象 扩容
              for (int i = 0; i < 5; i++) {
                  list.add(JDBCUtil.getConn());
              }
          }
      
          // 移除连接池中第一个连接对象,包装过后并将它返回
          Connection conn = list.remove(0);
          Connection connWrap = new ConnectionWrap(conn, list);
      
          return connWrap;
      }
      
      • 使用装饰者模式解决连接池的归还问题,符合面向接口编程
      public class ConnectionWrap implements Connection {
      
      Connection conn = null;
      List<Connection> list = null;
      
      public ConnectionWrap(Connection conn, List<Connection> list) {
          super();
          this.conn = conn;
          this.list = list;
      }
      
      @Override
      public void close() throws SQLException {
          // 在这里写归还操作
          System.out.println("归还前" + list.size());
          list.add(conn);
          System.out.println("归还后" + list.size());
      }
      @Override
      public PreparedStatement prepareStatement(String sql) throws SQLException {
          return conn.prepareStatement(sql);
      }
      
      • 连接池的使用
      @Test
      public  void testPool() throws SQLException {
          Connection connection = null;
          PreparedStatement ps = null;
          ResultSet rs = null;
          MyDataSource dataSource = new MyDataSource();
          try {
              // 其实在这里得到的是 ConnectionWrap 对象
              connection = dataSource.getConnection();
              String sql = "select * from bank";
      
              ps = connection.prepareStatement(sql);
              rs = ps.executeQuery();
      
              while(rs.next()) {
                  String name = rs.getString("name");
                  int id = rs.getInt("id");
                  int money = rs.getInt("money");
                  System.out.println(id + "---" + name + "---" + money);
              }
          } catch (SQLException e) {
              e.printStackTrace();
          }finally {
              JDBCUtil.release(connection, ps, rs);
          }
      }
      
    3. 开源连接池
      • DBCP
        1. 导入jar包
        2. 不使用配置文件
        public class DBCPDemo {
          @Test
          public void testDBCP01() {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
        
            try {
              // 1. 得到连接池对象
              BasicDataSource dataSource = new BasicDataSource();
        
              // 2. 设置连接属性
              dataSource.setDriverClassName("com.mysql.jdbc.Driver");
              dataSource.setUrl("jdbc:mysql://localhost:3307/hgzdata");
              dataSource.setUsername("root");
              dataSource.setPassword("root");
        
              // 3. 获取连接对象
              conn = dataSource.getConnection();
        
              // 4. 数据库操作
              String sql = "select * from bank";
              ps = conn.prepareStatement(sql);
              rs = ps.executeQuery();
              while(rs.next()) {
                  int id = rs.getInt("id");
                  String name = rs.getString("name");
                  int money = rs.getInt("money");
                  System.out.println(id + "---" + name + "---" + money);
        
              }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.release(conn, ps, rs);
            }
          }
        }
        
        1. 使用配置文件 将配置文件 dbcpconfig.properties 放置在 src 目录下
        BasicDataSourceFactory factory = new BasicDataSourceFactory();
        Properties properties = new Properties();
        InputStream is = new FileInputStream("src/dbcpconfig.properties");
        properties.load(is);
        DataSource dataSource = factory.createDataSource(properties);
        
      • C3P0
        1. 导入 jar 包
        2. 不使用配置文件
        // 1. 得到连接池对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        
        // 2. 设置连接属性
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3307/hgzdata");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        
        1. 使用配置文件 将配置文件 c3p0-config.xml 放在 src 目录下
        // 1. 得到连接池对象 默认读取配置文件 获取连接信息
        ComboPooledDataSource dataSource = new ComboPooledDataSource("configname");
        

    DBUtils

    1. dbutils 只是帮我们简化了CRUD 的代码, 但是连接的创建以及获取工作。 不在他的考虑范围,导入 jar 包
    2. update 操作
    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
    //增加
    queryRunner.update("insert into account values (null , ? , ? )", "aa" ,1000);
        
    //删除
    queryRunner.update("delete from account where id = ?", 5);
        
    //更新
    queryRunner.update("update account set money = ? where id = ?", 10000000 , 6);
    
    1. query 操作 new接口的匿名实现类
    public void queryTest() {
        // 获取查询对象
        QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
    
        try {
            User user = queryRunner.query("select * from bank where id = ?", new ResultSetHandler<User>() {
    
                @Override
                public User handle(ResultSet resultSet) throws SQLException {
                    User user = new User();
                    while (resultSet.next()) {
                        user.setName(resultSet.getString("name"));
                        user.setMoney(resultSet.getInt("money"));
                    }
                    return user;
                }
            }, 1);
            System.out.println(user.toString());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    1. query 操作 使用框架的 接口实现类 查询一行数据
    public void queryTest1() {
        QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
    
        // 查询单行数据
        try {
            User user = queryRunner.query("select * from bank where id = ?",
                    new BeanHandler<User>(User.class)
                    , 2);
            System.out.println(user.toString());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    1. query 操作 使用框架的 接口实现类 查询多行数据
    public void queryTest2() {
        QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
    
        // 查询单行数据
        try {
            List<User> users = queryRunner.query("select * from bank",
                    new BeanListHandler<User>(User.class));
            for (User user: users
                 ) {
                System.out.println(user.toString());
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    1. ResultSetHandler(queryRunner.query()第二个参数) 常用的实现类
      • 最常用
      BeanHandler,  查询到的单个数据封装成一个对象
      BeanListHandler, 查询到的多个数据封装 成一个List<对象>
      
      ArrayHandler,  查询到的单个数据封装成一个数组
      ArrayListHandler,  查询到的多个数据封装成一个集合 ,集合里面的元素是数组。
      
      MapHandler,  查询到的单个数据封装成一个map
      MapListHandler,查询到的多个数据封装成一个集合 ,集合里面的元素是map。 
      
      • 不常用
      ColumnListHandler
      KeyedHandler
      ScalarHandler
      
    2. 模拟DBUtils功能代码
      • update
      public void update(String sql, Object ...args) {
          Connection conn = null;
          PreparedStatement ps = null;
      
          try {
              ComboPooledDataSource dataSource = new ComboPooledDataSource();
      
              conn = dataSource.getConnection();
      
              ps = conn.prepareStatement(sql);
      
              // 根据元数据参数 获取问号个数
              ParameterMetaData parameterMetaData = ps.getParameterMetaData();
              int paramerterCount = parameterMetaData.getParameterCount();
      
              for (int i = 0; i < paramerterCount ; i++) {
                  ps.setObject(i + 1, args[i]);
              }
              ps.executeUpdate();
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              JDBCUtil.release(conn,ps);
          }
      }
      
      • query
      public <T> T query(String sql,ResultHandler<T> handler ,Object ...args) {
      
          Connection conn = null;
          PreparedStatement ps= null;
          ResultSet rs = null;
      
          // 1. 获取连接池
          ComboPooledDataSource dataSource = new ComboPooledDataSource();
      
          try {
              // 2. 获取连接对象
              conn = dataSource.getConnection();
              ps = conn.prepareStatement(sql);
              // 3. 根据问号个数填充参数
              ParameterMetaData parameterMetaData = ps.getParameterMetaData();
              int paramterCount = parameterMetaData.getParameterCount();
              for (int i = 0; i < paramterCount ; i++) {
                  ps.setObject(i+1, args[i]);
              }
              rs = ps.executeQuery();
              // 4. 让传入的 结果处理对象来处理 结果集
              T t = (T) handler.handle(rs);
              return t;
          } catch (Exception e) {
              e.printStackTrace();
              return null;
          } finally {
              JDBCUtil.release(conn, ps, rs);
          }
      }
      
      结果集处理接口
      public interface ResultHandler<T>{
          T handle(ResultSet rs);
      }
      
      query 方法的调用
          public void testQuery(){
          User user = query("select * from bank where id = ?", new ResultHandler<User>() {
              @Override
              public User handle(ResultSet rs) {
                  User user = new User();
                  try {
                      if (rs.next()) {
                          user.setName(rs.getString("name"));
                          user.setMoney(rs.getInt("money"));
                      }
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
                  return user;
              }
          }, 3);
      
          System.out.println(user.toString());
      }
      

    相关文章

      网友评论

          本文标题:事务&数据库连接池&DBUtils

          本文链接:https://www.haomeiwen.com/subject/zwyfnqtx.html