【JDBC 系统详细学习】

1. JDBC技术概述

1.1. 概念和理解

  • Java Database Connectivity – java连接数据库技术 是java程序连接数据库的技术统称

    • 在java代码中,使用JDBC提供的方法,可以发送字符串类型的sql语句数据库管理软件,并获取语句执行结果,进而实现数据库数据CURD操作
      - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ue1IF7CB-1676290178592)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5h1RSTCVDNin3zZPmEYGRWzUHS3qzOcfSvOQU2aOihQs*eIlcmy5MWKFPhj2odYyyLQIlWNk.JJOJE7NYuCFBxg!/mnull&bo=QgN4AQAAAAADBxo!&rf=photolist&t=5)]
  • 由**java语言的规范(接口)和各个数据库厂商的实现驱动(jar)**组成

    - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qANj3ytQ-1676290178593)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5h1RSTCVDNin3zZPmEYGRWy9IlmH0X54l.t4wemarhlUPGLC7UQenplHpdHYXS4iE6t*iRMxMxdNWJ1b4Hmm*Zo!/b&bo=LgPIAAAAAAADB8c!&rf=viewer_4)]

    - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lk7UJSkU-1676290178593)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5h1RSTCVDNin3zZPmEYGRWzsY1702vu96niHxYaKq13U6tDyl*46X.LMJhREdHYQhwG29UFu05bgANzTzQo2uNs!/mnull&bo=1gKCAQAAAAADB3U!&rf=photolist&t=5)]

  • 是一种典型的面向接口编程

  • 优势

    • 只需要学习jdbc接口规定方法,即可操作所有数据库软件
    • 项目中切换数据库只需要更新第三方jar包,不需更改代码

1.2. 核心概念和使用路线

1.2.1. 具体核心类和接口

  • DriverManager
    • 将第三方数据库厂商的实现驱动jar注册到程序中
    • 可以根据数据库连接信息获取 connection
  • Connection
    • 和数据库建立的连接,在连接对象上,可以多次执行数据库curd动作
    • 可以获取 statement 和 preparedStatement ,callableStatement 对象
  • Statement 和 PreparedStatement ,CallableStatement
    • 具体发送SQL语句到数据库管理软件对象
    • 不同方式发送稍有不同,PreparedStatement为重点
  • Result
    • 面向对象思维的产物(抽象成数据库的查询结果类)
    • 存储DQL查询数据库结果的对象
    • 需要我们进行解析,获取具体的数据库数据

1.2.2. 路线

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E1dbpCRD-1676290178594)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5hhSVlWhwdBiLQxoPOBSTZfYYoqoFSoKk2buRh9niDtHj6w8rwOq..VXWOmoWbx.1g0k*Hbnj0pd751KalJYjOg!/b&bo=UAkYAgAAAAADN1E!&rf=viewer_4)]

2. 核心API

2.1. 总体使用6步骤

  • 1.注册驱动
  • 2.创建连接
  • 3.创建发送SQL语句的对象
  • 4.发送SQL语句并获取结果集对象
  • 5.结果解析
  • 6.释放资源
  • 流程示意
    - [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyFFO9yj-1676290178594)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5hhSVlWhwdBiLQxoPOBSTZf4IA2LOpwLCHSh6S1RTo5TMz5FtD.F6QV.wGS88UC7KJqNTrti5O6RQrLYjlWtUzU!/b&bo=PAX8AgAAAAADN9U!&rf=viewer_4)]

2.2 基本演示

  • 初始化数据库和表信息

    • USE jdbc_learn;
      
      DROP TABLE IF EXISTS t_user;
      
      CREATE TABLE t_user (
      	id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'id',
      	account VARCHAR ( 20 ) NOT NULL UNIQUE COMMENT '账号',
      	`password` VARCHAR ( 64 ) NOT NULL COMMENT '密码',
      	nickname VARCHAR ( 20 ) NOT NULL COMMENT '昵称' 
      );
      INSERT INTO t_user ( account, `password`, nickname )
      VALUES
      	( 'root', '123456', '经理' ),(
      		'admin',
      		'666666',
      	'管理员' 
      	); 
      
  • 创建java工程并导入jar包

  • 建立连接并查询

    • 1、通过java.sql包下的 DriverManager类调用 registerDriver() 传入 java.sql.Driver接口 的实现类来 注册驱动

      • new Driver() 是mysql厂商 对java.sql.Driver类的实现类
        • 依赖:
          • 驱动版本 8+ import com.mysql.cj.jdbc.Driver;
          • 驱动版本 5+ import com.mysql.jdbc.Driver;
    • 2、通过 DriverManager.getConnection(String url, String username, String password) 建立连接获取连接对象

      • Connection connection = DriverManager.getConnection(
          "jdbc:mysql://127.0.0.1:3306/jdbc_learn",
          "root",
          "root_isymikasan0415"
        );
        
      • url: jdbc:数据库厂商名://ip地址:port/数据库名
        username: 数据库账号
        password: 数据库密码
        
    • 3、创建发送SQL语句的Statement对象

      • Statement statement = connection.createStatement();
        
    • 4、发送sql语句

      • ResultSet resultSet = statement.executeQuery("select * from t_user");
        
    • 5、解析结果集

    • 6、释放资源

      • 由内到外
        • resultSet.close()
        • statement.close();
        • connection.close();

