java学习之JDBC

本文3-5节是8和9节的底层源码,8与9节是实际开发中会用到的框架

参考资料
JDBC尚硅谷,宋红康主讲

1.JDBC概述

  • JDBC是一个“独立与特定数据库管理系统,通用的SQL数据库存取和操作的公共接口(API)”定义了用来访问数据库的标准java类库,(java.sql,javax.sql)使用这些类库可以以一种标准的方法,方便地访问数据库
  • JDBC为访问不同的数据库提供了一种统一的途径,为开发者屏蔽了一些细节问题。
  • JDBC的目标是使java程序员可以连接任何提供了JDBC驱动程序的数据库系统,这样使得程序员无需对特定的数据库系统的特点有过多的了解,大大简化和加快了开发过程。

有了JDBC以后,我们访问数据库是这样的
在这里插入图片描述

2.JDBC编写的步骤

  1. 导入java.sql包
  2. 建立驱动
  3. 获取对数据库的连接(创建Connection对象)
  4. 创建Statement对象
  5. 执行sql语句获得ResultSet对象
  6. 关闭连接

3.获取数据库连接的五种方式

首先导入mysql的jar包
点击:File->Project Structure->Modules->Dependencies
在这里插入图片描述

方式一

import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionText {
    public static void main(String[] args) throws SQLException {
        //建立驱动
        Driver driver = new com.mysql.cj.jdbc.Driver();
        //jdbc:mysql:协议
        //localhost:ip地址
        //3306:mysql默认的端口号
        //donglijiedian:数据库名
        String url = "jdbc:mysql://localhost:3306/donglijiedian";
        //将用户名和密码封装在Properties中
        Properties info = new Properties();
        info.setProperty("user","root");
        info.setProperty("password","2767");
        //获取连接
        Connection conn=driver.connect(url,info);
    }
}

方式二

import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionText2 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException {
        //在实际编程中最好不要在程序中出现第三方接口以提高程序的可移植性
        //获取Driver实现类对象,使用反射
        Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver driver = (Driver)clazz.newInstance();
        String url = "jdbc:mysql://localhost:3306/donglijiedian";
        Properties info = new Properties();
        info.setProperty("user","root");
        info.setProperty("password","2767");
        Connection conn=driver.connect(url,info);
        System.out.println(conn);
    }
}

方式三

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;

public class ConnectionText3 {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
        Driver driver = (Driver)clazz.newInstance();
        //使用DriverManager替换Driver
        //注册驱动
        DriverManager.registerDriver(driver);
        //提供另外三个基本信息
        String url = "jdbc:mysql://localhost:3306/donglijiedian";
        String user="root";
        String password="2767";
        //获取连接
        Connection conn=DriverManager.getConnection(url,user,password);
    }
}

方式四

import java.sql.Connection;
import java.sql.DriverManager;

public class ConnectionText4 {
    public static void main(String[] args) throws Exception {
        //加载驱动,jar包中有静态代码块,会自动注册驱动,不需要我们自己写了
        Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
        //提供另外三个基本信息
        String url = "jdbc:mysql://localhost:3306/donglijiedian";
        String user="root";
        String password="2767";
        //获取连接
        Connection conn=DriverManager.getConnection(url,user,password);
    }
}

方式五最终版
将连接数据库的四个基本信息写到一个配置文件中,配置文件以.properties结尾
配置文件的数据:

user=root
password=2767
url=jdbc:mysql://localhost:3306/donglijiedian
driverClass=com.mysql.cj.jdbc.Driver

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class ConnectionText5 {
    //降低了程序的耦合度
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        InputStream is=ConnectionText5.class.getClassLoader().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 dricerClass=pros.getProperty("driverClass");

        //加载驱动
        Class.forName(dricerClass);
        //建立连接
        Connection conn= DriverManager.getConnection(url,user,password);
    }
}

4.Statement的弊端(面试)

对于java而言,我们一般使用PreparedStatement取代Statement

  1. 需要拼写sql语句,如在sql语句中出现变量需要’"++"’
  2. 有sql注入的问题,缺乏安全性(致命缺点),sql注入:即利用某些系统没有对用户输入的数据进行充分的检查,而且在用户输入数据中注入非法的sql语句段,从而利用系统的sql引擎完成而恶意行为的做法。

5.PreparedStatement的使用

PreparedStatement的优点:

1.通过占位符,减少了拼接操作
2.通过预编译可以解决sql注入问题
3.支持对Blob文件的操作
4.可以实现更高效的批量操作

