JDBC与数据库连接池#yyds干货盘点#

黄沙百战穿金甲,不破楼兰终不还。这篇文章主要讲述JDBC与数据库连接池#yyds干货盘点#相关的知识,希望能为你提供帮助。
@TOC
< hr>
1 JDBC

  1. > java Database Connectivity java 数据库连接
  2. 为什么会出现JDBC?
1.1 使用JDBC的基本步骤
  1. 注册驱动
    DriverManager.registerDriver(new com.mysql.jdbc.Driver());

  2. 建立连接
    //第一种 DriverManager.getConnection("jdbc:mysql://localhost/test?user=root& password=root"); //参数一:协议 + 访问的数据库,参数二:用户名,参数三:密码。 //第二种 conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "root");

  3. 创建statement
    //创建statement,跟数据库打交道,一定需要这个对象 st = conn.createStatement();

  4. 执行sql,得到ResultSet
    //执行查询,得到结果集 String sql = "select * from stu"; rs = st.executeQuery(sql);

  5. 遍历结果集
    //遍历查询每一条记录 while (rs.next()) int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println("id=" + id + "==name==" + name + "==age==" + age);

  6. 释放资源
    if (rs != null) try rs.close(); catch (SQLException sqlEx)// ignore rs = null; ...

1.2 JDBC工具类构建
  1. 资源释放工作的整合
  2. 驱动防二次注册
    DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // Driver 这个类里面有静态代码块,一上来就执行了,所以等同于我们注册了两次驱动。 其实没这个必要的。 // 静态代码块 ---> 类加载了,就执行。 java.sql.DriverManager.registerDriver(new Driver()); // 最后形成以下代码即可。 Class.forName("com.mysql.jdbc.Driver");

  3. 使用properties配置文件
    1. 在src下面声明一个文件 xxx.properties,里面的内容如下:
      driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/student name=root password=root

    2. 在工具类里面,使用静态代码块,读取属性。
      // JDBC代码 public class JDBCUtil static String driverClass = null; static String url = null; static String name = null; static String password= null; static try //1. 创建一个属性配置对象 Properties properties = new Properties(); InputStream is = new FileInputStream("jdbc.properties"); // 使用类加载器,去读取src底下的资源文件 // InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); // 导入输入流。 properties.load(is); //读取属性 driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); name = properties.getProperty("name"); password = properties.getProperty("password"); catch (Exception e) e.printStackTrace(); /** * 获取连接对象 * @return */ public static Connection getConn() Connection conn = null; try Class.forName(driverClass); conn = DriverManager.getConnection(url, name, password); catch (Exception e) e.printStackTrace(); return conn; /** * 释放资源 * @param conn * @param st * @param rs */ public static void release(Connection conn , Statement st , ResultSet rs) closeRs(rs); closeSt(st); closeConn(conn); private static void closeRs(ResultSet rs) try if (rs != null) rs.close(); catch (SQLException e) e.printStackTrace(); finally rs = null; private static void closeSt(Statement st) try if (st != null) st.close(); catch (SQLException e) e.printStackTrace(); finally st = null; private static void closeConn(Connection conn) try if (conn != null) conn.close(); catch (SQLException e) e.printStackTrace(); finally conn = null;

      1.3 数据库的CRUD
  4. insert
    INSERT INTO t_stu (name, age) VALUES (wangqiang, 28); INSERT INTO t_stu VALUES (NULL, wangqiang2, 28);

    // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "insert into t_stu values(null , aobama , 59)"; //影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) System.out.println("添加成功"); else System.out.println("添加失败");

  5. delete
    DELETE FROM t_stu WHERE id = 6;

    // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "delete from t_stu where name=aobama"; // 影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) System.out.println("删除成功"); else System.out.println("删除失败");

  6. query
    SELECT * FROM t_stu;

    // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行sql语句,返回ResultSet String sql = "select * from t_stu"; rs = st.executeQuery(sql); // 4. 遍历结果集 while (rs.next()) String name = rs.getString("name"); int age = rs.getInt("age"); System.out.println(name + "" + age);

  7. update
    UPDATE t_stu SET age = 38 WHERE id = 1;

    // 1. 获取连接对象 conn = JDBCUtil.getConn(); // 2. 根据连接对象,得到statement st = conn.createStatement(); // 3. 执行添加 String sql = "update t_stu set age = 26 where name =qyq"; // 影响的行数,如果大于0表明操作成功,否则失败 int result = st.executeUpdate(sql); if (result > 0) System.out.println("更新成功"); else System.out.println("更新失败");

