跳至主要內容

JDBC

程序员李某某原创数据库MySQLjdbc大约 21 分钟

JDBC

获取数据库连接

获取数据库连接方式一

1.获取Driver对象

2.配置url协议地址

3.获取Properties对象,把用户名,密码封装到Properties

4.调用Driver的connect方法,获取连接

​ url:jdbc:mysql://localhost:3306/database? 传输协议/主机/端口号/数据库(文件)/

​ rewriteBatchedStatements=true (参数)读写批处理编译

​ characterEncoding=utf8 (参数)字符集

​ useSSL=false 安全验证

​ serverTimezone=UTC 时区

​ 参数之间用&连接

Driver driver = new com.mysql.cj.jdbc.Driver();//8.0
		//Driver driver = new com.mysql.jdbc.Driver();//5.7
		//String url = "jdbc:mysql://localhost:3306/database";
String url = "jdbc:mysql://localhost:3306/database?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";

Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","abc123");

Connection conn = driver.connect(url, info);

获取数据库连接方式二

由于Driver是第三方的,不方便管理,所以用反射替代

使用DriverManager替换Driver来获取连接

​ DriverManager中有一个返回Connection类型的静态方法getConnection

1.提供url、user、password

2.加载Driver(<u>mysql可以省,但不要省,写入配置文件后更改为其他像Oracle就不行了</u>)

3.通过DriverManager.getConnection()获取连接

String url = "jdbc:mysql://localhost:3306/atguigudb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
String user = "root";
String password = "abc123";

Class.forName("com.mysql.cj.jdbc.Driver");

Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);

获取数据库连接方式三

1.实现了数据与代码的分离。实现了解耦 2.如果需要修改配置文件信息,可以避免程序重新打包。(配置文件不被打包)

1.将url、user、password、driver对象写入配置文件.properties

在idea中,配置文件放在resources目录下

在eclipse中,放在src目录下

2.通过类的加载器,获取配置文件输入流,为了更具通用性,我们选择系统类加载器

3.创建Properties对象,输入流作为形参,读取配置文件信息

4.加载驱动

5.获取连接

此处直接封装为方法:如下

public class JDBCUtils {
    public static Connection getConnection() throws Exception {
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");

        Properties pros = new Properties();
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty( "password");
        String url = pros.getProperty("url");
        String driver = pros.getProperty("driver");

        Class.forName(driver);

        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }
}

PreparedStatment实现增删改

Statement弊端

使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题 如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement

PreparedStatment优点

解决Statement的拼串、sql注入问题:占位符

PreparedStatement操作Blob的数据,而Statement做不到。

PreparedStatement可以实现更高效的批量操作。

关闭资源封装为方法

public class JDBCUtils {
    public static void closeResource(Connection conn, PreparedStatement ps){
            try {
                if (conn != null)
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (ps != null)
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
    }
}

增删改通用步骤

1.获取连接

2.预编译sql语句,返回PreparedStatement实例

3.填充占位符

4.执行

5.资源关闭

Connection conn = JDBCUtils.getConnection();

String sql = "update employees2 set last_name = ? where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);

ps.setString(1,"李某某");
ps.setInt(2, 110);

ps.execute();

conn.close();
ps.close();

增删改封装为方法

public class JDBCUtils { 
    public static void update(String sql,Object ... args){
            Connection conn = null;
            PreparedStatement ps = null;
            try {
                conn = JDBCUtils.getConnection();
                ps = conn.prepareStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i + 1,args[i]);
                }
                ps.execute();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtils.closeResource(conn,ps);
            }
        }
}

