2021年9月1日

今天的内容

1.c3p0连接池

2.druid连接池

3.JDBC事务处理

4.DBUtil这个Dao层框架

1.连接池

1.1为什么要使用连接池

数据库的连接池

现在使用的db.properties这个文件,读取这个文件中的各个属性值,进行数据库的连接,Drivermanager.getConnection()获取数据库的连接对象。

每一次执行业务(增删改查)的时候,重新申请资源(Connection对象)。运行结束以后。直接关闭资源。不好,会导致数据库压力过大。好比咱们磁盘,每一次读取,写入。

磁盘会老化的。新的磁盘。随便读随便写。不会坏,为什么咱们用了八年的话会坏。是因为在这八年内你来回读,来回写。磁盘扛不住的。

希望采用一种共享单车的一种模式:

1.投放一定数据量车

2.用户使用,使用完毕以后,归还

3.市场需求大的话,再次投放

数据库连接池:池,游泳池,盛放很多的水

在这个池子中存放很多的连接对象。不用每次都重新连接数据库。

先连数据库,获取数据库的连接对象以后,执行sql语句。执行完sql语句以后要关闭连接对象。但是不是真正的关闭,而是把这个连接对象放到这个池子中,下次再进行sql的操作的时候,直接从池子中取连接对象。而不是重新去连接数据库了。这个池子起到一个缓存的作用!!!连接池的目的保存咱们数据库的。

1.2连接池需要考虑哪些问题【代码的角度】

数据库连接池是用来管理数据库连接资源,属性等

数据库连接必要的参数:1.url 2.user 3.assword 4.数据库的驱动

好比咱们写过一个db.properties这个文件

连接池对于数据库对象的管理:

1.初始化的容量,连接对象的容量

2.容量的最大值,连接对象的最大值

3.等待时间

数据库连接对象是需要归还的,依然是用最原始的close方法,

常见的数据库池:

1.C3P0 2.DBCP 3.Druid(德鲁伊) 阿里的连接池(2020以后也不用了)

1.3C3P0连接池的使用【重点】day32_wb1项目中

使用流程:

1.导包:需要三个包

c3p0-0.9.5.jar

mchage-common-java-0.2.19.jar

mysql-connector-java-5.1.47.jar

2.书写连接池的配置文件 c3p0-config.xml文件(你只需要复制黏贴就可以了,不需要写),这个文件放在src(有些同学偏偏就不放在src文件夹下面,你就等着报错去把!!!)

3.创建核心的类

<?xml version="1.0" encoding="utf-8" ?>
<c3p0-config>
   <!-- 命名的配置 -->
   <default-config>
       <!-- 连接数据库的4项基本参数 -->
       <property name="driverClass">com.mysql.jdbc.Driver</property>
       <property name="jdbcUrl">jdbc:mysql://localhost:3306/java2109?useSSL=false</property>
       <property name="user">root</property>
       <property name="password">123456</property>
       <!-- 如果池中数据连接不够时一次增长多少个 -->
       <property name="acquireIncrement">5</property>
       <!-- 初始化连接数 -->
       <property name="initialPoolSize">5</property>
       <!-- 最小连接数 -->
       <property name="minPoolSize">5</property>
       <!-- 最大连接数 -->
       <property name="maxPoolSize">40</property>
       <!-- JDBC的标准参数,用以控制数据源内加载的PrepareStatements数量 -->
       <property name="maxStatements">200</property>
       <!-- 连接池内单个连接所拥有的最大缓存statements数 -->
       <property name="maxStatementsPerConnection">5</property>
   </default-config>

   <named-config name="test1">
       <!-- 连接数据库的4项基本参数 -->
       <property name="driverClass">com.mysql.jdbc.Driver</property>
       <property name="jdbcUrl">jdbc:mysql://localhost:3306/java2109?useSSL=true</property>
       <property name="user">root</property>
       <property name="password">123456</property>
       <!-- 如果池中数据连接不够时一次增长多少个 -->
       <property name="acquireIncrement">5</property>
       <!-- 初始化连接数 -->
       <property name="initialPoolSize">5</property>
       <!-- 最小连接数 -->
       <property name="minPoolSize">5</property>
       <!-- 最大连接数 -->
       <property name="maxPoolSize">40</property>
       <!-- JDBC的标准参数,用以控制数据源内加载的PrepareStatements数量 -->
       <property name="maxStatements">200</property>
       <!-- 连接池内单个连接所拥有的最大缓存statements数 -->
       <property name="maxStatementsPerConnection">5</property>
   </named-config>
