第九篇 JDBC

本文详细介绍了JDBC的基础使用,包括初始化驱动、连接数据库、Statement与PreparedStatement的区别,以及CRUD操作、预编译语句、事务处理、ORM映射和数据库连接池。涵盖了参数设置、性能优化、SQL注入防护和数据库元数据获取等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDBC

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

初始化驱动

通过Class.forName("com.mysql.jdbc.Driver");
初始化驱动类com.mysql.jdbc.Driver

建立与数据库的连接

conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8", "root", "123456");

Statement

是用于执行SQL语句的,比如增加,删除

创建和获取statement对象

s = c.createStatement();

执行sql语句

String sql = "insert into hero values(null," + "'提莫'" + "," + 313.0f + "," + 50 + ")";
s.execute(sql);

关闭数据库

// 先关闭Statement
// 后关闭Connection

CRUD

是最常见的数据库操作,即增删改查

C 增加(Create)
R 读取查询(Retrieve)
U 更新(Update)
D 删除(Delete

executeQuery 执行SQL查询语句

            // 执行查询语句,并把结果集返回给ResultSet
            ResultSet rs = s.executeQuery(sql);
            while (rs.next()) {
                int id = rs.getInt("id");// 可以使用字段名
                String name = rs.getString(2);// 也可以使用字段的顺序
                float hp = rs.getFloat("hp");
                int damage = rs.getInt(4);
                System.out.printf("%d\t%s\t%f\t%d%n", id, name, hp, damage);
            }
            // 不一定要在这里关闭ReultSet,因为Statement关闭的时候,会自动关闭ResultSet
            // rs.close();
  1. 创建一个用户表,有字段name,password
  2. 插入一条数据 insert into user values(null,‘dashen’,‘thisispassword’);

获取表中共有几条数据 通过ResultSet获取出来

            String sql = "select count(*) from hero";
            ResultSet rs = s.executeQuery(sql);
            int total = 0;
            while (rs.next()) {
                total = rs.getInt(1);
            }

jdbc预编译statement

PreparedStatement
ResultSet 
这两个都是基1的

PreparedStatement
和 Statement一样,PreparedStatement也是用来执行sql语句的
与创建Statement不同的是,需要根据sql语句创建PreparedStatement


优点
参数设置
    Statement 需要进行字符串拼接,可读性和维护性比较差
    String sql = "insert into hero values(null,"+"'提莫'"+","+313.0f+","+50+")";
    PreparedStatement 使用参数设置,可读性好,不易犯错 
    String sql = "insert into hero values(null,?,?,?)";
性能优化
    PreparedStatement有预编译机制,性能比Statement更快
防止sql注入攻击
    假设name是用户提交来的数据
    String name = "'盖伦' OR 1=1";
    使用Statement就需要进行字符串拼接
    拼接出来的语句是: 
    select * from hero where name = '盖伦' OR 1=1
    因为有OR 1=1,这是恒成立的
    那么就会把所有的英雄都查出来,而不只是盖伦
    如果Hero表里的数据是海量的,比如几百万条,把这个表里的数据全部查出来
    会让数据库负载变高,CPU100%,内存消耗光,响应变得极其缓慢
    而PreparedStatement使用的是参数设置,就不会有这个问题

execute与executeUpdate的区别

相同点  execute与executeUpdate的相同点:都可以执行增加,删除,修改
不同点 
不同1:
    execute可以执行查询语句
    然后通过getResultSet,把结果集取出来
    executeUpdate不能执行查询语句
不同2:
    execute返回boolean类型,true表示执行的是查询语句,false表示执行的是insert,delete,update等等
    executeUpdate返回的是int,表示有多少条数据受到了影响

特殊操作

自增长  mysql语句中 AUTO_INCREMENT就表示自增长	 
	  PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 
      // 执行插入语句
      ps.execute();
      // 在执行完插入语句后,MySQL会为新插入的数据分配一个自增长id
	 // JDBC通过getGeneratedKeys获取该id
      ResultSet rs = ps.getGeneratedKeys();	
获取表的元数据
    DatabaseMetaData dbmd = c.getMetaData();
    // 获取数据库服务器产品名称
    System.out.println("数据库产品名称:\t"+dbmd.getDatabaseProductName());
    // 获取数据库服务器产品版本号
    System.out.println("数据库产品版本:\t"+dbmd.getDatabaseProductVersion());
    // 获取数据库服务器用作类别和表名之间的分隔符 如test.user
    System.out.println("数据库和表分隔符:\t"+dbmd.getCatalogSeparator());
    // 获取驱动版本
    System.out.println("驱动版本:\t"+dbmd.getDriverVersion());
    System.out.println("可用的数据库列表:");
    // 获取数据库名称
    ResultSet rs = dbmd.getCatalogs();
    while (rs.next()) {
    System.out.println("数据库名称:\t"+rs.getString(1));
    }

事务操作

c.setAutoCommit(false); 关闭自动提交

c.commit(); 手动提交

一类型的操作 要么都成功要么都失败
实现原理:关闭自动提交 再事务操作完成之后  进行手动提交
MYSQL 表的类型必须是INNODB才支持事务
修改表的类型为INNODB的SQL: 
alter table hero ENGINE  = innodb;

事务的4大属性
1、原子性,是指事务包含的所有操作要么全部成功,要么全部失败,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2、一致性,是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
3、隔离性,是指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
4、持久性,是指一个事务一旦被提交,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

在这里插入图片描述

jdbcorm

ORM=Object Relationship Database Mapping
对象和关系数据库的映射
一个对象对应数据库一条数据

DAO=DataAccess Object

数据访问对象  
写接口的地方  类似public void add(Hero hero);

数据库连接池

传统的连接方式
    当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。
    创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。
    同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。
    
数据库连接池原理
    如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建.
    使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。
    倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。
    整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。
    
ConnectionPool构造方法和初始化
	1. ConnectionPool() 构造方法约定了这个连接池一共有多少连接
	2. 在init() 初始化方法中,创建了size条连接。 注意,这里不能使用try-with-resource这种自动关闭连接的方式,因为连接恰恰需要保持不关闭状态,供后续循环使用
	3. getConnection, 判断是否为空,如果是空的就wait等待,否则就借用一条连接出去
	4. returnConnection, 在使用完毕后,归还这个连接到连接池,并且在归还完毕后,调用notifyAll,通知那些等待的线程,有新的连接可以借用了。

 ConnectionPool cp = new ConnectionPool(3);创建3条连接库的连接池

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值