测试添加:(注意,字段和表名不能用占位符代替

@Test
public void testInsert() {
        String sql = "INSERT INTO `employees2`(last_name,email,job_id) VALUES (?,?,?)";
        JDBCUtils.update(sql,"刘思思","hfkajshfjkahfajhf","dah5j");
}

PreparedStatment实现查询

ORM编程思想 (object relational mapping)

  • 一个数据表对应一个java类
  • 表中的一条记录对应java类的一个对象
  • 表中的一个字段对应java类的一个属性

查询步骤

image-20220505212508911
image-20220505212508911

1.获取连接

2.预编译sql,返回PreparedStatement实例

3.填充占位符

4.执行,并返回结果集

5.处理结果集

6.关闭资源

Connection conn = JDBCUtils.getConnection();

String sql = "select last_name,email,salary from employees where employee_id = ?";
PreparedStatement ps = conn.prepareStatement(sql);

ps.setObject(1,101);

ResultSet resultSet = ps.executeQuery();//执行并返回结果集

if (resultSet.next()){  //next()类似迭代器,判断是否有下条数据,有并下移
      //获取当前这条数据的各个字段
      String last_name = resultSet.getString(1);
      String email = resultSet.getString(2);
      double salary = resultSet.getDouble(3);
	  Employees dates = new Employees(last_name, email, salary);
      System.out.println(dates);
}

JDBCUtils.closeResource(conn,ps,resultSet);

通用操作低配版

查一个表,一条数据

 public static Employee queryForEmployees2(String sql,Object...args) throws Exception {
        Connection conn = JDBCUtils.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i + 1,args[i]);
        }
        ResultSet rs = ps.executeQuery();
        //通过结果集获取元数据,元数据含每个字段的信息
        ResultSetMetaData rsmd = rs.getMetaData();
        //通过元数据获取列数,即字段个数
        int columnCount = rsmd.getColumnCount();
        if (rs.next()){
            Employee emp = new Employee();
            for (int i = 0; i < columnCount; i++) {
                Object columnvalue = rs.getObject(i + 1);
                //String columnName = rsmd.getColumnName(i + 1);//通过元数据获取列名
                //sql和java的命名方式不同,开发中字段和属性对应不上,我们只能通过别名来进行操作
                String columnLabel = rsmd.getColumnLabel(i + 1);//通过元数据获取别名

                //列名就是Employee类的一个属性,知道了列名和要赋的值,用反射
                Field field = emp.getClass().getDeclaredField(columnLabel);
                field.setAccessible(true);
                field.set(emp,columnvalue);
            }
            return emp;
        }
        JDBCUtils.closeResource(conn,ps,rs);
        return null;
    }

通用操作升级版

查不同表,一条数据

public static <T> T query(Class<T> clazz,String sql,Object...args) throws Exception {
        Connection conn = JDBCUtils.getConnection();
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i + 1,args[i]);
        }
        ResultSet rs = ps.executeQuery();
        if (rs.next()){
            T t = clazz.newInstance();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            for (int i = 0; i < columnCount; i++) {
                Object columnValue = rs.getObject(i + 1);
                String columnLable = rsmd.getColumnLabel(i+1);

                Field field = clazz.getDeclaredField(columnLable);
                field.setAccessible(true);
                field.set(t,columnValue);
            }
            return t;
        }
        JDBCUtils.closeResource(conn,ps,rs);
        return null;
    }

通用操作终极版

查不同表,多条数据

public static <E> List<E> queryAll(Class<E> clazz,String sql,Object...args) throws Exception {
       Connection conn = JDBCUtils.getConnection();
       PreparedStatement ps = conn.prepareStatement(sql);
       for (int i = 0; i < args.length; i++) {
           ps.setObject(i + 1,args[i]);
       }
       ResultSet rs = ps.executeQuery();

       ArrayList<E> list = new ArrayList<>();

       while (rs.next()){
           E e = clazz.newInstance();
           ResultSetMetaData rsmd = rs.getMetaData();
           int columnCount = rsmd.getColumnCount();
           for (int i = 0; i < columnCount; i++) {
               Object columnValue = rs.getObject(i + 1);
               String columnLable = rsmd.getColumnLabel(i+1);

               Field field = clazz.getDeclaredField(columnLable);
               field.setAccessible(true);
               field.set(e,columnValue);
           }
           list.add(e);
       }
       return list;
       JDBCUtils.closeResource(conn,ps,rs);
       return null;
   }

PreparedStatment实现读取BLOB字段

BLOB字段,二进制字段,主要用来存储图片,视频,音频

image-20220505212652376
image-20220505212652376
  • 实际使用中根据需要存入的数据大小定义不同的BLOB类型。
  • 需要注意的是:如果存储的文件过大,数据库的性能会下降。
  • 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。(8.0未测试)

插入图片(增删相似)

