JDBC学习笔记(2)---B站尚硅谷宋红康

JDBC学习笔记(1)—B站尚硅谷宋红康

JDBC学习笔记(2)—B站尚硅谷宋红康

文章目录

  • 第6章:数据库事务
    • 6.1 数据库事务介绍
    • 6.2 JDBC事务处理
    • 6.3 事务的ACID属性
      • 6.3.1 数据库的并发问题
      • 6.3.2 四种隔离级别
      • 6.3.3 在mysql中设置隔离级别
  • 第7章:DAO及其实现类
    • 【BaseDAO.java】
    • 【CustomerDAO.java】
    • 【CustomerDAOImpl.java】
    • 【CustomerDAOImplTest.java】
    • 对于上述写法的优化
  • 第8章:数据库连接池
    • 8.1JDBC数据库连接池的必要性
    • 8.2 数据库连接池技术
    • 8.3 多种开源的数据库连接池
      • 8.3.1 C3P0数据库连接池
      • 8.3.2 DBCP数据库连接池
      • 8.3.3 Druid(德鲁伊)数据库连接池(主流用这个)
  • 第9章:Apache-DBUtils实现CRUD操作
    • 9.1 Apache-DBUtils简介
    • 9.2 主要API的使用
      • 9.2.1 DbUtils
      • 9.2.1 QueryRunner类
      • 9.2.3 ResultSetHandler接口及实现类
      • 9.2.4 使用DBUtils关闭资源
  • 第10章:总结
    • JDBC代码写法总结(使用Druid数据库连接池获取连接,使用DBUtils进行增删改查和资源的关闭)

第6章:数据库事务

6.1 数据库事务介绍

JDBC学习笔记(2)---B站尚硅谷宋红康_第1张图片

举例说明:
	AA有1000元,BB有1000元
	AA给BB转账100
	情况一:转账成功:AA-100元后有900元,BB+100元后有1100元,两个操作同时成功。
	情况二:转账失败:AA和BB都还是1000元。假如AA转了100元,但是出了问题没转过去,那么就要回滚到最初的状态,不能是情况三。
	情况三:转账失败:AA有900元,但是BB还是1000元
	
	事务:就是情况一和情况二,要么成功,要么失败回到最初的状态。
		AA-100元和BB+100元 合起来就是一个事务,作为一个工作单元执行。
	一组逻辑操作单元:一条或多条sql语句
	使数据从一种状态变成另一种状态:1000--->900 1000--->1100

	如果失败,就要回滚rollback到最初的状态;
	而回滚rollback是回到上一次提交的地方,因为有些操作是自动提交,所以可能无法回到最初的状态;
	所以要找出所有会自动提交的操作,然后将其设置为不自动提交。		

JDBC学习笔记(2)---B站尚硅谷宋红康_第2张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第3张图片

6.2 JDBC事务处理

package com.atguigu5.transanction;

import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;

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

public class TransactionTest {
    //针对数据表user_table,AA向BB转账100元

    //*******************************未考虑事务**********************************************
    //通用的增删改操作(未考虑事务)
    public void update(String sql,Object ...agrs){
        Connection connection = null;
        PreparedStatement ps = null;

        try{
            //1.获取数据库连接
            connection = JDBCUtils.getConnection();

            //2.预编译sql语句(由形参传入),返回PreparedStatement对象实例
            ps = connection.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < agrs.length; i++) {
                ps.setObject(i+1,agrs[i]);
            }

            //4.执行
            ps.execute();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //5.释放资源
            JDBCUtils.closeResource(connection,ps);
        }
    }

    @Test
    //未考虑事务
    public void testUpdate(){
        String sql1 = "update user_table set balance = balance - 100 where user = ?";
        update(sql1,"AA");

        String sql2 = "update user_table set balance = balance + 100 where user = ?";
        update(sql2,"BB");

        System.out.println("转账成功");
    }



    //*******************************考虑事务**********************************************
    /*
    考虑事务:
        如果出错了要能够回滚到最初的状态。
        怎么做?把所有能够自动提交的操作设置为不能自动提交。

        分析这个转账问题:发现需要解决两个自动提交问题,一个是DML(即update语句)自动提交,一个是关闭连接会自动提交。

        解决:关闭连接就会自动提交问题
            上面未考虑事务的逻辑:
                有两个sql操作:两次连接
                    ---获取连接1---
                    ***执行1***
                    ---关闭连接1--- 这里会自动提交
                    ---获取连接2---
                    ***执行2***
                    ---关闭连接2---

                 所以要改为:改为一次连接
                    ---获取连接---
                    ***执行1***
                    ***执行2***
                    ---关闭连接---

        解决:DML语句自动提交问题
            1.取消DML自动提交:
                conn.setAutoCommit(false);
            2.操作成功之后手动提交:
                conn.commit();
            3.操作不成功回滚:
                conn.rollback();
     */

    //通用的增删改操作(考虑事务)
    //该方法可以返回void,
    //也可以返回int。如果想要看所作操作影响了几条数据,可以使用return ps.executeUpdate(); 然后方法返回int。
    public int update(Connection conn,String sql,Object ...agrs){

        PreparedStatement ps = null;

        try{

            //1.预编译sql语句(由形参传入),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //2.填充占位符
            for (int i = 0; i < agrs.length; i++) {
                ps.setObject(i+1,agrs[i]);
            }

            //3.执行
            return ps.executeUpdate();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //4.释放资源
            JDBCUtils.closeResource(null,ps);
        }

        //这个别忘了
        return 0;
    }


    @Test
    //测试:考虑事务
    public void testUpdateWithTx(){

        Connection conn = null;

        try{
            //1.自己开连接
            conn = JDBCUtils.getConnection();

            //2.取消DML自动提交
            conn.setAutoCommit(false);

            String sql1 = "update user_table set balance = balance - 100 where user = ?";
            update(conn,sql1,"AA");

            //假如出现网络问题
            //System.out.println(10/0);

            String sql2 = "update user_table set balance = balance + 100 where user = ?";
            update(conn,sql2,"BB");

            System.out.println("转账成功");

            //3.提交数据(操作成功)
            conn.commit();

        }catch (Exception e){
            e.printStackTrace();

            //4.提交出现问题(回滚)
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {

            //5.恢复自动提交机制
            /*
            在某些时候,尤其是在数据库连接池中,数据库连接池中有很多连接,
            一个连接可能会被多个事务使用,一个事务不需要自动提交,但是下一个事务可能需要自动提交,所以要恢复自动提交机制
             */
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            
            //6.自己关连接
            JDBCUtils.closeResource(conn,null);
        }
    }

}

