首页 > 其他分享 >JDBC学习笔记

JDBC学习笔记

时间:2024-06-14 20:29:31浏览次数:37  
标签:ps 事务 JDBC String sql 笔记 学习 connection SQL

本质

由于每一个数据库的底层实现都是不同的,有不同的实现方法,使用起来会比较麻烦。于是就有了SUN公司开发的这一套jdbc的问世。

简而言之,jbdc(Java连接数据库)的本质就是接口,让程序员不用关心数据库的具体实现类,只需要针对该接口进行开发即可,接口存在的目的就是解耦合,提高程序的可拓展性。这里就是面向抽象的编程方法了。

六步入门

入门真就这六步(这里以MySQL数据库为例)

我们先从官网下载相应的驱动文件,笔者使用的是mysql-connector-j-8.4.0的版本  MySQL :: MySQL Community Downloadsicon-default.png?t=N7T8https://dev.mysql.com/downloads/

里面最核心的就是jar包中的.class文件

下载好后将其目录添加到系统环境变量classpath中

使用IDEA,在项目结构中添加JAR或目录,选择文件并将其导入即可

1、注册驱动

            Driver driver = new com.mysql.cj.jdbc.Driver();
            DriverManager.registerDriver(driver);

2、获取连接

            String url = "jdbc:mysql://127.0.0.1:3306/abc";
            String user = "username";
            String password = "123456";
            connection =  DriverManager.getConnection(url,user,password);

3、创建数据库操作对象

statement = connection.createStatement();

4、执行SQL

            String sql = "insert into test(name) values('root')";
            int count = statement.executeUpdate(sql);

statement.executeUpdate(sql)方法会返回一个int类型的数据,表示受该次SQL语句影响的行数,若返回为-1则表示SQL出错了。

5、处理对象结果集

            String sql = "select name from test";
            rs = statement.executeQuery(sql);
            //处理查询结果集
            boolean hasNext = rs.next();
            while (hasNext){
                //取出数据库中的内容
                System.out.println(rs.getString(1));
                hasNext = rs.next();
            }

6、释放资源

注意释放资源的代码要写在finally代码块中,以保证资源的释放。然后还是老规矩,由内到外向外释放,即先创建的后释放。

另外,为了避免释放过程出现异常导致其他资源释放不了的情况出现,将资源释放的代码分开try—catch。