public void insertTest() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        String sql = "insert into employees2(last_name,photo)values (?,?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setObject(1,"王八蛋");
        FileInputStream fis = new FileInputStream(new File("D:\\workspace_idea\\myself\\jdbc\\jdbc01\\src\\main\\java\\com\\botuer\\java2\\100.jpeg"));
        ps.setObject(2,fis);
        ps.execute();
        JDBCUtils.closeResource(conn,ps);
        fis.close();
    }

查询图片

 public void queryTest() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        String sql = "select id,last_name,photo from employees2 where last_name = '王八蛋'";
        PreparedStatement ps = conn.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
        if (rs.next()){
            int id = (Integer)rs.getInt(1);
            String last_name = rs.getString(2);
            Blob photo = rs.getBlob(3);
            Employee employee = new Employee(id, last_name);
            System.out.println(employee);

            InputStream is = photo.getBinaryStream();
            FileOutputStream fos = new FileOutputStream("liusisi.jpg");
            BufferedInputStream bis = new BufferedInputStream(is);
            BufferedOutputStream bos = new BufferedOutputStream(fos);

            byte[] buff = new byte[1024];
            int len;
            while ((len = bis.read(buff)) != -1){
                bos.write(buff,0,len);
                bos.flush();
            }
        }
     	is.close();
        fos.close();
        bis.close();
        bos.close();
        JDBCUtils.closeResource(conn,ps,rs);
    }

PreparedStatment实现批量操作

update、delete本身就具有批量操作的效果。

此时的批量操作,主要指的是批量插入。

方式一:Statement

public void insertTest1() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        Statement st = conn.createStatement();
        for (int i = 1; i <= 20000; i++) {
            String sql = "insert into goods(name)values('name_" + i + "')";
            st.execute(sql);
        }
    }

方式二

public void insertTest2() throws Exception {
        long start = System.currentTimeMillis();

        Connection conn = JDBCUtils.getConnection();
        String sql = "insert into goods1 (name) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 20000; i++) {
            ps.setString(1,"name_" + i);
            ps.execute();
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));//20000-- 63730 

方式三:攒sql

PreparedStatement的三个方法:类似流中的 byte[] buff = new byte[1024];

addBatch() 添加一批

executeBatch() 执行一批

clearBatch() 清理一批

public void insertTest3() throws Exception {
        long start = System.currentTimeMillis();

        Connection conn = JDBCUtils.getConnection();
        String sql = "insert into goods1 (name) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        for (int i = 0; i < 20000; i++) {
            ps.setString(1,"name_" + i);

            ps.addBatch();
            if(i % 500 == 0){
                ps.executeBatch();
                ps.clearBatch();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));//20000--2248,2000000--86142
    }

最终版:设置不自动提交

Connection的两个方法

setAutoCommit(false) 设置不自动提交

commit() 提交

public void insertTest3() throws Exception {
        long start = System.currentTimeMillis();

        Connection conn = JDBCUtils.getConnection();
        String sql = "insert into goods1 (name) values (?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        conn.setAutoCommit(false);
        for (int i = 0; i < 2000000; i++) {
            ps.setString(1, "name_" + i);
            ps.addBatch();
            if (i % 500 == 0) {
                ps.executeBatch();
                ps.clearBatch();
            }
        }
        conn.commit();
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));//20000--1351,2000000--63668
    }

事务处理

事务概述

1.什么叫数据库事务?

​ 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

​ 一组逻辑操作单元:一个或多个DML操作。

2.事务处理的原则:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。

当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;

要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

3.数据一旦提交,就不可回滚

4.哪些操作会导致数据的自动提交?

① DDL操作一旦执行,都会自动提交。

​ set autocommit = false 对DDL操作失效

② DML默认情况下,一旦执行,就会自动提交。

​ 我们可以通过set autocommit = false的方式取消DML操作的自动提交。

③ 默认在关闭连接时,会自动的提交数据

银行转账未考虑事务

 public void updateBalance1() {
        String sql1 = "update user_table set balance = balance - 100 where id = ?";
        JDBCUtils.update(sql1, 1);
        //模拟异常
        System.out.println(10/0);
        String sql2 = "update user_table set balance = balance + 100 where id = ?";
        JDBCUtils.update(sql2, 2);
    }

银行转账考虑事务