1.4 Dao模式
  1. > Data Access Object 数据访问对象
  2. 新建一个dao的接口,里面声明数据库访问规则
    /** * 定义操作数据库的方法 */ public interface UserDao /** * 查询所有 */ void findAll();

  3. 新建一个dao的实现类,具体实现早前定义的规则
    public class UserDaoImpl implements UserDao @Override public void findAll() Connection conn = null; Statement st = null; ResultSet rs = null; try //1. 获取连接对象 conn = JDBCUtil.getConn(); //2. 创建statement对象 st = conn.createStatement(); String sql = "select * from t_user"; rs = st.executeQuery(sql); while(rs.next()) String userName = rs.getString("username"); String password = rs.getString("password"); System.out.println(userName+"="+password); catch (Exception e) e.printStackTrace(); finally JDBCUtil.release(conn, st, rs);

  4. 直接使用实现
    @Test public void testFindAll() UserDao dao = new UserDaoImpl(); dao.findAll();

    1.5 Statement安全问题
  5. Statement执行,其实是拼接sql语句的。先拼接sql语句,然后再一起执行。
    String sql = "select * from t_user where username="+ username+" and password="+ password +""; UserDao dao = new UserDaoImpl(); dao.login("admin", "100234khsdf88 or 1=1"); // 一定可以登录成功 // SELECT * FROM t_user WHERE username=admin AND PASSWORD=100234khsdf88 or 1=1 // 前面先拼接sql语句,如果变量里面带有了数据库的关键字,那么一并认为是关键字。不认为是普通的字符串。 rs = st.executeQuery(sql);

    1.6 PrepareStatement
  6. > 该对象就是替换前面的statement对象。
  7. 相比较以前的statement, 预先处理给定的sql语句,对其执行语法检查。在sql语句里面使用 ? 占位符来替代后续要传递进来的变量。后面进来的变量值,将会被看成是字符串,不会产生任何的关键字。
    String sql = "insert into t_user values(null , ? , ?)"; ps = conn.prepareStatement(sql); //给占位符赋值 从左到右数过来,1 代表第一个问号,从1开始。 ps.setString(1, userName); ps.setString(2, password);

2 数据库连接池
  1. 数据库的连接对象创建工作,比较消耗性能。
  2. 一开始先在内存中开辟一块空间(集合),一开先往池子里面放置多个连接对象。后面需要连接的话,直接从池子里面取。不要去自己创建连接了。使用完毕,要记得归还连接。确保连接对象能循环利用。
2.1 自定义数据库连接池
  1. 代码实现
    /* *1. 开始创建10个连接。 *2. 来的程序通过getConnection获取连接 *3. 用完之后,使用addBack归还连接。 *4. 扩容。 */ public class MyDataSource implements DataSource List < Connection> list = new ArrayList< Connection> (); publicMyDataSource() for (int i = 0; i < 10; i++) Connection conn = JDBCUtil.getConn(); list.add(conn); // 该连接池对外公布的获取连接的方法 @Override public Connection getConnection() throws SQLException //来拿连接的时候,先看看,池子里面还有没有。 if(list.size() == 0 ) for (int i = 0; i < 5; i++) Connection conn = JDBCUtil.getConn(); list.add(conn); //remove(0) ---> 移除第一个。移除的是集合中的第一个。移除的是开始的那个元素 Connection conn = list.remove(0); return conn; /** * 用完之后,记得归还。 * @param conn */ public void addBack(Connection conn) list.add(conn); ......

  2. 出现的问题:
    1. 需要额外记住 addBack方法
    2. 无法面向接口编程。
      UserDao dao = new UserDaoImpl(); dao.insert(); DataSource dataSource = new MyDataSource(); // 因为接口里面没有定义addBack方法。

    3. 怎么解决? 以addBack 为切入点。 2.2 解决自定义数据库连接池出现的问题。
2.3 如何扩展某一个方法?
  1. 直接改源码 -> 无法实现。
  2. 继承 -> 必须得知道这个接口的具体实现是谁。
  3. 使用装饰者模式。
