JDBC入坑笔记



什么是JDBC

Java数据库连接 - Java DataBase Connectivity

JDBC可以让Java通过程序操作关系型数据库

JDBC基于驱动程序实现与数据库的连接与操作

image-20220516171753054

JDBC的优点

  • 统一的API,提供开发过程
  • 易于学习,容易上手,代码结构稳定
  • 功能强大,执行效率高,可处理海量数据

快速上手JDBC

image-20220516172442384

JDBC开发细节

获取JDBC驱动Jar

MySQL :: MySQL Connectors

创建数据库连接代码

image-20220516183104055
  • Calss.forName用于加载指定的JDBC驱动类,本质是通知JDBC注册这个驱动类,驱动由数据库厂商自行开发,连接字符串也不同
image-20220516183941981

数据库连接配置

DriverManager

DriverManager:用于注册/管理JDBC驱动程序

DriverManager.getConnection(连接字符串,用户名,密码)

返回值Connection对象,对应数据库的物理网络连接

Connection 对象

Connection对象用于JDBC与数据库的网络通信对象

java.sql.Connection是一个接口,具体由驱动厂商实现

所有数据库的操作都建立在Connection基础之上

image-20220516192835417

MySQL连接字符串

格式:jdbc.mysql://[主机ip] [:端口]/数据库名?参数列表

主机ip与端口是可选设置,默认值为127.0.0.1与3306

参数列表采用url编码,格式:参数1=值1&参数2=值2&…

MySQL连接字符串常用参数

image-20220516194149732

JDBC的查询操作及SQL注入漏洞

PreparedStatement:主要解决SQL注入问题

SQL注入攻击

SQL注入攻击是指利用SQL漏洞越权获取数据的黑客行为

SQL注入攻击根源是对原始SQL中的敏感字符做处理

解决方法:放弃Statement改用PreparedStatement处理SQL

PreparedStatement

PreparedStatement 预编译 Statement 是 Statement 的子接口

PreparedStatement 对SQL进行参数化,预防注入攻击

PreparedStatement 比 Statement 执行效率更高

image-20220519141326769
image-20220519141443786

错误使用方式

image-20220519145211537

JDBC工具类的抽取及增删改查