</c3p0-config>
package com.qfedu.a_Connection;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Demo1 {
   public static void main(String[] args) throws SQLException {
       //之前获取数据库的连接对象connection   封装了一个JdbcUtil 使用的Properties对象
       //读取src下面的db.properties这个文件 把url,user,password,driver 读取出来
       //然后获取connection
       //现在c3p0获取connection对象
       //如果实例化了 ComboPooledDataSource以后,就立马读取了xml文件
       //底层是一个dom4j的文档操作方式,专门出来xml文件的
       //将xml文件读取到了pool对象中了,就这么尿性!!!
       //configName 就是配置文件中咱们没有使用默认的,使用的测试的一个数据连接
       //ComboPooledDataSource pool = new ComboPooledDataSource("test1");   <named-config name="test1">
       ComboPooledDataSource pool = new ComboPooledDataSource();//不传参就代表默认的     <default-config>
       Connection connection = pool.getConnection();
       //接受不了,我没有发现url,password,user,咋就加载了?咋就获取了connection
       //有对象接下来就是执行sql语句
       System.out.println(connection);
       String sql = "insert into work(name, age, info) values(?,?,?)";
       PreparedStatement preparedStatement = connection.prepareStatement(sql);
       preparedStatement.setObject(1, "翠翠");
       preparedStatement.setObject(2, 34);
       preparedStatement.setObject(3,"和计算机技能");
       int i = preparedStatement.executeUpdate();
       System.out.println(i);
       preparedStatement.close();
       connection.close();//j将对象放到池子中

  }
}

思考:

我之前封装JdbcUtil这个类。不用db.properties了,开始使用连接池。能不能修改?

下节课修改一下。

重新使用连接池进行JdbcUtil的封装。连接池就是获取Connection对象的

1.4使用连接池获取Connection对象day32_wb2这个项目

1.导包

c3p0-0.9.5.jar

mchage-common-java-0.2.19.jar

mysql-connector-java-5.1.47.jar

commons-beanutils-1.9.3.jar

commons-logging-1.2.jar

2.在src下面黏贴一个c3p0-config.xml文件(之前db.properties这个文件不用了)

3.修改JdbcUtil这个类,这个类之前是用db.properties,现在用的是连接池

package com.qfedu.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
* 1.完成驱动的自动加载
* 2.完成必要的数据处理 url user password
* 3.完成connection这个方法
* 4.完成统一的close方法
*/
public class JdbcUtil {

   //为什么呢么是static修饰的?是因为getConnection是静态方法,
   //静态方法中不能使用非静态的后成员属性
   private static ComboPooledDataSource pool = new ComboPooledDataSource();
   //简化getConnection方法的写法
//封装一个静态的方法,获取connetion对象
   public static Connection getConnection() {
       Connection connection = null;
       try {
           connection = pool.getConnection();
      } catch (SQLException e) {
           e.printStackTrace();
      }
       return connection;
  }