JDBC学习笔记(2)---B站尚硅谷宋红康_第4张图片

6.3 事务的ACID属性

JDBC学习笔记(2)---B站尚硅谷宋红康_第5张图片

6.3.1 数据库的并发问题

JDBC学习笔记(2)---B站尚硅谷宋红康_第6张图片

6.3.2 四种隔离级别

JDBC学习笔记(2)---B站尚硅谷宋红康_第7张图片

READ UNCOMMITTED 没有解决任何问题
READ COMMITTED 解决了脏读问题
REPEATABLE READ 解决了脏读和不可重复读问题
SERIALIZABLE 解决了脏读、不可重复读和幻读问题,但性能太差(一致性最好,但并发性较差)

一般使用 READ COMMITTED 和 REPEATABLE READ 
一般是数据提交之后 才允许用户读

6.3.3 在mysql中设置隔离级别

JDBC学习笔记(2)---B站尚硅谷宋红康_第8张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第9张图片

java代码中如何设置隔离级别?

JDBC学习笔记(2)---B站尚硅谷宋红康_第10张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第11张图片

alt + 7 可以看到一个类或接口的所有方法和属性等
java里面用数字代表隔离级别

mysql查看当前隔离级别:select@@transaction_isolation;
mysql默认的隔离级别是:REPEATABLE-READ

第7章:DAO及其实现类

JDBC学习笔记(2)---B站尚硅谷宋红康_第12张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第13张图片

java中的Dao是什么意思

【BaseDAO.java】

package com.atguigu6.dao;

import com.atguigu3.util.JDBCUtils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

//封装了针对数据表的通用的操作(考虑事务):通用的增删改操作,通用的查操作
//BaseDAO 基础DAO,父类DAO
//设置为抽象类,无法实例化,虽然里面没有抽样方法
public abstract class BaseDAO {

    //通用的增删改操作(考虑事务)(也应该可以用于表结构的创建删除和修改)
    //该方法可以返回void,
    //也可以返回int。如果想要看所作操作影响了几条数据,可以使用return ps.executeUpdate(); 然后方法返回int。
    public int update(Connection conn, String sql, Object ...agrs){

        PreparedStatement ps = null;

        try{

            //1.预编译sql语句(由形参传入),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //2.填充占位符
            for (int i = 0; i < agrs.length; i++) {
                ps.setObject(i+1,agrs[i]);
            }

            //3.执行
            return ps.executeUpdate();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //4.释放资源
            JDBCUtils.closeResource(null,ps);
        }

        //这个别忘了
        return 0;
    }



    //通用的查操作操作,返回数据表中的一条记录(考虑事务)
    public <T> T getInstance(Connection conn,Class<T> clazz,String sql, Object ...args){

        PreparedStatement ps = null;
        ResultSet rs = null;

        try{

            //1.获取数据库连接
            conn = JDBCUtils.getConnection();

            //2.预编译sql语句(有参数传递),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //4.执行,并返回结果集
            rs = ps.executeQuery();

            //5.处理结果集

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();

            //这里演示获取一条数据,使用if
            if (rs.next()){

                T t = clazz.newInstance();

                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i+1);

                    //获取每个列的列名的别名:通过ResultSetMetaData的getColumnLabel()方法
                    String columnLabel = rsmd.getColumnLabel(i+1);

                    //通过反射:为clazz对象的columnLabel属性 赋值为 columnValue (即clazz类中的set方法)
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }

                return t;
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        //结果集中什么都没有,返回null
        return null;
    }



    //通用的查操作操作,返回数据表中的多条记录(考虑事务)
    public <T> List<T> getForList(Connection conn,Class<T> clazz, String sql, Object ...args){

        PreparedStatement ps = null;
        ResultSet rs = null;

        try{

            //1.获取数据库连接
            conn = JDBCUtils.getConnection();

            //2.预编译sql语句(有参数传递),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //4.执行,并返回结果集
            rs = ps.executeQuery();

            //5.处理结果集

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();

            //创建集合对象
            List<T> list = new ArrayList<T>();

            while (rs.next()){

                T t = clazz.newInstance();

                //给t对象指定的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i+1);

                    //获取每个列的列名的别名:通过ResultSetMetaData的getColumnLabel()方法
                    String columnLabel = rsmd.getColumnLabel(i+1);

                    //通过反射:为t对象的columnLabel属性 赋值为 columnValue (即T类中的set方法)
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }

                list.add(t);
            }

            //这里return list;写在while外面
            return list;

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        //结果集中什么都没有,返回null
        return null;
    }