JDBC程序中为了让多个 SQL 语句作为一个事务执行:

  • 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
  • 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
  • 在出现异常时,调用 rollback(); 方法回滚事务

若此时 Connection 没有被关闭,还可能被重复使用,则需要恢复其自动提交状态 setAutoCommit(true)。尤其是在使用数据库连接池技术时,执行close()方法前,建议恢复自动提交状态。

步骤一、原增删改方法中,执行一次DML后关闭了连接,

不需要方法执行此操作,因此我们要自己创建连接,自己关闭连接

//考虑事务增删改
   public static void updateWithTransaction(Connection conn,String sql,Object ... args){
        PreparedStatement ps = null;
        try {
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps);
        }
    }

步骤二、设置自动提交conn.setAutoCommit(false)

完后事务后提交 conn.commit()

并重新设置自动提交conn.setAutoCommit(true)

在catch中执行conn.rollback()

 public void updatebalance2()  {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            conn.setAutoCommit(false);

            String sql1 = "update user_table set balance = balance - 100 where id = ?";
            JDBCUtils.updateWithTransaction(conn, sql1, 1);

            //模拟异常
            System.out.println(10/0);
            
            String sql2 = "update user_table set balance = balance + 100 where id = ?";
            JDBCUtils.update(sql2, 2);
            System.out.println("转账成功");

            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            JDBCUtils.closeResource(conn, null);
        }
    }

设置数据库隔离级别

2个事务,一个修改,一个查询

首先把查询通用方法改为考虑事务的

 public static <E> List<E> selectWithTransaction(Connection conn, Class<E> clazz, String sql, Object... args) throws Exception {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            ArrayList<E> list = new ArrayList<>();
            while (rs.next()){
                E e = clazz.newInstance();
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLable = rsmd.getColumnLabel(i+1);

                    Field field = clazz.getDeclaredField(columnLable);
                    field.setAccessible(true);
                    field.set(e,columnValue);
                }
                list.add(e);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(null,ps,rs);
        }
        return null;
    }

修改事务:不提交,不关闭

//数据库隔离级别测试:更新事务
    public void testUpdateWithTransaction() throws Exception {
        Connection conn = JDBCUtils.getConnection();
        conn.setAutoCommit(false);
        String sql = "update user_table set balance = ? where id = ? or id = ?";
        JDBCUtils.updateWithTransaction(conn,sql,4000,3,4);

        Thread.sleep(10000);
        System.out.println("修改结束");
    }

查询事务:不提交,不关闭

conn.setTransactionIsolation(),参数可以是常量值,也可以直接通过Connection调静态属性

conn.getTransactionIsolation()

静态属性TRANSACTION_READ_UNCOMMITTED,TRANSACTION_READ_COMMITTED,TRANSACTION_REPEATABLE_READ,TRANSACTION_SERIALIZABLE

 //数据库隔离级别测试:查询事务
    public void testSelectWithTransaction() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            conn.setTransactionIsolation(1);
            //获取隔离级别
            System.out.println(conn.getTransactionIsolation());
            conn.setAutoCommit(false);
            String sql = "select id,name,balance from user_table where id = ? or id = ?";
            List<UserTable> users = JDBCUtils.selectWithTransaction(conn, UserTable.class, sql, 3, 4);
            users.forEach(System.out::println);
//            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
//            try {
//                conn.setAutoCommit(true);
//            } catch (SQLException e) {
//                e.printStackTrace();
//            }
//            JDBCUtils.closeResource(conn,null,null);
        }
    }

数据库连接池

JDBC数据库连接池的必要性

连接资源不能重复利用,大量同时申请连接资源,导致服务器崩溃

每次用完都需要断开,导致内存泄漏(对象未被回收)

不能控制被创建的连接数(需要就会申请,连接过多,导致服务器崩溃,内存泄漏)

数据库连接池

image-20220507112039512
image-20220507112039512

我们需要一个缓冲区,存放可以重复利用的现有的连接,提高了系统反应速度可以设置最大最小默认连接数防止服务器崩溃,连接用完放回连接池,不需要断开,统一的连接管理,避免了内存泄漏

多种开源的数据库连接池

JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:

  • DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。

  • C3P0 是一个开源组织提供的一个数据库连接池,**速度相对较慢,稳定性还可以。**hibernate官方推荐使用

  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点

  • BoneCP 是一个开源组织提供的数据库连接池,速度快

  • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快

  • DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池

  • DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。

  • 特别注意:

    • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
    • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

C3P0连接池

配置文件为:【c3p0-config.xml】

配置文件信息

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
	<named-config name="helloc3p0">
		<!-- 获取连接的4个基本信息 -->
		<property name="user">root</property>
		<property name="password">abc123</property>
		<property name="jdbcUrl">jdbc:mysql:///test</property>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		
		<!-- 涉及到数据库连接池的管理的相关属性的设置 -->
		<!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
		<property name="acquireIncrement">5</property>
		<!-- 初始化数据库连接池时连接的数量 -->
		<property name="initialPoolSize">5</property>
		<!-- 数据库连接池中的最小的数据库连接数 -->
		<property name="minPoolSize">5</property>
		<!-- 数据库连接池中的最大的数据库连接数 -->
		<property name="maxPoolSize">10</property>
		<!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
		<property name="maxStatements">20</property>
		<!-- 每个连接同时可以使用的 Statement 对象的个数 -->
		<property name="maxStatementsPerConnection">5</property>

	</named-config>
</c3p0-config>

获取连接

private static DataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection2() throws SQLException{
	Connection conn = cpds.getConnection();
	return conn;
}

DBCP连接池(弃用)

配置文件为:【dbcp.properties】

配置文件信息

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=abc123

initialSize=10
#...
  • 配置属性说明
属性默认值说明
initialSize0连接池启动时创建的初始化连接数量
maxActive8连接池中可同时连接的最大的连接数
maxIdle8连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle0连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait无限制最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatementsfalse开启池的Statement是否prepared
maxOpenPreparedStatements无限制开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout300超过时间限制,回收没有用(废弃)的连接
removeAbandonedfalse超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收

获取连接

private static DataSource source = null;
static{									//读配置文件,创建连接池对象
	try {
		Properties pros = new Properties();	
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");		
		pros.load(is);
		//根据提供的BasicDataSourceFactory创建对应的DataSource对象
		source = BasicDataSourceFactory.createDataSource(pros);
	} catch (Exception e) {
		e.printStackTrace();
	}	
}
public static Connection getConnection4() throws Exception {		
	Connection conn = source.getConnection();	
	return conn;
}

Druid(德鲁伊)连接池

Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。

调用上和DBCP差不多

配置文件为:【druid.properties】

配置文件信息

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=20
maxWait=1000
filters=wall
  • 详细配置参数:
配置缺省说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:<https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter>
driverClassName根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

获取连接

private static DataSource source = null;
static{									//读配置文件,创建连接池对象
	try {
		Properties pros = new Properties();	
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");		
		pros.load(is);
		//根据提供的BasicDataSourceFactory创建对应的DataSource对象
		source = DruidDataSourceFactory.createDataSource(pros);
	} catch (Exception e) {
		e.printStackTrace();
	}	
}
public static Connection getConnection4() throws Exception {		
	Connection conn = source.getConnection();	
	return conn;
}

Apache-DbUtils实现CRUD操作

DbUtils类:作用不大,都可以自己写

close(参数) 参数可为Connection,Statement,ResultSet

closeQuietly(参数)

closeQuietly(Connection conn, Statement stmt, ResultSet rs)

QueryRunner类

int update(Connection conn, String sql, Object... params) 增删改,返回值为影响多少条记录

T insert(Connection conn,String sql,ResultSetHandler<T> rsh, Object... params) 只能插入

int[] batch(Connection conn,String sql,Object[][] params) 批处理

T insertBatch(Connection conn,String sql,ResultSetHandler<T> rsh,Object[][] params) 只能批量插入

Object query(Connection conn, String sql, ResultSetHandler rsh,Object... params) 查询

public void testInsert() throws Exception {
	QueryRunner runner = new QueryRunner();			//创建QueryRunner对象
	Connection conn = JDBCUtils.getConnection3();	//考虑事务情况下,需要自己创建链接
	String sql = "insert into customers(name,email,birth)values(?,?,?)";	//预编译sql
	int count = runner.update(conn, sql, "何成飞", "he@qq.com", "1992-09-08");	//调用update
	System.out.println("添加了" + count + "条记录");	
	JDBCUtils.closeResource(conn, null);		//关闭资源
}