封装DbUtils工具类

 public class DbUtils {
     /**
      * 创建新的数据库连接
      * @return 新的Connection对象
      * @throws SQLException
      * @throws ClassNotFoundException
      */
     public static Connection getConnection() throws SQLException, ClassNotFoundException {
         //1. 加载并注册JDBC驱动
         Class.forName("com.mysql.cj.jdbc.Driver");
         //2. 创建数据库连接
         String url = "jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&severTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
         Connection conn = DriverManager.getConnection(url, "root", "123456");
         return conn;
     }

     /**
      * 关闭连接,释放资源
      * @param rs 结果集对象
      * @param stmt Statement对象
      * @param conn Connection对象
      */
     public static void closeConnection(ResultSet rs, Statement stmt, Connection conn) {
         //5. 关闭连接,释放资源
         try {
             if (rs != null) {
                 rs.close();
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }
         try {
             if (stmt != null) {
                 stmt.close();
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }
         try {
             if (conn != null && !conn.isClosed()) {
                 conn.close();
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }
     }
 }

JDBC实现新增数据

image-20220519162650384
String sql = "insert into employee(eno,ename,salary,dname) values(?,?,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, eno);
pstmt.setString(2, ename);
pstmt.setFloat(3, salary);
pstmt.setString(4, dname);
int cnt  = pstmt.executeUpdate(); //所有写操作都用 executeUpdate,且返回值为整型,代表本次执行所影响的记录数

实现JDBC更新与删除数据

image-20220519174159339
            String sql = "update employee set salary=? where eno=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setFloat(1, salary);
            pstmt.setInt(2, eno);
            int cnt = pstmt.executeUpdate();
            if (cnt == 1) {
                System.out.println("运功薪资以调成完成!");
            } else {
                System.out.println("没有找到编号:"+eno+",员工的数据。");
            }
image-20220519174312618
            conn = DbUtils.getConnection();
            String sql = "delete from employee where eno = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, eno);
            int cnt = pstmt.executeUpdate();
            if (cnt == 1) {
                System.out.println("员工数据以删除!");
            } else {
                System.out.println("没有找到编号:"+eno+",员工的数据。");
            }

JDBC中的事务管理

事务是以一种可靠的、一致的方式,访问和操作数据库的程序单元

事务依赖于数据库实现,MySQL通过事务区作为数据缓冲地带

image-20220519204422648
image-20220519204639376
image-20220519204823342
image-20220519205113038
image-20220519205159088

手动事务开发

public class TransactionSample {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = DbUtils.getConnection();
            //JDBC默认使用自动提交的模式
            conn.setAutoCommit(false);//关闭自动提交
            String sql = "insert into employee(eno,ename,salary,dname) values (?,?,?,?)";
            for (int i = 1000; i < 1020; i++) {
                if (i == 1005) {
                    // throw new RuntimeException("插入失败");
                }
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000f);
                pstmt.setString(4, "市场部");
                pstmt.executeUpdate();
            }
            conn.commit();//提交数据
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (conn != null && !conn.isClosed()) {
                    conn.rollback();//数据回滚
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } finally {
            DbUtils.closeConnection(null, pstmt, conn);
        }
    }
}

JDBC的日期处理及批量处理

Date日期对象的处理

        //String到Java.sql.Date分为两步
        //1.String转为Java.util.Date
        java.util.Date udHiredate = null;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            udHiredate = sdf.parse(strHiredate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //2.java.util.Date转为Java.sql.Date
        long time = udHiredate.getTime(); //获取从1970年到现在的毫秒数
        java.sql.Date sdHiredate = new java.sql.Date(time);

数据批处理

            long startTime = new Date().getTime();
            conn = DbUtils.getConnection();
            //JDBC默认使用自动提交的模式
            conn.setAutoCommit(false);//关闭自动提交
            String sql = "insert into employee(eno,ename,salary,dname) values (?,?,?,?)";
            for (int i = 100000; i < 200000; i++) {
                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000f);
                pstmt.setString(4, "市场部");
                pstmt.executeUpdate();
            }
            conn.commit();//提交数据
            long endTime = new Date().getTime();
            System.out.println("tc1执行时长:"+ (endTime-startTime));
            long startTime = new Date().getTime();
            conn = DbUtils.getConnection();
            //JDBC默认使用自动提交的模式
            conn.setAutoCommit(false);//关闭自动提交
            String sql = "insert into employee(eno,ename,salary,dname) values (?,?,?,?)";
            pstmt = conn.prepareStatement(sql);
            for (int i = 200000; i < 300000; i++) {
                pstmt.setInt(1, i);
                pstmt.setString(2, "员工" + i);
                pstmt.setFloat(3, 4000f);
                pstmt.setString(4, "市场部");
                pstmt.addBatch();//将参数加入批处理任务
//                pstmt.executeUpdate();
            }
            pstmt.executeBatch();//指向批处理任务
            conn.commit();//提交数据
            long endTime = new Date().getTime();
            System.out.println("tc2执行时长:"+ (endTime-startTime));

连接池与JDBC进阶使用

阿里巴巴Druid连接池

Druid是阿里巴巴开源连接池组件,是最好的连接池之一

Druid对数据库连接进行有效的管理与重用,最大化程序执行效率

image-20220521185441027

Druid连接池配置与使用

/**
 * Druid连接池配置与使用
 */
public class DruidSample {
    public static void main(String[] args) {
        //1.加载属性文件
        //创建Properties类,利用Properties对象保存druid-config.properties配置文件信息
        Properties properties = new Properties();
        //当前类.cslass.getReource("/文件名").getPath() 表示获取当前类路径下对应文件的路径,用String类型保存
        String propertyFile = DruidSample.class.getResource("/druid-config.properties").getPath();
        try {
            //将propertyFile转回到UTF-8
            //空格 -> %20 c:\java%20code\druid-config.properties
            //URLDecoder().decode()将%20还原到原来的空格 c:\java code\druid-config.properties
            propertyFile = new URLDecoder().decode(propertyFile, "UTF-8");
            //使用properties.load()方法,加载propertyFile路径文件
            properties.load(new FileInputStream(propertyFile));
        } catch (Exception e) {
            e.printStackTrace();
        }

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //2.获取DateSource数据源对象
            //利用DruidDataSourceFactory数据源工厂.createDataSource,将已载入的properties对象放入。
            //方法返回时的数据源DataSource 选用javax.sql下的
            DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
            //3.创建数据库连接
            conn = dataSource.getConnection();
            pstmt = conn.prepareStatement("select * from employee limit 0,10");
            rs = pstmt.executeQuery();
            while (rs.next()) {
                Integer enoId = rs.getInt(1);
                String ename = rs.getString("ename");
                Float salary = rs.getFloat("salary");
                String dname = rs.getString("dname");
                System.out.println("部门:" + dname + " 编号:" + enoId + " 姓名:" + ename + " 工资:" + salary);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeConnection(rs, pstmt, conn);
        }
    }
}

不使用连接池:conn.close()关闭连接,使用连接池:conn.close()将连接回收至连接池

这个需要配置druid-config.properties文件,如果在程序运行中达到了maxActive上线,其余的程序一直会进入等待,等待其他正在使用连接程序调用close方法将这个数据库连接进行回收。

#初始的尺寸
initialSize=10
#最多不得大于20
maxActive=20

Druid连接池的配置连接数量的初始值为什么要和最大值保持一致?

是因为在我们日常工作的时候,做为应用程序,最好在一开始就把所有的连接和资源都分配好,用户进来后直接分配现成的资源避免出现重新创建资源的情况,这样对整体的程序管理和性能都有帮助。

扩展知识:C3P0连接池

image-20220522152342482

在C3P0中强制配置文件名叫c3p0-config.xml文件,并且放在根路径上,在创建ComboPooledDataSource对象的时候会自动加载XML文件

并且根据XML文件,创建DataSource数据源对象

image-20220522152400030

Commons DBUtils使用入门

commons-dbutils 是 Apache 提供的开源JDBC工具类库

查找

image-20220522160808919

更新

image-20220522163603655
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片