2.3.DriverManager

DriverManager(驱动管理类)作用:

  • 注册驱动

    我们查询MySQL提供的Driver类,看它是如何实现的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9unhDJP-1676290178594)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5hhSVlWhwdBiLQxoPOBSTZetm8Jn.ZIWyHmK7OK.iYFgKsyId2HRFXAIgUN.tNEfeJNyQujSN89.j7EuCL03BfA!/b&bo=ZAWuAwAAAAADF*4!&rf=viewer_4)]

    在该类中的静态代码块中已经执行了 DriverManager 对象的 registerDriver() 方法进行驱动的注册了,此时如果再次调用会注册两个驱动

    • 静态代码块会在类加载的时候自动执行,因此只要触发类加载即可

      • 通过反射加载

        Class.forName("com.mysql.jdbc.Driver");
        
  • 创建连接对象 DriverManager.getConnection()

    • 三个参数

      • url :jdbc:数据库厂商名://ip地址:port/数据库名?key=value&key=value
      • username
      • Password
    • 两个参数

      • url: 同上
      • Properties info : 存储账号和密码
        • Properties 类似于 Map 以 key=value 形式 但都是字符串类型
          • Properties对象至少包含:
            • Key user
            • Key password
       Properties info = new Properties();
              info.put("user", "root");
              info.put("password", "root_isymikasan0415");
      
      Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jdbc_learn", info);
              
      
    • 一个参数

      • url:同上

2.4. Statement

发送SQL语句并返回结果集

根据SQL分类不同,调用不同的方法

  • sql分类
    • DDL:容器创建,修改,删除
    • DML:数据插入、修改、删除
    • DQL:查询
    • DCL:权限控制
    • TCL:事务控制
  • executeUpdate()
    • return : int
    • sql 语句为DDL、 DML时使用
  • executeQuery()
    • return: resultSet 结果集
    • sql 语句为 DQL时使用

2.5.ResultSet

要想进行数据解析,需要:

  • 1、移动游标指定获取数据行

    • resultSet内部包含游标默认指定第一行数据前
    • 通过调用next()方法向后移动
      • 返回 ture: 有更多行并且向下移动一行
      • 返回 false: 没有更多行数据
    • 通过 while(resultSet.next()){ 获取每一行数据 }
  • 2、获取指定数据行的列数据

    • resultSet.get类型(String columnLabel | int columnIndex);
      • columnLabel 根据列名获取数据 有别名写别名
      • columnIndex 根据下标获取,从左至右 从1开始

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3eP1Yok-1676290178595)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5tibkh9dB9awhECDd*nD8J1a9oO2fHCAUCENoC0GGIk4KA7PSZrtSj8ad*OWYFTvh7UAuTFmAKlroBc4BEACaQg!/mnull&bo=JgkYAgAAAAADBxc!&rf=photolist&t=5)]

  • 获取结果集列的对象

    • ResultSetMetaData metaData = resultSet.getMetaData
    • metaData.getColumnCount(): 获取列数量
    • metaData.getColumnLabel(int index) : 根据下角标获取列名(有别名获取别名,没有别名获取列名) 从1开始

2.6.PreparedStatement

预编译SQL语句并执行:预防SQL注入问题

2.6.1. Sql 注入:

用户名和密码拼接到sql语句中,拼接后的sql语句如下

select * from tb_user where username = 'admin' and password = ''or '1' = '1'

从上面语句可以看出条件 username = 'admin' and password = '' 不管是否满足,而 or 后面的 '1' = '1' 是始终满足的,最终条件是成立的,就可以正常的进行登陆了。

2.6.2. 执行

  • 1、编写SQL语句结构,动态值部分使用 ? 替代

    String sql = "select * from t_user where account= ? and password = ? ;"
    
  • 2、创建 PrepareStatement 并设置SQL结构

    PrepareStatement prepareStatement = connection.prepareStatement(sql);
    
  • 3、给占位符单独赋值

    • 参数1: index 占位符的位置 从左到右 从1开始
    • 参数2: object 占位符的值
    prepareStatement.setObject(1,account);
    prepareStatement.setObject(1,password);
    
  • 4、发送SQL语句,获取返回值

    • executeUpdate()
      • return : int
      • sql 语句为DDL、 DML时使用
    • executeQuery()
      • return: resultSet 结果集
      • sql 语句为 DQL时使用

3.扩展提升

3.1.主键回显

需求:插入数据后需要获取新增数据的主键用以做为关联表的外键

  • 创建preparedStatement时需传入第二个参数

    PreparedStatement preparedStatement = connection.prepareStatement(
    sql, Statement.RETURN_GENERATED_KEYS);
    
  • 获取主键值结果集对象

    ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
    

3.2.批量插入