finally {
            //释放资源,保证资源释放,老规矩,从里到外
            try{
                if (statement!=null){
                    statement.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            try {
                if (connection!=null){
                    connection.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

诶,这样写太麻烦了,有没有改进的办法?

查看到需要释放的Connection和Statement两个类都实现了AutoCloseable接口!!!

那我们就可以使用try—with—resource语句让系统自动释放了

修改后代码如下

public class ConnectionTest {
    public static void main(String[] args) throws Exception {
        String url = "jdbc:mysql://127.0.0.1:3306/abc";
        String user = "username";
        String password = "123456";
        try (
                Connection connection = DriverManager.getConnection(url, user, password);
                Statement statement = connection.createStatement();
        ) {
            //注册驱动
            Driver driver = new com.mysql.cj.jdbc.Driver();
            DriverManager.registerDriver(driver);
            //获取连接
            //url:统一资源定位符 (网络中某个资源的绝对路径)
            //url包括协议、ip、端口号、资源名
            //jdbc:mysql是协议,127.0.0.1是ip,3306是端口号,rental是资源名
            //什么是通信协议?是在通信前定好的数据传送格式。数据包具体怎么传数据,格式提前定好。
            System.out.println("数据库连接对象:" + connection);
            //获取数据库操作对象,创建该对象来发送SQL语句
            //执行SQL
            String sql = "insert into test(name) values('root')";
            int count = statement.executeUpdate(sql);
            System.out.println(count);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

SQL注入

原因:

1、用户输入的数据中含有符合SQL语法的语句,会导致原SQL语句的语义被歪曲

2、采用了字符串拼接的方式来构造SQL语句

3、拼接完SQL语句之后再一起编译发送

下面是会发生SQL注入现象的关于用户登录的例子:

        Scanner scanner = new Scanner(System.in);
        System.out.println("please input your username:");
        String name = scanner.nextLine();
        System.out.println("please input your password:");
        String pwd = scanner.nextLine();

        String sql = "select * from login_test where name='" + name + "'and pwd ='" + pwd + "'";
        try {
            if (DUtil.select_D(sql).next()){
                System.out.println("login!!!");
            }
            else System.out.println("error");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

上述的例子中可以看到,用户通过SQL注入成功破除了登录的验证

解决方法:

使用占位符,利用PreparedStatement来对SQL语句进行预编译,就算用户输入了能够扭曲SQL语义的数据也不影响编译过后的SQL语句的语义。

举例:

public class Login {
    public static void main(String[] args) throws ClassNotFoundException {
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String url = bundle.getString("url");
        String username = bundle.getString("username");
        String password = bundle.getString("password");
        Class.forName("com.mysql.cj.jdbc.Driver");
        PreparedStatement ps = null;
        ResultSet rs = null;
        try (
                Connection connection = DriverManager.getConnection(url, username, password);
        ) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("please input your username:");
            String name = scanner.nextLine();
            System.out.println("please input your password:");
            String pwd = scanner.nextLine();

            String sql = "select * from login_test where name= ? and pwd = ?";
            ps = connection.prepareStatement(sql);
            ps.setString(1,name);
            ps.setString(2,pwd);
            rs = ps.executeQuery();
            if (rs.next()){
                System.out.println("login!!!");
            }
            else System.out.println("error");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps!=null){
                try {
                    ps.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

修改代码后,SQL注入失效。最关键的一点就是SQL语句预编译了,没有给注入的机会。

分页查询

核心公式:   第pageNo页:limit(pageNo-1)*pageSize, pageSize

从第X条数据开始,往后显示Y条数据

实例代码:

String sql = "select name from test limit ?,?";
ps = conn.prepareStatement(sql);
ps.setInt(1,(pageNo-1)*pageSize);
ps.setInt(2,pageSize);
rs = ps.executeQuery();

模糊查询

核心:like……

直接看示例:

String sql = "select name from test where name like ?";
ps = conn.prepareStatement(sql);
ps.setString(1,Ename);

批处理

使用批处理可以降低处理时间,把指令打包到一起然后执行一次(磁盘IO一次),减少java程序与系统数据库的交互次数

如何启用?

在URL后面添加参数:rewriteBatchedStatements=true

使用示例如下:

    public static void main(String[] args) throws ClassNotFoundException {
        long start = System.currentTimeMillis();
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String url = bundle.getString("url");
        String username = bundle.getString("username");
        String password = bundle.getString("password");
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            int count = 0;
            connection = DriverManager.getConnection(url, username, password);
            String name = "kk";
            String pwd = "123456";
            String sql = "insert into login_test(name,pwd) values(?,?)";
            ps = connection.prepareStatement(sql);
            for (int i = 0; i < 3000; i++) {
                ps.setString(1, name);
                ps.setString(2, pwd);
                //将指令打包!!!
                ps.addBatch();
                if (i % 500 == 0) {
                    count += ps.executeBatch().length;
                }
            }
            count += ps.executeBatch().length;
            long end = System.currentTimeMillis();
            System.out.println("exe " + count + "orders used time:" + (end-start));

打包后可以执行3000条insert语句时间缩短至500毫秒左右

未打包为:

效果拔群!!!

事务隔离

JDBC事务默认是自动提交的,只要执行一条DML语句则自动提交一次。(很危险的喔)

若两条DML语句有关联,如果执行过程当中出了问题就很容易导致数据出错。

一般开发中,我们都是需要用多条DML语句来实现一个业务的。

所以要添加事务控制来保障业务安全。

三步走:1、开启事务 2、手动提交事务 3、手动回滚事务

1、开启事务,即将JDBC的自动提交事务关闭 setAutoCommit

            //开启事务,将JDBC的自动事务提交修改为false
            DUtil.connection.setAutoCommit(false);

2、手动提交事务,提交事务的时机在需要一同提交的事务之后  commit

            //事务一
            String sql1 = "update login_test set name = ? where id = 1";
            PreparedStatement ps1 = DUtil.connection.prepareStatement(sql1);
            ps1.setString(1,"abc");
            ps1.executeUpdate();

            //模拟异常发生
            String s = null;
            s.toString();

            //事务二
            String sql2 = "update login_test set name = ? where id = 1";
            PreparedStatement ps2 = DUtil.connection.prepareStatement(sql2);
            ps1.setString(1,"kevin");
            ps1.executeUpdate();

            //提交事务,事务结束
            DUtil.connection.commit();

3、手动回滚业务,这里用使用到异常处理机制,把回滚的代码写到catch代码块中。当其中有事务发生异常,执行回滚,结束事务。

catch (Exception e){
            //若有异常发生,回滚事务。然后事务结束
            try {
                DUtil.connection.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
            e.printStackTrace();

隔离级别

事务有四大特性: 原子性、一致性、隔离性、持久性

在jdbc中使用java代码设置事务的隔离级别:

            DUtil.connection.setTransactionIsolation(int类型的隔离级别);

隔离级别有:读未提交、读提交、可重复读、串行化

三大现象:脏读、不可重复读、幻读

隔离级别从低到高:读未提交<读提交<可重复读<串行化(不支持并发啦)

脏读

脏读指当一个事务正在修改某些数据时,另一个事务在未提交的情况下读取这些数据。这就可能导致读取到还未提交的脏数据。

不可重复读

两个事务读取同一数据,但第二个事务在第一个事务读取之后修改了该数据,导致第一个事务再次读取该数据时获取到的数据与第一次读取的数据不一致。

幻读

指一个正在执行的事务读取到了其他事务提交后新增的数据。

悲观锁和乐观锁

悲观锁是行级锁,将数据行锁住,事务必须排队执行,不允许并发。乐观锁是多线程并发,实现乐观锁的关键是加入版本号,各事务操作后要修改资源的版本号,如果读取到的版本号和执行后读到版本号一致即可提交,不一致则回滚。

for update开启行级锁

连接池

本质是一种缓存技术,池子嘛,拿来存数据的。所有连接池都实现了javax.sql.DataSource这个接口。

如果不创建连接池的话?

Connection是一个重量级的对象,建立两个进程之间的通信消耗的资源比较大,数据库操作大部分时间都是耗费在连接对象创建上。1、每次请求都创建对象,太慢了,且浪费资源 ;2、如果请求太多,不做连接数量上的限制的话数据库服务器会崩掉。

属性

1、初始化连接数 initialSize

2、最大连接数 maxActive

3、最小空闲连接数量 minIdle

4、最大空闲连接数量 maxIdle

5、最大等待时间 maxWait

6、连接有效性检查 testOnBorrow、testOnReturn

7、连接的driver、url、user、password

常用连接池

Druid、HikariCP

Druid是阿里巴巴开源的一个数据库连接池实现,基于JDBC规范,为数据库连接提供高性能、可伸缩性的解决方案。

优点

  1. 高性能:经过精心优化,Druid在性能上表现出色,支持连接池预热,减少首次请求的延迟。
  2. 监控和统计:Druid提供了丰富的监控和统计功能,如连接池的使用情况、SQL执行情况等,有助于识别性能问题和优化SQL查询。
  3. 防SQL注入:内置了防SQL注入的功能,增强了应用程序的安全性。
  4. 丰富的配置选项:支持通过配置文件或编程方式进行高度自定义,满足各种需求

HikariCP是一个高性能的JDBC连接池,相较于其他连接池,在速度、内存使用和特性方面都有显著的优势。

优点

  1. 高性能:通过减少连接创建和销毁的开销,以及优化连接的管理策略,HikariCP提供了卓越的数据库连接性能。
  2. 低内存消耗:采用轻量级的设计和内存管理策略,使HikariCP在内存使用方面非常高效。
  3. 易于集成:提供了简单易用的API和配置选项,便于集成到各种应用程序中。
  4. 即时初始化和快速验证:支持即时初始化连接池中的最小连接数,减少首次请求时的等待时间;使用快速连接测试(如发送PING命令)验证连接的有效性,降低验证成本。

感谢大家阅读

标签:ps,事务,JDBC,String,sql,笔记,学习,connection,SQL
From: https://blog.csdn.net/ddfhfjd/article/details/139583229

相关文章

  • 入门学习Python的十个建议
    引言不知不觉中,我接触python已经十年有余了。在这十年中,我见证了Python从一个相对小众的编程语言,成长为如今数据科学、人工智能和自动化领域的主力军。Python的简洁性、强大的库支持和广泛的社区使其成为了许多开发者的首选工具。在这篇文章中,我将分享一些学习Python的建议,希......
  • HTML的学习总结#4
            这篇是对今天的学习进行总结,今天的学习内容不是很多,但是含金量很大,所以我会认真的总结介绍,也希望大家跟我一起学习认识。文字阴影         主要是给文字添加阴影效果,使得文字更加着重和增强显示效果。<style>/*水平位置垂直位......
  • MySQL入门学习-聚合和分组.子查询.相关子查询
        在MySQL中,子查询是指在一个查询语句中嵌套另一个查询语句。子查询可以分为相关子查询和非相关子查询两种类型。    相关子查询是指子查询的执行结果依赖于外部查询中的值。在执行相关子查询时,MySQL会先执行外部查询,然后根据外部查询的结果来执行子查询......
  • Python遗传算法GA对长短期记忆LSTM深度学习模型超参数调优分析司机数据
    全文链接:https://tecdat.cn/?p=36004原文出处:拓端数据部落公众号随着大数据时代的来临,深度学习技术在各个领域中得到了广泛的应用。长短期记忆(LSTM)网络作为深度学习领域中的一种重要模型,因其对序列数据的强大处理能力,在自然语言处理、时间序列预测等领域中取得了显著的成果。然......
  • automa学习:写一个取某东图书数据的片断
    周五了,实在没事情了。正好上午有个朋友问automa的事,心想再写一个练习一下,毕竟,熟能生巧。目标某东图书:分析及介绍如下。1.新建标签页1.悬停元素。要注意县停.cate_menu_item:nth-child(14)>.cate_menu_lk:nth-child(1) 点击元素,即上面画圈的地方,该区域可以用元素......
  • 计网笔记-第四章:网络层
    第四章重点五类IP地址划分的表格(Ctrl+F查找wiki)例题1:子网划分(查找1111)例题2:子网划分与CIDR(查找2222)链路状态路由(全局路由):Dijkstra(查找Dijkstra)距离向量路由(分布式路由算法):DV(查找4.5.2)第四章复习大纲路由器的结构IP协议(重点)IP报文格式IPv4寻址ICMPDHCP......
  • 04《android studio开发实战(第三版)》第七到十章阅读笔记
    第七章:持久化存储本章介绍了SharedPreferences的使用方法,它是一种轻量级的存储方案,用于保存简单的键值对数据,如用户设置和配置。 学习了如何创建SharedPreferences对象,使用getSharedPreferences()方法读取和写入数据,以及如何使用apply()和commit()提交修改。了解了如何在Andro......
  • 6.13学习内容
    今天完成了计算机网络的实验三 综合性训练(搭建中小企业园区网)一、实验目的:1.通过对网络设备的连通和对拓扑的分析,加深对常见典型局域网拓扑的理解;2.通过路由建立起网络之间的连接,了解网络路由的设计与配置;3.进一步熟悉交换机、路由器的基本操作命令。二、实验设备:六个PC,两......
  • 6.7学习进度
    今天完成了计算机网络的实验一valan的创建和划分,还完成了部分结组作业的代码。实验一vlan的创建与划分一、实验目的:1.了解vlan的工作原理;2.学习基于端口划分vlan的方法;3.了解跨交换机的相同vlan之间的通信;4.进一步学习交换机端口的配置命令。二、实验原理:1.创建VLAN:首先需......
  • 6.10学习进度
    今天为端午节,完成了数据库的实验二实验二SQL语言的使用一、实验目的:掌握使用SQL语言进行各种查询的操作和视图的操纵方法。二、实验要求:在现有的数据库上进行各种查询操作,对视图的创建、使用等操作。三、实验步骤:1、开始→程序→MicrosoftSQLServer→SQLServerMan......