    //用于查询特殊值的通用方法(考虑事务),比如:select count(*) from user_table; 只有一列
    public <E> E getValue(Connection conn,String sql,Object ...args){
        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();

            //处理结果集
            if (rs.next()){
                return (E) rs.getObject(1);
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        return null;
    }
}

针对不同的表,写不同的具体的DAO

具体的DAO按说写一个类继承BaseDAO就好了
但是为了让结构更加规范,写一个接口规范,然后写一个类实现该接口并继承BaseDAO

【CustomerDAO.java】

package com.atguigu6.dao;

import com.atguigu6.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

//此接口用于规范针对于customers表的常用操作
public interface CustomerDAO {
    //定义一些抽象方法

    //增:将customer对象添加到数据表中
    void insert(Connection conn, Customer customer);

    //删:针对指定的id,删除表中的一条记录
    void deleteById(Connection conn,int id);

    //改:针对内存中的customer对象,去修改数据表中指定的记录
    //修改指定的customer对象在表中的属性
    void update(Connection conn,Customer customer);

    //查(返回一条记录):针对指定的id查询对应的Customer对象
    Customer getCustomerById(Connection conn,int id);

    //查(返回多条记录):查询表中所有记录构成的集合
    List<Customer> getAll(Connection conn);

    //返回数据表中数据的条目数
    Long getCount(Connection conn);

    //返回数据表中最大的生日
    Date getMaxBirth(Connection conn);
}

package com.atguigu6.bean;

import java.util.Date;

/*
ORM编程思想(object relational mapping)
一个表对应一个java类
表中的一条数据对应java类的一个对象
表中的一个字段对应java类的一个属性
 */
public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;

    public Customer() {
    }

    public Customer(int id, String name, String email, Date birth) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.birth = birth;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", birth=" + birth +
                '}';
    }
}

【CustomerDAOImpl.java】

package com.atguigu6.dao;

import com.atguigu6.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

//对于接口CustomerDAO的实现类
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO{

    @Override
    //该方法可以返回值,不一定是void类型,看情况需要,update方法有返回值。
    public void insert(Connection conn, Customer customer) {
        String sql = "insert into customers(name,email,birth) values(?,?,?)";
        update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth());
    }

    @Override
    public void deleteById(Connection conn, int id) {
        String sql = "delete from customers where id = ?";
        update(conn,sql,id);
    }

    @Override
    public void update(Connection conn, Customer customer) {
        String sql = "update customers set name = ?,email = ?,birth = ? where id = ?";
        update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth(),customer.getId());
    }

    @Override
    public Customer getCustomerById(Connection conn, int id) {
        String sql = "select id,name,email,birth from customers where id = ?";
        Customer customer = getInstance(conn,Customer.class,sql,id);
        return customer;
    }

    @Override
    public List<Customer> getAll(Connection conn) {
        String sql = "select id,name,email,birth from customers";
        List<Customer> list = getForList(conn, Customer.class, sql);
        return list;
    }

    @Override
    public Long getCount(Connection conn) {
        String sql = "select count(*) from customers";
        return getValue(conn,sql);
    }

    @Override
    public Date getMaxBirth(Connection conn) {
        String sql = "select max(birth) from customers";
        return getValue(conn,sql);
    }
}

【CustomerDAOImplTest.java】

写一个测试类,测试CustomerDAOImpl
在CustomerDAOImpl代码中 右键单击  选择 GO to --->  Test 即可快速生成测试类。
package com.atguigu6.dao;

import com.atguigu3.util.JDBCUtils;
import com.atguigu6.bean.Customer;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

//写一个测试类,测试CustomerDAOImpl
class CustomerDAOImplTest {

    CustomerDAOImpl dao = new CustomerDAOImpl();

    @Test
    void insert() {
        Connection conn = null;
        try {

            conn = JDBCUtils.getConnection();
            //这里的new Customer(id,,,); id写不写都行,数据库里会自动编号。
            Customer customer = new Customer(1,"李四","li@126.com", new Date(3298749136L));

            //测试:向表中添加一个customer
            dao.insert(conn,customer);
            System.out.println("添加成功");

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void deleteById() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //测试:删除表中13号customer
            dao.deleteById(conn,13);

            System.out.println("删除成功");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void update() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //这个id要去表中找,要修改哪个customer就找他的id
            Customer customer = new Customer(18,"贝多芬","bei@126.com",new Date(3298749136L));

            //测试:修改表中18号customer的属性
            dao.update(conn,customer);

            System.out.println("修改成功");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void getCustomerById() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //测试:查看表中id为19号的customer
            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void getAll() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //测试:查看表中所有的数据
            List<Customer> list = dao.getAll(conn);
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void getCount() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //测试:表中的数据条数
            Long count = dao.getCount(conn);
            System.out.println("表中的数据条数为:" + count);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }

    @Test
    void getMaxBirth() {
        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            //测试:获取最大生日
            Date maxBirth = dao.getMaxBirth(conn);
            System.out.println("最大生日为:" + maxBirth);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCUtils.closeResource(conn,null);
        }
    }
}

对于上述写法的优化

上面的方法也可以用,也可以不优化。

优化:
已经知道是对于customers表的操作,那么方法的参数中就不应该传Customer.class,把这个参数去掉。
即把Class clazz 去掉,通过泛型。
package com.atguigu6.daoyouhua;

import com.atguigu3.util.JDBCUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

//封装了针对数据表的通用的操作(考虑事务):通用的增删改操作,通用的查操作
//BaseDAO 基础DAO,父类DAO
//设置为抽象类,无法实例化,虽然里面没有抽样方法
public abstract class BaseDAO<T> {

    private Class<T> clazz = null;