3 开源连接池 3.1 DBCP
  1. 导入jar文件
  2. 不使用配置文件:
    public void testDBCP01() Connection conn = null; PreparedStatement ps = null; try // 1. 构建数据源对象 BasicDataSource dataSource = new BasicDataSource(); // 连的是什么类型的数据库, 访问的是哪个数据库,用户名,密码。。 // jdbc:mysql://localhost/bank 主协议:子协议 ://本地/数据库 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost/bank"); dataSource.setUsername("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admin"); ps.setInt(2, 1000); ps.executeUpdate(); catch (SQLException e) e.printStackTrace(); finally JDBCUtil.release(conn, ps);

  3. 使用配置文件方式:
    1. 声明一个文件 xxx.properties,里面的内容如下:
      # 连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/bank username=root password=root

    #< !-- 初始化连接 -->
    initialSize=10
    #最大连接数量
    maxActive=50
    #< !-- 最大空闲连接 -->
    maxIdle=20
    #< !-- 最小空闲连接 -->
    minIdle=5
    #< !-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
    maxWait=60000
    #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property; ]
    #注意:" user" 与 " password" 两个属性会被明确地传递,因此这里不需要包含他们。
    connectionProperties=useUnicode=true; characterEncoding=gbk
    #指定由连接池所创建的连接的自动提交(auto-commit)状态。
    defaultAutoCommit=true
    #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
    #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
    defaultTransactionIsolation=READ_UNCOMMITTED
    ```java Connection conn = null; PreparedStatement ps = null; try BasicDataSourceFactory factory = new BasicDataSourceFactory(); Properties properties = new Properties(); InputStream is = new FileInputStream("src//dbcpconfig.properties"); properties.load(is); DataSource dataSource = factory.createDataSource(properties); //2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null, ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "liangchaowei"); ps.setInt(2, 100); ps.executeUpdate(); catch (Exception e) e.printStackTrace(); finally JDBCUtil.release(conn, ps);

    3.2 C3P0
  4. 导入jar文件
  5. 【JDBC与数据库连接池#yyds干货盘点#】不使用配置文件方式
    Connection conn = null; PreparedStatement ps = null; try // 1. 创建datasource ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 2. 设置连接数据的信息 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); dataSource.setUser("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admi234n"); ps.setInt(2, 103200); ps.executeUpdate(); catch (Exception e) e.printStackTrace(); finally JDBCUtil.release(conn, ps);

  6. 使用配置文件方式
    // 默认会找 xml 中的 default-config 分支。 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 1. 设置连接数据的信息 dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); dataSource.setUser("root"); dataSource.setPassword("root"); // 2. 得到连接对象 conn = dataSource.getConnection(); String sql = "insert into account values(null , ? , ?)"; ps = conn.prepareStatement(sql); ps.setString(1, "admi234n"); ps.setInt(2, 103200);

    3.3 DBUtils
3.3.1 增删改
  1. //dbutils 只是帮我们简化了CRUD 的代码, 但是连接的创建以及获取工作。 不在它的考虑范围 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);

    3.3.2 查询
  2. 直接new接口的匿名实现类
    QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); Accountaccount =queryRunner.query("select * from account where id = ?", new ResultSetHandler< Account> () @Override public Account handle(ResultSet rs) throws SQLException Account account=new Account(); while(rs.next()) String name = rs.getString("name"); int money = rs.getInt("money"); account.setName(name); account.setMoney(money); return account; , 6); System.out.println(account.toString());

  3. 直接使用框架已经写好的实现类。
    • 查询单个对象
      QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource()); //查询单个对象 Account account = queryRunner.query("select * from account where id = ?", new BeanHandler< Account> (Account.class), 8);

    • 查询多个对象
      QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());

    List< Account> list = queryRunner.query(" select * from account " ,new BeanListHandler< Account> (Account.class));
    ### 3.3.3 ResultSetHandler 常用的实现类

  4. // 以下两个是使用频率最高的 BeanHandler,// 查询到的单个数据封装成一个对象 BeanListHandler,// 查询到的多个数据封装 成一个List< 对象> // ------------------------------------------------- ArrayHandler,// 查询到的单个数据封装成一个数组 ArrayListHandler,// 查询到的多个数据封装成一个集合 ,集合里面的元素是数组。 MapHandler,// 查询到的单个数据封装成一个map MapListHandler,// 查询到的多个数据封装成一个集合 ,集合里面的元素是map。


    推荐阅读