   //现在封装的时候,我不知道需要关闭哪个资源
   //有的需要关闭一个,有的需要关闭两个,有的需要关闭三个
   //关闭一个资源 connection
   public static void close(Connection connection) {
       close(connection, null, null);
  }
   //关闭两个资源 connection statement
   public static void close(Connection connection, Statement statement) {
       close(connection, statement, null);
  }
   //关闭三个资源的 connection statement   resultSet
   public static void close(Connection connection, Statement statement, ResultSet resultSet) {
       if (connection != null) {
           try {
               connection.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       if (statement != null) {
           try {
               statement.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       if (resultSet != null) {
           try {
               resultSet.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }

}

1.5druid连接池day32_wb3

德鲁伊连接池是阿里巴巴在用的一个连接池

使用流程

1.导包

druid-1.0.9.jar

2.书写配置文件 druid.properties,在src文件夹下面新建一个druid.properties

3.创建核心类

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/java2109?useSSL=false
username=root
password=123456

initialSize=5
maxActive=20
maxWait=2000
package com.qfedu.a_druid;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Properties;

public class Demo1 {
   public static void main(String[] args) throws Exception {

       //和db.properties非常相似
       Properties properties = new Properties();
       properties.load(new FileInputStream("./src/druid.properties"));
       //druid里面有一个核心类,创建数据资源核心类
       DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
       //可以拿这个dataSource获取数据库连接对象
       Connection connection = dataSource.getConnection();
       System.out.println(connection);
       String sql = "insert into work(name, age, info) values(?,?,?)";
       PreparedStatement preparedStatement = connection.prepareStatement(sql);
       preparedStatement.setObject(1, "花花");
       preparedStatement.setObject(2, 23);
       preparedStatement.setObject(3,"你就是你近几年");
       int i = preparedStatement.executeUpdate();
       System.out.println(i);
       preparedStatement.close();
       connection.close();//j将对象放到池子中
  }
}

1.6之前讲过使用c3p0对JdbcUtil进行封装day32_wb3

接下来将一下druid连接池在封装的JdbcUtil类下面咋写的

自己尝试一下

package com.qfedu.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

/**
* 1.完成驱动的自动加载
* 2.完成必要的数据处理 url user password
* 3.完成connection这个方法
* 4.完成统一的close方法
*/
public class JdbcUtil {

   private static DataSource ds;

   static {
       Properties properties = new Properties();
       try {
           properties.load(new FileInputStream("./src/druid.properties"));
           ds = DruidDataSourceFactory.createDataSource(properties);

      } catch (Exception e) {
           e.printStackTrace();
      }
  }
   //简化getConnection方法的写法
   //简化getConnection方法的写法
//封装一个静态的方法,获取connetion对象
   public static Connection getConnection() {
       Connection connection = null;
       try {
           connection = ds.getConnection();
      } catch (SQLException e) {
           e.printStackTrace();
      }
       return connection;
  }

   //现在封装的时候,我不知道需要关闭哪个资源
   //有的需要关闭一个,有的需要关闭两个,有的需要关闭三个
   //关闭一个资源 connection
   public static void close(Connection connection) {
       close(connection, null, null);
  }
   //关闭两个资源 connection statement
   public static void close(Connection connection, Statement statement) {
       close(connection, statement, null);
  }
   //关闭三个资源的 connection statement   resultSet
   public static void close(Connection connection, Statement statement, ResultSet resultSet) {
       if (connection != null) {
           try {
               connection.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       if (statement != null) {
           try {
               statement.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
       if (resultSet != null) {
           try {
               resultSet.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }

}

2.JDBC的事务操作

2.1事务的四大特性【ssm框架的时候会重新讲事务】

原子性 隔离性 一致性 持久性

2.2在JDBC里面事务怎么执行

要在JDBC李阿敏执行事务,本质是将多个sql语句包裹在一个事务上面

语法格式:

try{
   Connection conn = Drivermanager.getConnetcion();
   conn.setAutoCommit(false);//关闭自动提交
   //才去写sql语句
   insert(),delete().update();
   //如果没有错误的话,直接提交
   conn.commit();
   
}catch(Exception e) {
   //如果走异常这一步,怎么办?回滚,回到原来的状态就可以了
   conn.rollback();
}finally{
   conn.setAutoCommit(true);//一个事务结束以后,开启自动提交。默认的
   conn.close();
}

模拟转账

张三给李四转200块钱。

至少得两个sql语句

张三这个账户减钱

李四这个账户增钱

package com.qfedu.a_druid;

import com.qfedu.util.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

//事务操作的讲解
public class Demo3 {
   public static void main(String[] args) throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       try {
           //1.开启事务
           connection.setAutoCommit(false);//把自动提交关闭
           //2.编写sql语句
           //money=money+?   等号右边的money是数据库原有的值
           String sql = "update bank_sys set money=money+? where name=?";

           //减钱   张三的账户减钱 200
           PreparedStatement preparedStatement = connection.prepareStatement(sql);
           preparedStatement.setObject(1, -200);
           preparedStatement.setObject(2, "张三");
           int i = preparedStatement.executeUpdate();

           //突然这个地方有异常了
           int i2 =  1/0;//这个是异常代码
           //整体的所有sql语句都是在一个事务当中。这两个sql语句都在一个事务中
           //y一荣俱荣,一损俱损。只有你有哪一个sql语句没有执成功。那么其他sql语句也就不会成功。
           //一致性 加上事务以后要么同时成功,要么同时失败!!!事务更加安全些!!!
           //ssm 使用AOP进行事务的操作,是因为在配置文件中直接写事务,让咱们的代码更加健壮些!!!

           //增钱 李四的账户增加钱200
           preparedStatement.setObject(1, 200);
           preparedStatement.setObject(2, "李四");
           int i1 = preparedStatement.executeUpdate();
           //如果没有错误的话,就提交上面的执行的两次sql语句
           connection.commit();//事务的提交


      }catch (Exception e){
           //回滚
           connection.rollback();
      }finally {
           connection.setAutoCommit(true);
           connection.close();
      }
  }
}

和小黑屏上面的命令差不多,关键字都是一样的

3.JDBCUtil

3.1JDBCUtils是什么?

主要封装了JDBC的代码,简化dao层的操作。换句话说就是官方给咱们封装的

BaseDao,咱们自己封装了一个BaseDao。不更新了官方封装的是JDBCUtil这个。

你感觉我封装好?还是官方封装的好?

只要是官方封装的都好!!!,但是现在咱们这个JDBCUtils不用了,老程序员(2011年)我们用这个东西,现在不用了,因为apache组织不再对它进行维护了。不更新

咱们以后会学习 mybatis,mybatis-plus,hibernate, JPA 等这些框架所以淘汰了。

让大家多学习点!!!,看看底层。

不会也没有关系,后期不用,但是他可以扩展眼界!!!

只要学会BaseDao就可以了,功能是一模一样的额,都是对数据库的数据进行增删改查的

3.2入门案例

1.新建项目

2.导包

mysql-connector-java

druid

beanutils

3.工具类JdbcUtil

获取connetion对象的

https://commons.apache.org/proper/commons-dbutils/apidocs/index.html

以上官方手册,不再维护更新了

核心类叫QueryRunner

package com.qfedu.a_dbutils;

import com.qfedu.util.JdbcUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.*;
import org.junit.Test;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class TestDBUtils {
   @Test
   public void testAdd() throws SQLException {
       //1.实例化DBUtils核心类,是JDK提供的吗?是一个apach组织提供的
       //人家封装好以后了一个jar,供咱们直接使用,下载jar,并导入才能使用这个类!!!
       QueryRunner queryRunner = new QueryRunner();
       Connection connection = JdbcUtil.getConnection();
       String sql = "insert into work (name, age, info) values(?,?,?)";
       //2.调用方法执行sql语句
       int execute = queryRunner.execute(connection, sql, "小王", 38, "男女你进行洒金");
       System.out.println(execute);
       //不需关闭,因为封装好了。

  }
   @Test//删除数据
   public void testDelete() throws SQLException {
       //1.获取数据库连接对象
       Connection connection = JdbcUtil.getConnection();
       //2.创建dbutils的核心类
       QueryRunner queryRunner = new QueryRunner();
       //3.准备sql语句
       String sql = "delete from work where id = ?";
       int update = queryRunner.update(connection, sql, 5);
       System.out.println(update);

  }

   @Test//查询一条数据的第一种写法 比较麻烦
   public void testSelectOne() throws SQLException {
       //查询一条数据
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work where id = ?";
       //T这个泛型是啥?为啥有一个泛型?查询出来的数据,要把赋值给一个对象!!!
       //回调接口,和匿名内部类的写法一致。
       Work work= queryRunner.query(connection, sql, 9,new ResultSetHandler<Work>() {
           @Override
           public Work handle(ResultSet resultSet) throws SQLException {
               //有个参数叫 resultSet 这个resultSet就是取出来的结果集
               //为什么叫回调函数,先执行connection,再执行sql然后给出来一个resultSet
               //有resultSet 接下来干嘛?因为查询的是一条数据,没必要使用while循环了
               Work work = null;
               if (resultSet.next()) {
                   int id = resultSet.getInt("id");
                   String name = resultSet.getString("name");
                   int age = resultSet.getInt("age");
                   String info = resultSet.getString("info");
                   //数据取出来了,赋值给对象
                   work = new Work(id, name, age, info);
              }

               return work;
          }
      });
       System.out.println(work);
  }

   @Test//查询一条数据的第二种写法,简单的很
   public void testSelectOne2() throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work where id = ?";
       //通过connection sql   参数 把取到的数据赋值给BeanHandler这个类,然后这个类对Work对象进行赋值
       Work work = queryRunner.query(connection, sql, new Object[]{10}, new BeanHandler<Work>(Work.class));
       System.out.println(work);

  }
   //查询多条数据
   //也让他返回一个List集合
   @Test
   public void testSelectAll() throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work";
       List<Work> works = queryRunner.query(connection, sql, new BeanListHandler<Work>(Work.class));
       for (Work work : works) {
           System.out.println(work);
      }

  }
   @Test//返回值是一个Map集合,开发的时候,绝对用不到返回值是Map集合,除非特殊情况,但是咱们还要继续处理ma;
   //仔把这个Map变成List集合,咱们为什么不干脆直接使用返回List集合的方法呢?可以直接使用
   public void testSelectAll2() throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work";
       //id这个字段取出来的值会当成咱们的Map集合中key值, value值是work
       Map<Integer, Work> id = queryRunner.query(connection, sql, new BeanMapHandler<Integer, Work>(Work.class, "id"));
       System.out.println(id);
  }
   //返回一个数组,返回的是一条数据。
   @Test
   public void testSelect() throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work";
       Object[] query = queryRunner.query(connection, sql, new ArrayHandler());
       System.out.println(Arrays.toString(query));

  }
   @Test//返回的是一个数组,一条数据存到一个数组中,但是将一个数组存到List集合中了
   public void testSelectAll3() throws SQLException {
       Connection connection = JdbcUtil.getConnection();
       QueryRunner queryRunner = new QueryRunner();
       String sql = "select * from work";
       List<Object[]> query = queryRunner.query(connection, sql, new ArrayListHandler());
       for (Object[] objects : query) {
           System.out.println(Arrays.toString(objects));
      }
  }

}

明天讲前端(html css js jquery bootstrap 等)前端讲完以后 讲 servlet

posted @ 2021-09-01 19:18  张三疯321  阅读(12)  评论(0编辑  收藏  举报