    //代码块
    {
        /*
        删掉了Class clazz参数,就要想办法获得。
        假如实现类CustomerDAOImpl继承BaseDAO,那么父类的泛型就是Customer类
        实现类CustomerDAOImpl是子类,BaseDAO是父类
        子类继承了父类,子类要获得父类的泛型,即获得clazz = Customer
        如何获得?
         */

        //获得当前this对象的带有泛型参数的直接父类和该父类的类型(this虽然写在BaseDAO里,但当new了一个子类时,this代表子类对象)
        Type genericSuperclass =  this.getClass().getGenericSuperclass();

        //获得父类所有的泛型参数,将其整体作为一个对象
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;

        //获取父类的泛型参数,放在一个数组里(泛型参数可能有多个)
        Type[] typeArguments = parameterizedType.getActualTypeArguments();

        //泛型的第一个参数,即Customer
        clazz = (Class<T>) typeArguments[0];
    }

    /*
    以上也可以写在构造方法里:
    public BaseDAO(){

    }
     */


    //通用的增删改操作(考虑事务)(也应该可以用于表结构的创建删除和修改)
    //该方法可以返回void,
    //也可以返回int。如果想要看所作操作影响了几条数据,可以使用return ps.executeUpdate(); 然后方法返回int。
    public int update(Connection conn, String sql, Object ...agrs){

        PreparedStatement ps = null;

        try{

            //1.预编译sql语句(由形参传入),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //2.填充占位符
            for (int i = 0; i < agrs.length; i++) {
                ps.setObject(i+1,agrs[i]);
            }

            //3.执行
            return ps.executeUpdate();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //4.释放资源
            JDBCUtils.closeResource(null,ps);
        }

        //这个别忘了
        return 0;
    }



    //通用的查操作操作,返回数据表中的一条记录(考虑事务)
    public T getInstance(Connection conn,String sql, Object ...args){

        PreparedStatement ps = null;
        ResultSet rs = null;

        try{

            //1.获取数据库连接
            conn = JDBCUtils.getConnection();

            //2.预编译sql语句(有参数传递),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //4.执行,并返回结果集
            rs = ps.executeQuery();

            //5.处理结果集

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();

            //这里演示获取一条数据,使用if
            if (rs.next()){

                T t = clazz.newInstance();

                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i+1);

                    //获取每个列的列名的别名:通过ResultSetMetaData的getColumnLabel()方法
                    String columnLabel = rsmd.getColumnLabel(i+1);

                    //通过反射:为clazz对象的columnLabel属性 赋值为 columnValue (即clazz类中的set方法)
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }

                return t;
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        //结果集中什么都没有,返回null
        return null;
    }



    //通用的查操作操作,返回数据表中的多条记录(考虑事务)
    public List<T> getForList(Connection conn,String sql, Object ...args){

        PreparedStatement ps = null;
        ResultSet rs = null;

        try{

            //1.获取数据库连接
            conn = JDBCUtils.getConnection();

            //2.预编译sql语句(有参数传递),返回PreparedStatement对象实例
            ps = conn.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }

            //4.执行,并返回结果集
            rs = ps.executeQuery();

            //5.处理结果集

            //获取结果集的元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();

            //创建集合对象
            List<T> list = new ArrayList<T>();

            while (rs.next()){

                T t = clazz.newInstance();

                //给t对象指定的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    //获取每个列的列值:通过ResultSet
                    Object columnValue = rs.getObject(i+1);

                    //获取每个列的列名的别名:通过ResultSetMetaData的getColumnLabel()方法
                    String columnLabel = rsmd.getColumnLabel(i+1);

                    //通过反射:为t对象的columnLabel属性 赋值为 columnValue (即T类中的set方法)
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }

                list.add(t);
            }

            //这里return list;写在while外面
            return list;

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        //结果集中什么都没有,返回null
        return null;
    }


    //用于查询特殊值的通用方法(考虑事务),比如:select count(*) from user_table; 只有一列
    //这是泛型方法,上面T是泛型类。
    public <E> E getValue(Connection conn,String sql,Object ...args){
        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();

            //处理结果集
            if (rs.next()){
                return (E) rs.getObject(1);
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCUtils.closeResource(null,ps,rs);
        }

        return null;
    }
}

package com.atguigu6.daoyouhua;

import com.atguigu6.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

//此接口用于规范针对于customers表的常用操作
public interface CustomerDAO {
    //定义一些抽象方法

    //增:将customer对象添加到数据表中
    void insert(Connection conn, Customer customer);

    //删:针对指定的id,删除表中的一条记录
    void deleteById(Connection conn,int id);

    //改:针对内存中的customer对象,去修改数据表中指定的记录
    //修改指定的customer对象在表中的属性
    void update(Connection conn,Customer customer);

    //查(返回一条记录):针对指定的id查询对应的Customer对象
    Customer getCustomerById(Connection conn,int id);

    //查(返回多条记录):查询表中所有记录构成的集合
    List<Customer> getAll(Connection conn);

    //返回数据表中数据的条目数
    Long getCount(Connection conn);

    //返回数据表中最大的生日
    Date getMaxBirth(Connection conn);
}

package com.atguigu6.daoyouhua;

import com.atguigu6.bean.Customer;

import java.sql.Connection;
import java.sql.Date;
import java.util.List;