ResultSetHandler接口及实现类

定义返回数据结果的形式

主要实现类

  • **BeanHandler:**将结果集中的第一行数据封装到一个对应的JavaBean实例中。即查一行,以对象的形式呈现。有泛型和形参
  • **BeanListHandler:**查每一行,以对象组成的List的形式呈现。有泛型和形参
  • **ScalarHandler:**查询单个值对象。没有泛型和形参
  • **MapHandler:**将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。即查一行,以Map的形式呈现。没有泛型和形参
  • **MapListHandler:**查每一行,以Map组成的List的形式呈现。没有泛型和形参
  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
public void testQueryList() throws Exception{
	QueryRunner runner = new QueryRunner();		
	Connection conn = JDBCUtils.getConnection3();	
	String sql = "select id,name,email,birth from customers where id < ?";	
    //以类的对象形式呈现,需要填入泛型
	BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
	List<Customer> list = runner.query(conn, sql, handler, 23);
	list.forEach(System.out::println);	
	JDBCUtils.closeResource(conn, null);
}

自定义ResultSetHandler的实现类

public void testQueryInstance1() throws Exception{
	QueryRunner runner = new QueryRunner();
	Connection conn = JDBCUtils.getConnection3();	
	String sql = "select id,name,email,birth from customers where id = ?";	
	ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>() {

		@Override
		public Customer handle(ResultSet rs) throws SQLException {
			System.out.println("handle");
//			return new Customer(1,"Tom","tom@126.com",new Date(123323432L));				
			if(rs.next()){
				int id = rs.getInt("id");
				String name = rs.getString("name");
				String email = rs.getString("email");
				Date birth = rs.getDate("birth");				
				return new Customer(id, name, email, birth);
			}
			return null;				
		}
	};		
	Customer customer = runner.query(conn, sql, handler, 23);		
	System.out.println(customer);		
	JDBCUtils.closeResource(conn, null);
}
java类型SQL类型
booleanBIT
byteTINYINT
shortSMALLINT
intINTEGER
longBIGINT
StringCHAR,VARCHAR,LONGVARCHAR
byte arrayBINARY , VAR BINARY
java.sql.DateDATE
java.sql.TimeTIME
java.sql.TimestampTIMESTAMP
BigDecimalDECIMAL
类名常用对象用途
Driverdriver创建驱动new com.mysql.cj.jdbc.Driver()
Classclazz反射加载驱动Class.forName("com.mysql.cj.jdbc.Driver");
ClassLoader,SystemClassLoaderClassLoader.getSystemClassLoader().getResourceAsStream(“配置文件”)返回InputStream
InputStreamis读入
Propertiespros封装配置文件setProperty(),加载配置文件load(is),获取配置信息getProperty,返回配置信息
DriverManager静态方法,获取连接getConnection(url, user, password)返回Connection
Connectionconn预编译SQL语句prepareStatement(sql),返回PreparedStatement
PreparedStatementps填充占位符setObject(),执行execute(),执行返回结果集executeQuery()返回ResultSet
ResultSetrs控制指针下移next(),获取元数据getMetaData()返回ResultSetMetaData,获取数据值getObject()即列值
ResultSetMetaDatarsmd获取列数getColumnCount(),获取列名getColumnName(),获取别名getColumnLabel()
Fieldfield反射获取属性对象:类名.class.getDeclaredField(“”),私有设置setAccessible(true),改属性值set(类对象,属性值)

给数据库添加日期

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");	//格式化
java.util.Date date = sdf.parse("2019-05-06");	//解析:字符串 ---> Util下的日期
long time = date.getTime();			//获取时间戳
Date dateSQL = new Date(time);		//通过时间戳构造器创建对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
java.util.Date date = sdf.parse("2019/05/06");
System.out.println(date);	//Mon May 06 00:00:00 CST 2019
String format = sdf.format(date);
System.out.println(format);	//2019/05/06
long time = date.getTime();
Date dateSql = new Date(time);
java.util.Date dateUtil = dateSql;
System.out.println(dateUtil);	//2019-05-06
上次编辑于:
贡献者: ext.liyuanhao3