5.1增删改

5.1.1封装获取连接和关闭连接的操作

由于每次对数据库的操作都要进行连接,所以我们新建建一个util包,把链接操作封装起来

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

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 dricerClass=pros.getProperty("driverClass");

        //加载驱动
        Class.forName(dricerClass);
        //建立连接
        Connection conn= DriverManager.getConnection(url,user,password);
        return conn;
    }

    //关闭资源的操作
    public static void closeResource(Connection conn, Statement ps){
        if (null!=ps){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (null!=null){
            try {conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    //方法的异步,后面查询操作会用到
    public static void closeResource(Connection conn, Statement ps, ResultSet rs){
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
package util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

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 dricerClass=pros.getProperty("driverClass");

        //加载驱动
        Class.forName(dricerClass);
        //建立连接
        Connection conn= DriverManager.getConnection(url,user,password);
        return conn;
    }
    
    //关闭资源的操作
    public static void closeResource(Connection conn, Statement ps){
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }

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

5.1.2通用的增删改模板

增删改操作仅仅是预编译的sql语句以及占位符的填充有不同,所以我们可以写一个通用的增删改操作的模板

//使用可变参数数组,实现增删改
    public static void alter(String sql,Object ...args){//sql中占位符的个数与可变形参的长度一致
        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);
        }
    }

5.2查询

在java中查询操作会返回一个结果集ResultSet,重点就是对ResultSet的操作

java与sql对应数据类型转换表

java类型sql类型
booleanBIT
byteTINYINT
shortSAMLLINT
intINTEGER
longBIGINT
StringCHAR,VARCHAR,LONGVARCHER
byte arrayBINARY,VAR BINARY
java.sql.DateDATE
java.sql.TimeTIME
java.sql.TimeStapTIMESTAMP

customer表
在这里插入图片描述

Customer类对应customer表

import java.util.Date;

public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birthday;

    public Customer() {
    }

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

    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 getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

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

5.2.1通用的查询操作模板(针对customer表)

为了使代码得到更好的复用,我们将对customer表的查询操作封装起来,写成一个通用的模板

//针对于customer表的通用操作
    public static Customer queryForCustomer(String sql,Object...args)  {
        Connection conn= null;
        PreparedStatement ps= null;
        ResultSet rs= null;
        try {
            conn = JDBCutils.getConnection();
            ps = conn.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            rs = ps.executeQuery();

            //获取结果集的列数
            ResultSetMetaData rsmd=rs.getMetaData();
            int colum=rsmd.getColumnCount();
            if(rs.next()){
                Customer cust=new Customer();
                //处理一行数据中的每一个里列
                for(int i=0;i<colum;i++){
                    //获取列值
                    Object value=rs.getObject(i+1);
                    //获取每个列的列名
                    String columValue=rsmd.getColumnLabel(i+1);
                    //给cust对象指定的某个属性赋值为value,通过反射
                    Field field=Customer.class.getDeclaredField(columValue);
                    field.setAccessible(true);
                    field.set(cust,value);
                }
                return cust;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(conn,ps,rs);
        }
        return null;
    }

5.2.2通用的查询操作模板(针对不同的表)

//针对不同的表的查询,返回表中的多条记录
    public static <T> List<T> getForList(Class<T> clazz,String sql,Object... args){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCutils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }

            rs = ps.executeQuery();
            //获取结果集
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columCount = rsmd.getColumnCount();
            //创建集合对象
            ArrayList<T> list=new ArrayList<T>();
            while (rs.next()){
                T t = clazz.getDeclaredConstructor().newInstance();
                for (int i=0;i<columCount;i++){
                    //获取列值,通过结果集
                    Object columValue=rs.getObject(i+1);
                    //获取列名
                    //String columName = rsmd.getColumnName(i+1);

                    //获取别名,若没起别名,则获取的是列名,所以推荐使用这个
                    String columName = rsmd.getColumnLabel(i+1);

                    //借助反射,将对象的columName的属性赋值为指定的值columValue
                    Field field=clazz.getDeclaredField(columName);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(conn,ps,rs);
        }
        return null;
    }

5.3操作BLOB类型的字段

5.3.1BLOB类型概述

在Mysql中,一共有四种类型的BLOB

BLOB是一个二进制的大型图像,是一个可以存储大量数据的容器
注:若大量地存储Blob对象,会导致效率下降

类型大小
TinyBlob最大255K
Blob最大65K
MediumBlob最大16M
LongBlob最大4G

注意:若存放的图片没有超过类型的大小限制也报错,则应该进入my.ini配置文件加上这个参数:max_allowed+packet=16M

5.3.2Blob类型的插入

import util.JDBCutils;

import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;

//往数据库中插入Blob类型的数据
public class BlobInsertTest {
    public static void main(String[] args)  {
        Connection conn= null;
        PreparedStatement ps = null;
        try {
            conn = JDBCutils.getConnection();
            String sql = "insert into customer(id,name,email,birthday,photo)values(?,?,?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setObject(1,2);
            ps.setObject(2,"zhangwei");
            ps.setObject(3,"zhangwei@qq.com");
            ps.setObject(4,"1992-09-09");

            //以流的形式传入一个文件
            FileInputStream is = new FileInputStream(new File("C:\\Users\\ryoxm\\Desktop\\pictures\\0.jpg"));
            ps.setBlob(5,is);

            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(conn,ps);
        }
    }
}

5.3.3Blob类型的读取

import bean.Customer;
import util.JDBCutils;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.*;

public class BlobQueryText {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCutils.getConnection();
            String sql="select * from customer where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setObject(1,2);

            rs = ps.executeQuery();
            if (rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                Date birth = rs.getDate("birthday");

                Customer customer = new Customer(id,name,email,birth);
                System.out.println(customer);

                //将Blob类型的字段以文件的方式下载下来保存在本地,以文件的方式
                Blob photo = rs.getBlob("photo");
                InputStream is = photo.getBinaryStream();
                FileOutputStream fos = new FileOutputStream("zhangwei.jpg");
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer))!=-1){
                    fos.write(buffer,0,len);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        JDBCutils.closeResource(conn,ps,rs);
    }
}

5.3.4批量插入数据

向goods表中插入20000个货物的id和name

import util.JDBCutils;
import java.sql.Connection;
import java.sql.PreparedStatement;

//使用preparedstatement实现批量
//update、delete本身就有批量操作的作用
//这个批量操作主要指的是批量插入
public class InsetDemo {
    public static void main(String[] args) {
        Connection conn= null;
        PreparedStatement ps = null;
        try {
            conn = JDBCutils.getConnection();
            String sql="insert into goods(id,name)values(?,?)";
            ps = conn.prepareStatement(sql);
            //这种方式的好处是只需要创建一个sql语句。
            for (int i=0;i<=20000;i++){
                ps.setObject(1,i);
                ps.setObject(2,"name_"+i);
                //攒sql
                ps.addBatch();
                if (i%500==0){
                    //执行
                    ps.executeBatch();
                    //清空batch
                    ps.clearBatch();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(conn,ps);
        }
    }

6.事物

6.1事物的引入

下面是user_table
在这里插入图片描述

现在假设AA要给BB转账100
我们必须要让两个事件同时发生,不然万一出现网络延迟等等,就可能会出现AA扣了钱,而BB也没到账的情况,所以我们引入了事物。

什么叫数据库事物

一组逻辑操作单元,一个或多个DML操作

事物处理的原则

事物中的所有操作要么同时成功,要么同时失败

注意:数据一旦提交,就不可回滚,回滚操作是回滚到上一次的提交点。
DML操作一旦执行会默认提交,所以我们要取消自动提交
并且在关闭连接后,也会自动提交。

6.2考虑事物之后的增删改操作

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

/*
* 针对于数据表user_table来说,
* AA用户给BB用户转账100*/
public class TransactionText {
    public static void main(String[] args) {
        Connection conn = null;
        try {
            conn=JDBCutils.getConnection();
            //取消自动提交
            conn = JDBCutils.getConnection();
            conn.setAutoCommit(false);
            String sql1 = "update user_table set balance = balance-100 where user = ?";
            alter(conn,sql1,"AA");
            String sql2 = "update user_table set balance = balance+100 where user = ?";
            alter(conn,sql2,"BB");

            System.out.println("转账成功");
            //提交数据
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚数据
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            JDBCutils.closeResource(conn,null);
        }
    }
    //考虑数据库事物之后的增删改操作2.0版本
    public static void alter(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 {
            //恢复默认的连接状态,若出现了异常该连接没有关闭,有可能会被再次调用,此时我们需要把其提交数据恢复成默认的true
            try {
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //关闭资源
            JDBCutils.closeResource(null,ps);
        }
    }
}

6.3java代码设置数据库的隔离级别

查看当前连接的隔离级别

conn.getTransactionIsolation();

设置当前连接的隔离级别

conn.setTransactionIsolation();

7.数据库操作类Dao

7.1提供操作数据表的BaseDao

封装了针对于数据表的通用操作,根据不同的表,提供不同的Dao

import util.JDBCutils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public abstract class BaseDao {
    //考虑上事物的通用的增删改操作
    public int alter(Connection conn, String sql, Object ...args){//sql中占位符的个数与可变形参的长度一致
        PreparedStatement ps= null;
        try {
            //预编译
            ps = conn.prepareStatement(sql);
            //填充占位符
            for (int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            //执行
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            JDBCutils.closeResource(null,ps);
        }
        return 0;
    }

    //针对于不同的表的查询操作,返回表中的一条记录,考虑上事物
    public static <T> T getInstance(Connection conn,Class<T> clazz,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();
            //获取结果集
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columCount = rsmd.getColumnCount();
            if (rs.next()){
                T t = clazz.getDeclaredConstructor().newInstance();
                for (int i=0;i<columCount;i++){
                    //获取列值,通过结果集
                    Object columValue=rs.getObject(i+1);
                    //获取列名
                    //String columName = rsmd.getColumnName(i+1);

                    //获取别名,若没起别名,则获取的是列名,所以推荐使用这个
                    String columName = rsmd.getColumnLabel(i+1);

                    //借助反射,将对象的columName的属性赋值为指定的值columValue
                    Field field=clazz.getDeclaredField(columName);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(null,ps,rs);
        }
        return null;
    }

    //考虑事物后的针对不同的表的查询,返回表中的多条记录
    public static <T> List<T> getForList(Connection conn,Class<T> clazz, 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();
            //获取结果集
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columCount = rsmd.getColumnCount();
            //创建集合对象
            ArrayList<T> list=new ArrayList<T>();
            while (rs.next()){
                T t = clazz.getDeclaredConstructor().newInstance();
                for (int i=0;i<columCount;i++){
                    //获取列值,通过结果集
                    Object columValue=rs.getObject(i+1);
                    //获取列名
                    //String columName = rsmd.getColumnName(i+1);

                    //获取别名,若没起别名,则获取的是列名,所以推荐使用这个
                    String columName = rsmd.getColumnLabel(i+1);

                    //借助反射,将对象的columName的属性赋值为指定的值columValue
                    Field field=clazz.getDeclaredField(columName);
                    field.setAccessible(true);
                    field.set(t,columValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(null,ps,rs);
        }
        return null;
    }

    //用于查询特殊值的通用方法
    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 (SQLException e) {
            e.printStackTrace();
        }finally {
            JDBCutils.closeResource(null,ps,rs);

        }
        return null;
    }
}

7.2CustomerDao及CustomerDaoimpl的实现

此接口用于规范针对于Customer表的常用操作

CustomerDao


import bean.Customer;

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

/*此接口用于规范针对于Customer表的常用操作*/
public interface CustomerDao {
    //将cust对象添加到数据库中
    void insert(Connection conn, Customer cust);
    //针对指定的id,删除表中的一天记录
    void deleteById(Connection conn,int id);
    //针对于内存中cust对象,修改表中的一条记录
    void update(Connection conn,Customer cust);
    //根据指定id,查询对应的customer
    Customer getCustomerById(Connection conn,int id);
    //查询表中所有的记录构成的集合
    List<Customer> getAll(Connection conn);
    //返回数据表中的数据的数目
    long getCount(Connection conn);
    //返回数据表中最大的生日
    Date getMaxDate(Connection conn);
}

CustomerDaoimpl

import bean.Customer;

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

public class CustomerDaoimpl extends BaseDao implements CustomerDao{

    @Override
    public void insert(Connection conn, Customer cust) {
        String sql="insert into customer(id,name,email,birthday)values(?,?,?,?)";
        alter(conn,sql,cust.getId(),cust.getName(),cust.getEmail(),cust.getDate());
    }

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

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

    @Override
    public Customer getCustomerById(Connection conn, int id) {
        String sql="select id,name,email,birthday from customer 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,birthday from customer";
        List<Customer> list=getForList(conn,Customer.class,sql);
        return list;
    }

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

    @Override
    public Date getMaxDate(Connection conn) {
        String sql="select max(birthday) from customer";
        return getValue(conn,sql);
    }
}

8.数据库连接池

基本思想

为数据库连接建立一个缓冲池,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需在缓冲区中取出一个,使用完放回即可,其负责分配管理和释放数据库连接,允许重复使用同一个连接,而不是重复建立连接。

8.1C3P0(了解)

所有的连接池都是需要首先导入jar包。

c3p0-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>

    <!-- This app is massive! -->
    <named-config name="helloc3p0">
        <!--提供获取连接的四个基本信息 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/study</property>
        <property name="user">root</property>
        <property name="password">2767</property>
        <!--进行数据库连接池管理的基本信息-->
        <!--当连接池中的连接数不够时,一次向数据库申请的连接数-->
        <property name="acquireIncrement">5</property>
        <!--初始化时的连接数-->
        <property name="initialPoolSize">10</property>
        <!-- 数据库连接池中维护的最少连接数-->
        <property name="minPoolSize">10</property>
        <!-- 数据库连接池中维护的最多连接数-->
        <property name="maxPoolSize">100</property>
        <!--数据库连接池最多维护的Statement的个数 -->
        <property name="maxStatements">50</property>
        <!--每个连接中建议最多使用的Statement的个数-->
        <property name="maxStatementsPerConnection">5</property>

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

java代码

import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class C3P0Text1 {
    public static void main(String[] args) throws SQLException {
        ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
        Connection conn = cpds.getConnection();
        System.out.println(conn);
    }
}

8.2DBCP(了解)

java代码

import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.SQLException;


public class DBCPText {
    //测试DBCP的数据库连接技术
    public static void main(String[] args) throws SQLException {
        //创建数据库连接池
        BasicDataSource source = new BasicDataSource();
        //设置基本信息
        source.setDriverClassName("com.mysql.cj.jdbc.Driver");
        source.setUrl("jdbc:mysql://localhost:3306/study");
        source.setUsername("root");
        source.setPassword("2767");
        //还可以设置其他设计数据库连接池管理的相关属性
        source.setInitialSize(10);
        source.setMaxActive(10);
        //.....
        Connection conn = source.getConnection();
        System.out.println(conn);
    }
}

类似于C3P0,我们也可以通过配置文件来进行连接,这里就不演示了,可以在DBCP的帮助文档中找到。

8.3Druid(重点)

Druid是开发中用的最多的连接池,需要掌握

通过加载druid.properties配置文件来建立连接

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

public class DruidText {

    public static void main(String[] args) throws Exception {
        Properties pros = new Properties();
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
        pros.load(is);
        DataSource source = DruidDataSourceFactory.createDataSource(pros);

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

9.Apache-DBUtils实现增删改查

数据库连接池代替了创建连接
这一节就可以替换上文写的增删改查操作
上文写的就类似于底层的源码

还是一样的,使用前要先导入相应的jar包

这是Apache组织提供的一个开源JDBC工具类库,他是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

9.1QueryRunner

import bean.Customer;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import util2.JDBCutils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class QueryRunnerText {
    public static void main(String[] args) {
        QueryRunner runner = null;
        Connection conn = null;
        try {
            runner = new QueryRunner();
            conn = JDBCutils.getConnection();

            //插入数据测试
           /* String sql="insert into customer (id,name,email,birthday)value (?,?,?,?)";
            int insertCount = runner.update(conn,sql,"2","蔡徐坤","caixukun@163.com","1997-09-08");
            System.out.println("添加了"+insertCount+"条数据");*/

            //查询一条记录
            String sql1="select id,name,email,birthday from customer where id = ?";
            //BeanHandler是ResultSetHandler的一个实现类,用于封装表中的一条记录
            BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
            Customer customer = runner.query(conn,sql1,handler,2);
            System.out.println(customer);

            //查询多条记录
            String sql2="select id,name,email,birthday from customer where id < ?";
            //BeanListHandler也是ResultSetHandler的一个实现类,用于封装表中的多条记录
            BeanListHandler<Customer> handler1 = new BeanListHandler<>(Customer.class);
            List<Customer> list = runner.query(conn,sql2,handler1,3);
            list.forEach(System.out::println);

            //查询表中的一些特殊值
            String sql3="select count(*) from customer";
            //ScalarHandler用于查询特殊值
            ScalarHandler handler2=new ScalarHandler();
            //返回结果为Object
            Long count = (Long) runner.query(conn,sql3,handler2);
            System.out.println(count);


        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //使用dbutils.jar中提供的Dbutils工具类,实现资源的关闭
            try {
                DbUtils.close(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值