//对于接口CustomerDAO的实现类
public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {

    @Override
    //该方法可以返回值,不一定是void类型,看情况需要,update方法有返回值。
    public void insert(Connection conn, Customer customer) {
        String sql = "insert into customers(name,email,birth) values(?,?,?)";
        update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth());
    }

    @Override
    public void deleteById(Connection conn, int id) {
        String sql = "delete from customers where id = ?";
        update(conn,sql,id);
    }

    @Override
    public void update(Connection conn, Customer customer) {
        String sql = "update customers set name = ?,email = ?,birth = ? where id = ?";
        update(conn,sql,customer.getName(),customer.getEmail(),customer.getBirth(),customer.getId());
    }

    @Override
    public Customer getCustomerById(Connection conn, int id) {
        String sql = "select id,name,email,birth from customers where id = ?";
        Customer customer = getInstance(conn,sql,id);
        return customer;
    }

    @Override
    public List<Customer> getAll(Connection conn) {
        String sql = "select id,name,email,birth from customers";
        List<Customer> list = getForList(conn,sql);
        return list;
    }

    @Override
    public Long getCount(Connection conn) {
        String sql = "select count(*) from customers";
        return getValue(conn,sql);
    }

    @Override
    public Date getMaxBirth(Connection conn) {
        String sql = "select max(birth) from customers";
        return getValue(conn,sql);
    }
}

JAVA反射-getGenericSuperclass()用法

第8章:数据库连接池

使用数据库连接池代替自己创建连接

8.1JDBC数据库连接池的必要性

JDBC学习笔记(2)---B站尚硅谷宋红康_第14张图片

8.2 数据库连接池技术

JDBC学习笔记(2)---B站尚硅谷宋红康_第15张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第16张图片

JDBC学习笔记(2)---B站尚硅谷宋红康_第17张图片

8.3 多种开源的数据库连接池

JDBC学习笔记(2)---B站尚硅谷宋红康_第18张图片
在这里插入图片描述

8.3.1 C3P0数据库连接池

package com.atguigu7.connection;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;

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

//C3P0数据库连接池的两种实现方式
public class C3P0Connection {
    //第一种实现方式:不推荐
    @Test
    //首先需要添加c3p0-0.9.5.5.jar包和mchange-commons-java-0.2.19.jar包
    public void testGetConnection() throws Exception {
        //该怎么使用?
        //c3p0-0.9.1.2/doc/index.html文件中有使用方法

        //获取C3P0数据库连接池
        ComboPooledDataSource cpds = new ComboPooledDataSource();//获取连接池对象
        cpds.setDriverClass("com.mysql.cj.jdbc.Driver");//设置数据库驱动
        cpds.setJdbcUrl("jdbc:mysql://localhost:3306/shangguigu_jdbc_test");//设置url
        cpds.setUser("root");//用户
        cpds.setPassword("abc123");//密码

        //通过设置相关操作,对数据据连接池进行管理:
        //比如:设置初始时数据库连接池中的连接数
        cpds.setInitialPoolSize(10);

        //还可以设置连接池中最大的连接数等

        //获取连接
        Connection conn = cpds.getConnection();
        System.out.println(conn);

        //销毁C3P0数据库连接池
        //DataSources.destroy(cpds);
    }


    //第二种方式:使用配置文件:c3p0-config.xml(在src下创建,文件名只能是这个)
    //推荐
    @Test
    public void testGetConnection1() throws SQLException {
        ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
        Connection conn = cpds.getConnection();
        System.out.println(conn);
    }
}

c3p0-config.xml:

<c3p0-config>
    
    
    <named-config name="helloc3p0">
        
        <property name="driverClass">com.mysql.cj.jdbc.Driverproperty>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/shangguigu_jdbc_testproperty>
        <property name="user">rootproperty>
        <property name="password">abc123property>

        
        
        <property name="acquireIncrement">5property>
        
        <property name="initialPoolSize">10property>
        
        <property name="minPoolSize">10property>
        
        <property name="maxPoolSize">100property>
        
        <property name="maxStatements">50property>
        
        <property name="maxStatementsPerConnection">2property>
    named-config>
c3p0-config>
测试:
package com.atguigu7.connection;

import com.atguigu6.bean.Customer;
import com.atguigu6.dao.CustomerDAOImpl;
import com.atguigu7.util.newJDBCUtils;
import org.junit.jupiter.api.Test;

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

public class Test {
    //将第二种方式封装起来newJDBCUtils,测试c3p0连接池获取连接
    @Test
    public void testGetCustomerById(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection1();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }
}

package com.atguigu7.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.*;

public class newJDBCUtils {
    //获取连接:C3P0
    //一个连接池,多个连接
    private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection1() throws SQLException {
        Connection conn = cpds.getConnection();
        return conn;
    }