优化原理:只建立一次连接

  • 逐条循环插入示例
String insertSql =
"INSERT INTO t_user(account,password,nickname) values(?,?,?)";

// 创建发送SQL语句的Statement对象
PreparedStatement statement = connection.prepareStatement(insertSql);

        for (int i = 0; i < 1000; i++) {
          // 给占位符赋值
            statement.setObject(1, "acount" + i);
            statement.setObject(2, "password" + i);
            statement.setObject(3, "nickname" + i);
						
          // 执行语句
            statement.executeUpdate();
            
        }
  • 批量插入示例

    String insertSql = "INSERT INTO t_user(account,password,nickname) values(?,?,?)";
    
            // 创建发送SQL语句的Statement对象
            PreparedStatement statement = connection.prepareStatement(insertSql);
    
            for (int i = 0; i < 1000; i++) {
    
    
                statement.setObject(1, "acount" + i);
                statement.setObject(2, "password" + i);
                statement.setObject(3, "nickname" + i);
    
                // 不执行,追加到values的后面
            		// 即:sql: values(?,?,?),(?,?,?),()...
                statement.addBatch();
            }
            
            // 执行批量操作
            statement.executeBatch();
    
    • 需在获取连接时传入 url 添加可选参数 ?rewriteBatchStatement=ture
  • 总结流程

    • 创建连接时开启批量插入
    • insert into 必须是 values 且语句不能以 ; 结束(追加需要)
    • 不是执行每句,而是批量添加 addBatch()
    • 追加完成后 执行 executeBatch()

3.3.事务

  • 同一事务中的多个 sql语句执行不会立即更新数据库而是存入内存

    • 全成功时才统一执行提交更新数据库
    • 有一个失败则回滚事务
  • 使用场景

    • 设计多条修改数据库的语句都必须开启事务
      • 转账(加钱-减钱)
      • 批量修改
      • 批量添加
  • 事务类型

    • 自动提交:mysql默认将每个语句单独开启事务,执行成功提交,失败回滚(不符合我们需求)
    • 手动提交:手动开启事务,添加语句,手动提交和回滚
  • Sql开启事务方式

    • 关闭当前连接的自动提交事务
      • SET autocommit = off;
      • Sql1
      • Sql2
      • Sql3
      • Commit /Rollback :手动提交或者回滚
    • 三个sql全在一个事务里
  • 一个事务的基本要求是在同一个连接上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sC4twDRQ-1676290178595)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5j4IfcjcPffJyiyIfQRrGk1aF6LSbcqOPo5IsGDQGPnwLJbFaX88XCoPc8WGY19GYC4MKhLVuMMvsdM1MacfIbY!/b&bo=XgSAAwAAAAADB*s!&rf=viewer_4)]

4.Druid连接池

4.1.连接性能消耗问题分析

  • 连接生命周期:创建 --> 使用 -->销毁
  • 连接的创建和销毁时间远大于执行时间

4.2.连接池作用

连接池原理就是将创建好的连接放入容器中,使用就获取,用完放入,节省创建和销毁的时间

4.3.连接池的选择

  • Javax.sql.DataSources
    • 规定了连接池需实现连接方法和回收方法
    • 因此选择的连接池不仅考虑性能外要看是否有其他扩展方法

4.4.Druid连接池的使用

需要导入jar包

4.4.1.硬编码(基本不用)

  • 创建一个连接池对象
  • 设置连接池参数
    • 必须
      • DriverClassName 注册驱动
      • url
      • user
      • password
    • 非必需
      • InitialSze 初始化连接数量
  • 获取连接
  • 回收连接

- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cz1F4fWW-1676290178595)(http://m.qpic.cn/psc?/V14G0v6C4SOTMx/ruAMsa53pVQWN7FLK88i5j4IfcjcPffJyiyIfQRrGk3Izrw*qPdojjaj2twBbX0xqk0UFCCWxZxjI.fSi*UcOn28F7iyGBy6X2Jzch6K12U!/b&bo=FgRiAgAAAAADN2A!&rf=viewer_4)]

4.4.2.软编码

  • 创建druid.properties文件

    # key 固定命名
    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql:///dbName?key=value&key=value
    username=root
    password=1234
    # 初始化连接数量
    initialSize=5
    # 最大连接数
    maxActive=10
    # 最大等待时间
    maxWait=3000
    
  • /**
     * 通过读取外部配置文件方法,实例化连接池对象
     */
    public class DruidDemo {
    
        public static void main(String[] args) throws Exception {
            
            //1. 加载配置文件
            Properties prop = new Properties();
          
          // src下的文件可以通过类加载器加载 
          // 当前类名.class.getClassLoader().getResourceAsStream("druid.properties")
          // 如果在src包下的别的包下按层级 即可 "xx/druid.properties"
        
    InputStream ips =      DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties")
           
    prop.load(ips)
      
      
            //2. 使用连接池的工具类的工厂模式创建连接池
    DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
          
          
            //3. 获取数据库连接 Connection
     Connection connection = dataSource.getConnection();
    
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mikasa_akm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值