    //关闭资源
    //参数本来应该写PreparedStatement,但是PreparedStatement是Statement是子接口,
    //所以写Statement范围更大点,既可以传PreparedStatement,也可以传Statement。
    public static void closeResource(Connection connection, Statement ps){
        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet){
        try {
            //关闭之前避免出现空指针
            if (resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

8.3.2 DBCP数据库连接池

package com.atguigu7.connection;

import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ResourceBundle;

//DBCP数据库连接池
public class DBCPConnection {
    //首先需要导入commons-dbcp2-2.9.0.jar和commons-pool2-2.11.1.jar包
    //出错的话:还要导入commons-logging-1.2.jar包
    /*
    如何写?commons-dbcp2-2.9.0/apidocs/index.html文件有介绍
     */

    @Test
    //方式一:不推荐
    public void testGetConnection() throws SQLException {
        //创建DBCP数据库连接池
        BasicDataSource source = new BasicDataSource();
        
        //设置基本信息
        source.setDriverClassName("com.mysql.cj.jdbc.Driver");
        source.setUrl("jdbc:mysql://localhost:3306/shangguigu_jdbc_test");
        source.setUsername("root");
        source.setPassword("abc123");
        
        //还可以设置其他涉及数据库连接池管理的相关属性
        source.setInitialSize(10);//设置初始化连接数
        source.setMaxTotal(10);//设置连接池可同时连接的最大的连接数

        Connection conn = source.getConnection();
        System.out.println(conn);
    }


    //方式二(推荐):使用配置文件dbcp.properties(src路径下)
    @Test
    public void testGetConnection1() throws Exception{
        Properties pros = new Properties();

        pros.load(new FileInputStream("src/dbcp.properties"));

        DataSource source = BasicDataSourceFactory.createDataSource(pros);

        Connection conn = source.getConnection();

        System.out.println(conn);
    }
}

dbcp.properties:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/shangguigu_jdbc_test
username=root
password=abc123

#其他配置也可以写
initialSize=10

JDBC学习笔记(2)---B站尚硅谷宋红康_第19张图片

因版本问题,上面的属性有些改变了。

测试:
package com.atguigu7.connection;

import com.atguigu6.bean.Customer;
import com.atguigu6.dao.CustomerDAOImpl;
import com.atguigu7.util.newJDBCUtils;

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

public class Test {
    //C3P0:将第二种方式封装到newJDBCUtils,测试c3p0连接池获取连接
    @org.junit.jupiter.api.Test
    public void testGetCustomerById(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection1();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

    //DBCP:将第二种方式封装到newJDBCUtils,测试DBCP连接池获取连接
    @org.junit.jupiter.api.Test
    public void testGetCustomerById1(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection2();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

}

package com.atguigu7.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;

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

public class newJDBCUtils {
    //获取连接:C3P0
    //一个连接池,多个连接
    private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection1() throws SQLException {
        Connection conn = cpds.getConnection();
        return conn;
    }


    //获取连接:DBCP
    private static DataSource source;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/dbcp.properties"));

            //创建一个DBCP数据库连接池(一个连接池)
            source = BasicDataSourceFactory.createDataSource(pros);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection2() throws Exception{
        Connection conn = source.getConnection();
        return conn;
    }


    //关闭资源
    //参数本来应该写PreparedStatement,但是PreparedStatement是Statement是子接口,
    //所以写Statement范围更大点,既可以传PreparedStatement,也可以传Statement。
    public static void closeResource(Connection connection, Statement ps){
        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet){
        try {
            //关闭之前避免出现空指针
            if (resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

8.3.3 Druid(德鲁伊)数据库连接池(主流用这个)

JDBC学习笔记(2)---B站尚硅谷宋红康_第20张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第21张图片

package com.atguigu7.connection;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.jupiter.api.Test;

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

//Druid数据库连接池
public class DruidConnection {
    //首先导入包druid-1.2.8.jar
    //怎么写?查看文档index.html

    @Test
    //采用配置文件方式(src下的druid.properties)
    public void testGetConnection() throws Exception{
        Properties pros = new Properties();
        pros.load(new FileInputStream("src/druid.properties"));

        DataSource source = DruidDataSourceFactory.createDataSource(pros);
        Connection conn = source.getConnection();

        System.out.println(conn);
    }
}

druid.properties:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/shangguigu_jdbc_test
username=root
password=abc123

#可以设置其他属性
initialSize=10
maxActive=10
测试:
package com.atguigu7.connection;

import com.atguigu6.bean.Customer;
import com.atguigu6.dao.CustomerDAOImpl;
import com.atguigu7.util.newJDBCUtils;

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

public class Test {
    //C3P0:将第二种方式封装到newJDBCUtils,测试c3p0连接池获取连接
    @org.junit.jupiter.api.Test
    public void testGetCustomerById(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection1();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

    //DBCP:将第二种方式封装到newJDBCUtils,测试DBCP连接池获取连接
    @org.junit.jupiter.api.Test
    public void testGetCustomerById1(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection2();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }


    //Druid:封装到newJDBCUtils,测试Druid连接池获取连接
    @org.junit.jupiter.api.Test
    public void testGetCustomerById2(){
        CustomerDAOImpl dao = new CustomerDAOImpl();
        Connection conn = null;
        try {
            conn = newJDBCUtils.getConnection3();

            Customer customer = dao.getCustomerById(conn,19);
            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

}
package com.atguigu7.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mysql.cj.util.EscapeTokenizer;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;

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

public class newJDBCUtils {
    //获取连接:C3P0连接池
    //一个连接池,多个连接
    private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection1() throws SQLException {
        Connection conn = cpds.getConnection();
        return conn;
    }


    //获取连接:DBCP连接池
    private static DataSource source;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/dbcp.properties"));

            //创建一个DBCP数据库连接池(一个连接池)
            source = BasicDataSourceFactory.createDataSource(pros);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection2() throws Exception{
        Connection conn = source.getConnection();
        return conn;
    }



    //获取连接:Druid连接池
    private static DataSource source1;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/druid.properties"));

            source1 = DruidDataSourceFactory.createDataSource(pros);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection3() throws Exception{
        Connection conn = source1.getConnection();
        return conn;
    }




    //关闭资源
    //参数本来应该写PreparedStatement,但是PreparedStatement是Statement是子接口,
    //所以写Statement范围更大点,既可以传PreparedStatement,也可以传Statement。
    public static void closeResource(Connection connection, Statement ps){
        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet){
        try {
            //关闭之前避免出现空指针
            if (resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

第9章:Apache-DBUtils实现CRUD操作

Apache下有一个DBUtils.jar包,可以实现增删改查操作
DBUtils代替了我们自己使用PreparedStatement写的通用的增删改查操作

9.1 Apache-DBUtils简介

JDBC学习笔记(2)---B站尚硅谷宋红康_第22张图片
JDBC学习笔记(2)---B站尚硅谷宋红康_第23张图片

9.2 主要API的使用

9.2.1 DbUtils

JDBC学习笔记(2)---B站尚硅谷宋红康_第24张图片

9.2.1 QueryRunner类

JDBC学习笔记(2)---B站尚硅谷宋红康_第25张图片

快捷键ctrl+alt+t:对代码块进行try..catch...
package com.atguigu8.dbutils;

//DBUtils:
//首先要导入commons-dbutils-1.7.jar包
//怎么写?查看commons-dbutils-1.7\apidocs\index.html文件

import com.atguigu6.bean.Customer;
import com.atguigu7.util.newJDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.jupiter.api.Test;

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

//dbutils是Apache组织提供的一个开源JDBC工具类库,封装了针对数据库的增删改查操作
public class QueryRunnerTest {
    /*
    QueryRunner是DBUtils提供一个类:
        该类有方法:
            查询:query()
            增删改:update()
     */

    @Test
    //插入一条数据
    public void testInsert() {
        //快捷键ctrl+alt+t:对代码块进行try..catch...
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "insert into customers(name,email,birth) values(?,?,?)";

            //update()方法有返回值,也可以不用,返回int,影响了几条数据
            //update()有很多重载的方法,根据具体情况选择
            int insertCount = runner.update(conn,sql,"王五","wang@126.com","2001-09-09");

            System.out.println("添加了" + insertCount + "条数据");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

    //删和改操作与增操作差不多,只不过sql语句不同



    @Test
    //查操作:返回一条数据
    /*
      query(Connection,sql,ResultSetHandler,...args);
      ResultSetHandler是结果集处理器,这是一个接口,要使用它的实现类。
      DBUtils提供了很多实现类,这里返回一个对象,使用:BeanHandler类
    */
    //使用BeanHandler返回一个对象
    public void testQuery1(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "select id,name,email,birth from customers where id = ?";

            BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);

            //查询id为23的customer
            Customer customer = runner.query(conn, sql, handler, 23);

            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }


    @Test
    //查操作:返回多条数据
    //使用BeanListHandler返回多个个对象
    public void testQuery2(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "select id,name,email,birth from customers where id < ?";

            BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);

            //查询id小于23的customer
            List<Customer> list = runner.query(conn, sql, handler, 23);

            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }

    /*
    其他的ResultSetHandler的实现类:
        ArrayHandler:返回一个数组对象
        ArrayListHandler:返回多个数组对象
        MapHandler:返回一个键值对
        MapListHandler:返回多个键值对
     */

    @Test
    //演示MapHandler
    public void testQuery3(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "select id,name,email,birth from customers where id = ?";

            MapHandler handler = new MapHandler();

            //查询id为23的customer
            Map<String, Object> map = runner.query(conn, sql, handler, 23);

            System.out.println(map);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }


    @Test
    //查询特殊值:select count(*) from customers
    //使用ScalarHandler
    public void testQuery4(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "select count(*) from customers";

            ScalarHandler handler = new ScalarHandler();

           Long count = (Long) runner.query(conn,sql,handler);

            System.out.println(count);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }


    @Test
    //如果DBUtils提供的实现类无法满足要求:自定义ResultSetHandler实现类
    public void testQuery5(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = newJDBCUtils.getConnection3();

            String sql = "select id,name,email,birth from customers where id = ?";

            //匿名实现类
            ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){

                @Override
                public Customer handle(ResultSet resultSet) throws SQLException {
                    if (resultSet.next()){
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        String email = resultSet.getString("email");
                        Date birth = resultSet.getDate("birth");
                        Customer customer = new Customer(id,name,email,birth);
                        return customer;
                    }
                    return null;
                }
            };

            Customer customer = runner.query(conn,sql,handler,23);

            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            newJDBCUtils.closeResource(conn,null);
        }
    }
}

JDBC学习笔记(2)---B站尚硅谷宋红康_第26张图片

JDBC学习笔记(2)---B站尚硅谷宋红康_第27张图片

9.2.3 ResultSetHandler接口及实现类

JDBC学习笔记(2)---B站尚硅谷宋红康_第28张图片

9.2.4 使用DBUtils关闭资源

package com.atguigu7.util;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;

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

public class newJDBCUtils {
    //获取连接:C3P0连接池
    //一个连接池,多个连接
    private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection1() throws SQLException {
        Connection conn = cpds.getConnection();
        return conn;
    }


    //获取连接:DBCP连接池
    private static DataSource source;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/dbcp.properties"));

            //创建一个DBCP数据库连接池(一个连接池)
            source = BasicDataSourceFactory.createDataSource(pros);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection2() throws Exception{
        Connection conn = source.getConnection();
        return conn;
    }



    //获取连接:Druid连接池
    private static DataSource source1;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/druid.properties"));

            source1 = DruidDataSourceFactory.createDataSource(pros);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection3() throws Exception{
        Connection conn = source1.getConnection();
        return conn;
    }




    //关闭资源
    //参数本来应该写PreparedStatement,但是PreparedStatement是Statement是子接口,
    //所以写Statement范围更大点,既可以传PreparedStatement,也可以传Statement。
    public static void closeResource(Connection connection, Statement ps){
        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void closeResource(Connection connection, PreparedStatement ps, ResultSet resultSet){
        try {
            //关闭之前避免出现空指针
            if (resultSet!=null){
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (ps!=null){
                ps.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            //关闭之前避免出现空指针
            if (connection!=null){
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    //使用DBUtils关闭资源
    public static void closeResource1(Connection conn, PreparedStatement ps, ResultSet rs){
        /*
        方法一:
        try {
            DbUtils.close(rs);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            DbUtils.close(ps);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            DbUtils.close(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        */

        //方法二:
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(ps);
        DbUtils.closeQuietly(conn);
    }

}

第10章:总结

JDBC代码写法总结(使用Druid数据库连接池获取连接,使用DBUtils进行增删改查和资源的关闭)

不考虑事务的写法(考虑事务看第6章):

Customer类(具体看情况是哪个类)

package com.lastcode.bean;

import java.util.Date;

/*
ORM编程思想(object relational mapping)
一个表对应一个java类
表中的一条数据对应java类的一个对象
表中的一个字段对应java类的一个属性
 */
public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;

    public Customer() {
    }

    public Customer(int id, String name, String email, Date birth) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.birth = birth;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", birth=" + birth +
                '}';
    }
}

需要导入的包:
JDBC学习笔记(2)---B站尚硅谷宋红康_第29张图片

因为要使用@Test 测试,还要导入:

JDBC学习笔记(2)---B站尚硅谷宋红康_第30张图片
druid.properties 配置文件

#该配置文件要写在src路径下
driverClassName=com.mysql.cj.jdbc.Driver
#shangguigu_jdbc_test 是自己创建的数据库
url=jdbc:mysql://localhost:3306/shangguigu_jdbc_test
username=root
password=abc123

#可以设置其他属性
initialSize=10
maxActive=10

JDBCUtils类

//封装获取连接和关闭资源操作
//通过Druid数据库连接池获取数据库连接
//通过DBUtils关闭资源
package com.lastcode;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;

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

//封装获取连接和关闭资源操作
//通过Druid数据库连接池获取数据库连接
//通过DBUtils关闭资源
public class JDBCUtils {
    //获取数据库连接
    private static DataSource source;
    static {
        try{
            Properties pros = new Properties();
            pros.load(new FileInputStream("src/druid.properties"));

            source = DruidDataSourceFactory.createDataSource(pros);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws Exception{
        Connection conn = source.getConnection();
        return conn;
    }

    //关闭资源
    public static void closeResource(Connection conn, PreparedStatement ps, ResultSet rs){
        /*
        方法一:
        try {
            DbUtils.close(rs);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            DbUtils.close(ps);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        try {
            DbUtils.close(conn);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        */

        //方法二:
        DbUtils.closeQuietly(rs);
        DbUtils.closeQuietly(ps);
        DbUtils.closeQuietly(conn);
    }
}

DBUtils类

package com.lastcode;

import com.atguigu6.bean.Customer;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

//使用DBUtils完成增删改查操作
/*
    1.QueryRunner是DBUtils提供一个类:
        该类有方法:
            查询:query()
            增删改:update()

    2.ResultSetHandler是结果集处理器,也是DBUtils提供一个类,这是一个接口,要使用它的实现类。
        query(Connection,sql,ResultSetHandler,...args);

      DBUtils提供了很多实现类:
        返回一个对象,使用:BeanHandler类
        使用BeanListHandler返回多个个对象
        ArrayHandler:返回一个数组对象
        ArrayListHandler:返回多个数组对象
        MapHandler:返回一个键值对
        MapListHandler:返回多个键值对

        查询特殊值:select count(*) from customers
        使用ScalarHandler
*/
public class DBUtilsTest {
    @Test
    //1.插入一条数据
    public void testInsert() {

        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = JDBCUtils.getConnection();

            String sql = "insert into customers(name,email,birth) values(?,?,?)";

            //update()方法有返回值,也可以不用,返回int,影响了几条数据
            //update()有很多重载的方法,根据具体情况选择
            int insertCount = runner.update(conn,sql,"王五","wang@126.com","2001-09-09");

            System.out.println("添加了" + insertCount + "条数据");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null,null);
        }
    }


    //2.删和改操作与增操作差不多,只不过sql语句不同


    @Test
    //3.查操作:返回一条数据
    //使用BeanHandler返回一个对象
    public void testQuery1(){

        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = JDBCUtils.getConnection();

            String sql = "select id,name,email,birth from customers where id = ?";

            BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);

            //查询id为23的customer
            Customer customer = runner.query(conn, sql, handler, 23);

            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null,null);
        }
    }


    @Test
    //4.查操作:返回多条数据
    //使用BeanListHandler返回多个个对象
    public void testQuery2(){

        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = JDBCUtils.getConnection();

            String sql = "select id,name,email,birth from customers where id < ?";

            BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);

            //查询id小于23的customer
            List<Customer> list = runner.query(conn, sql, handler, 23);

            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null,null);
        }
    }


    @Test
    //5.查询特殊值:select count(*) from customers
    //使用ScalarHandler
    public void testQuery3(){

        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = JDBCUtils.getConnection();

            String sql = "select count(*) from customers";

            ScalarHandler handler = new ScalarHandler();

            Long count = (Long) runner.query(conn,sql,handler);

            System.out.println(count);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null,null);
        }
    }


    @Test
    //6.如果DBUtils提供的实现类无法满足要求:自定义ResultSetHandler实现类
    public void testQuery5(){
        Connection conn = null;

        try {
            QueryRunner runner = new QueryRunner();

            //通过druid获取连接
            conn = JDBCUtils.getConnection();

            String sql = "select id,name,email,birth from customers where id = ?";

            //匿名实现类
            ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){

                @Override
                public Customer handle(ResultSet resultSet) throws SQLException {
                    if (resultSet.next()){
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        String email = resultSet.getString("email");
                        Date birth = resultSet.getDate("birth");
                        Customer customer = new Customer(id,name,email,birth);
                        return customer;
                    }
                    return null;
                }
            };

            Customer customer = runner.query(conn,sql,handler,23);

            System.out.println(customer);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,null,null);
        }
    }
}

你可能感兴趣的