JDBC编程

JDBC编程

JDBC即java数据库互连,是java语言和数据库之间独立于数据库的连接标准API,JDBC从根本上来说是一种规范,具体的实现需要依赖于具体数据库生产商提供jar包【驱动】,提供了统一的接口用于访问不同的底层数据库,允许使用java语言编写不同的应用程序以访问数据库。

  • 为Java语言定义了一个SQL调用级别的统一界面

  • 为访问关系型数据库提供了一个标准的界面

  • 所谓的JDBC实际上就是一种用于执行SQL语句的API,可以采用一致的方式连接不同的额数据库系统,从而使用标准的SQL语言来存取数据库中的数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SXXFXyDJ-1657335309051)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220603171919325.png)]

具体的数据库访问采用的都是c/s架构

  • 支持多种不同的数据库服务器

  • 主要的业务逻辑集中在客户端中

  • 服务器端的逻辑侧重于数据库的操作

  • 客户端将频繁访问远程数据库,可能会导致网络流量增大问题

JDBC的体系结构

Java应用程序可以通过Java API与数据库连接,JavaAPI中的一组用Java语言编写的类和接口位于java.sql和javax.sql包中,实际动作则是由JDBC驱动管理器通过数据库生产厂商提供的JDBC驱动程序与数据库管理系统进行连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MsZ9TCcl-1657335309052)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220603172018306.png)]

每个JDBC应用程序至少需要有一个JDBC驱动程序,JDBC驱动是Driver接口的实现类

  • Driver接口是驱动程序需要实现的接口,Driver使DriverManager和JDBC应用可以独立于具体的数据库系统

  • 在不同类型的应用中添加驱动的方式不同

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gda27bIX-1657335309052)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220603172053084.png)]

JDBC API

  • java.sql.DriverManager负责加载、拆除驱动程序,负责获取和数据库管理系统的连接

  • java.sql.Connection实现对某个数据库系统的连接

一个数据库管理系统能够提供的连接数是有限的,必须保证连接及时关闭

  • java.sql.Statement用于实现向数据库系统提交SQL语句

java.sql.PreparedStatement用于执行预编译的SQL语句

java.sql.CallableStatement用于提交执行存储过程

  • java.sql.ResultSet是数据库系统返回的查询结果集

JDBC基本操作步骤

  • 加载驱动程序

  • 创建数据库连接,必须保证及时关闭

  • 提交执行SQL语句

  • 接收并处理SQL的执行结果

  • 关闭释放资源

1、加载驱动

首先将驱动jar包从数据库系统官方网站上下载下来,并将驱动jar包添加到构建路径中

Class.forName(“驱动串”)。这里的驱动串就是需要加载的JDBC驱动程序的名称

早期没有很多数据库提供驱动,所以java提供了通过微软的ODBC连接数据库的方法,这个驱动已经包含在java中,叫做jdbc-odbc桥接方式。例如访问micorosoft office中的access数据库的方法为

Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);

JDBC的驱动方式可以分为4种类型:JDBC-ODBC桥接方式、Native-API驱动、JDBC-Net驱动和Native-protocol驱动

//受检型异常 ClassNotFoundException 
Class.forName("com.mysql.cj.jdbc.Driver");

//从反射机制的角度上说new A()等价于Class.forName("com.yan.A").newInstance(),所以加 载操作也可以简化
new Driver(); 

如果使用JDBC高版本驱动时实际上还可以省略加载步骤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NZV6n0IX-1657335309053)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220603172433288.png)]

2、获取连接

可以通过驱动管理器DriverManager类获取和数据库系统的连接

Connection conn=DriverManager.getConnection("连接串","连接数据库的用户名称","对应的 口令")
  • 连接串用于代表需要连接的数据库,连接串的标准 jdbc主协议:子协议:其它部分 ,但是不同的数据库系统其对应的连接串不同。例如连接oracle则驱动串为 oracle.jdbc.OracleDriver ,连接串为 jdbc:oracle:thin:@localhost:1521:test
// 受检型异常 ClassNotFoundException 
Class.forName("com.mysql.cj.jdbc.Driver"); 
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test? serverTimezone=UTC", "root", "123456"); 
System.out.println(conn); //com.mysql.cj.jdbc.ConnectionImpl@365185bd

注意:数据库连接属于稀有资源,所以需要使用时进行创建,使用完毕必须结束关闭,不能依赖于垃圾回收。建议使用try/finally结构

3、构建语句对象

在与特定数据库建立连接后,就可以发送SQL语句,在发送SQL语句之前需要创建一个语句对象

最古老的方式是使用Statement对象

Statement stmt=conn.createStatement();
ResultSet res=stmt.executeQuery("select * from tb_student");

一般不建议使用Statement对象,这个对象用于提交静态sql语句。有sql注入漏洞,建议使用Statement的子接口PreparedStatement

  • Statement对象中提供了executeQuery方法来执行查询操作,这个方法会返回查询结果集ResultSet类对象,其中包含SQL语句的查询结果

  • Statement对象中提供了executeUpdate方法来执行修改操作,这个方法会返回一个int值,表示sql的增删改操作所影响的数据行数

需要查询tb_student表

//注意导入包时不要出现错误 
import java.sql.Connection;
import java.sql.DriverManager; 
import java.sql.ResultSet; 
import java.sql.Statement; 
public class Test1 {
    public static void main(String[] args) throws Exception { 
        // 受检型异常 
        ClassNotFoundException Class.forName("com.mysql.cj.jdbc.Driver"); 
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test? serverTimezone=UTC", "root","123456"); 
        Statement stmt = conn.createStatement(); 
        ResultSet rs = stmt.executeQuery("select * from tb_student"); 
    } 
}

4、接收查询结果集

JDBC中使用ResultSet对象来表示查询结果集,可以理解为一个指向满足查询结果的行指针,其中并不存放实际数据,所以在使用结果集之前不能关闭连接

Statement stmt = conn.createStatement(); 
ResultSet rs = stmt.executeQuery("select * from tb_student"); 

5、遍历处理结果集

注意:连接不能关闭

ResultSet对象包含了执行查询后满足条件的所有行,提供了对应的访问方法,有一组对应的getXxx方法获取指定列的值

while(rs.next()){ //用于移动行指针,同时判断是否有数据,如果有数据则返回为true,否则 false 
    /** | tb_student | CREATE TABLE `tb_student` ( 
    `id` bigint(20) NOT NULL AUTO_INCREMENT, 
    `name` varchar(32) NOT NULL,
    `age` int(11) DEFAULT '16', 
    `sex` tinyint(1) DEFAULT '1', 
    `dept` varchar(32) DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 | 
    */ //由于历史原因,列序号从1开始 
    long id=rs.getLong(1);//指定列时可以使用序号,也可以使用列名称,一般建议使用列名 称,因为数据表的结构会有变动 
    String name=rs.getString("name");
    int age=rs.getInt("age");
    boolean sex=rs.getBoolean("sex"); 
    System.out.println(id+":"+name+":"+age+":"+sex); 
}

一般列是什么数据类,则应该使用对应类型的getXxx方法,实际上使用getString可以获取任意类型的列中的数据

如果执行修改操作,则返回的是int类型数据,表示受影响行数

    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=UTC", "root", "123456");
    Statement stmt = conn.createStatement(); 
    int len=stmt.executeUpdate("update tb_student set dept='材料系' where id=8"); 
    if(len>0)
        System.out.println("修改成功!");
    else		
        System.out.println("修改失败!");

6、关闭对象

手动关闭ResultSet、Statement和Connection,注意这里必须保证Connection对象关闭

建议使用try/finally结构

正向打开对象,逆向关闭对象

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Statement;
public class Test3 { 
    public static void main(String[] args) {
        Connection conn = null; 
        Statement stmt = null;
        ResultSet rs = null;
       String sql="select * from tb_student"; 
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=UTC", "root", "123456"); 
            stmt = conn.createStatement(); rs=stmt.executeQuery(sql);
            while(rs.next()){
                long id=rs.getLong("id");
                String name=rs.getString("name"); 
                boolean sex=rs.getBoolean("sex"); 
                System.out.println(id+"\t"+name+"\t"+(sex?"男":"女")); 
            } 
        } catch (Exception e) {
            e.printStackTrace(); 
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException e2) { 
                e2.printStackTrace(); 
            }try {
                if (stmt != null)
                    stmt.close(); 
            } catch (SQLException e1) {
                e1.printStackTrace(); 
            }try {
                if (conn != null)
                    conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
}

JDBC主要编程接口对象

JDBC即java数据库互连,是java语言和数据库之间独立于数据库的连接标准API,JDBC从根本上来说是一种规范,具体的实现需要依赖于具体数据库生产商提供jar包【驱动】,提供了统一的接口用于访问不同的底层数据库,允许使用java语言编写不同的应用程序以访问数据库。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Statement;
public class Test3 { 
    public static void main(String[] args) {
        Connection conn = null; 
        Statement stmt = null;
        ResultSet rs = null;
       String sql="select * from tb_student"; 
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=UTC", "root", "123456"); 
            stmt = conn.createStatement(); rs=stmt.executeQuery(sql);
            while(rs.next()){
                long id=rs.getLong("id");
                String name=rs.getString("name"); 
                boolean sex=rs.getBoolean("sex"); 
                System.out.println(id+"\t"+name+"\t"+(sex?"男":"女")); 
            } 
        } catch (Exception e) {
            e.printStackTrace(); 
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (SQLException e2) { 
                e2.printStackTrace(); 
            }try {
                if (stmt != null)
                    stmt.close(); 
            } catch (SQLException e1) {
                e1.printStackTrace(); 
            }try {
                if (conn != null)
                    conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
}
Connection接口

Connection接口用于表示应用和数据库系统之间的静态连接,提供了针对事务处理的方法以及创建执行sql语句和存储过程的方法,同时提供了一些基本的错误处理方法

public interface Connection extends Wrapper, AutoCloseable
  • createStatement():Statement 创建用于执行sql语句的语句对象

  • prepareStatement(String sql):PreparedStatement 创建PreparedStatement对象,用于实现数据库的动态访问

  • prepareCall(String sql):CallableStatement 用于创建执行存储过程的CallableStatement对象

  • close():void结束Connection对象数据库连接,务必注意:数据库连接属于稀有资源,必须保证及时关闭

  • isClose():boolean测试是否已经关闭数据库连接

  • setAutoCommit(boolean):void设置事务是否自动提交

  • commit()在连接上提交事务

  • rollback()回滚撤销事务

Statement接口

Statement接口用于提交执行静态的SQL语句,并返回SQL的执行结果

public interface Statement extends Wrapper, AutoCloseable 
  • executeQuery(String查询语句):ResultSet 执行sql语句,并返回满足查询条件的结果集

  • executeUpdate(String增删改语句):int 执行增删改语句,并返回sql语句的影响行数

  • execute(String sql):boolean 执行任意的SQL语句,如果第一个执行结果是ResultSet则会返回true,如果返回整数则返回false

public class Test2 {
    public static void main(String[] args) throws Exception {
        new Driver(); 
        try (Connection conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=Asia/Shanghai", "root","123456"); 
             Statement stmt = conn.createStatement(); 
            ) { 
            String sql = "create table if not exists tb_users(id int)";
            boolean res = stmt.execute(sql); 
            System.out.println(res); 
        } 
    } 
}
  • close():void 关闭语句对象

  • addBatch(String sql)将多条sql语句放入到一个批处理中执行

  • executeBatch()向数据库发送一批sql语句,并执行

三种语句对象

在JDBC种提供了三种语句对象Statement、PreparedStatement和CallableStatement

  • Statement用于发送简单的静态SQL语句,不带参数。不支持预编译、有sql注入的风险。不允许使用
mysql> create table tb_users( 
    -> id bigint primary key auto_increment, 
    -> username varchar(20) not null, 
    -> password varchar(20) not null); 
Query OK, 0 rows affected (0.03 sec) 
    所谓的用户登录就是提交用户名称和口令到应用中,应用按照用户名username和口令password,到数 据库tb_users表中进行查询,如果能够查询到数据,则登录成功,否则登录失败 
    
    String username = "yanjun";
String password = "123456"; 
boolean success = false;
try ( Connection conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=Asia/Shanghai", "root", "123456");
     Statement stmt = conn.createStatement();
    ) { 
    // 通过字符串拼接生成对应的sql语句 
    String sql = "select * from tb_users where username='" + username + "' and password='" + password + "'"; 
    success = stmt.executeQuery(sql).next();
}
if (success) 
    System.out.println("登录成功!"); 
else
    System.out.println("登录失败!");

  • PreparedStatement是Statement的子接口,用于发送含有一个或者多个参数的sql语句。PreparedStatement比Statement执行效率高,因为它支持预编译功能,同时在一定程度上可以防止SQL注意,一般使用使用这个接口
public class Test4 {
    public static void main(String[] args) throws Exception {
        String username = "yanjun"; 
        String password = "123456";
        try (Connection conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=Asia/Shanghai", "root", "123456");
            ) {
            String sql="select * from tb_users where username=? and password=?";
            PreparedStatement ps=conn.prepareStatement(sql);
            //针对?参数ps对象提供了一组对应的setXxx方法,用于给参数赋值
            ps.setString(1, username);
            ps.setString(2, password);
            ResultSet rs=ps.executeQuery(); 
            while(rs.next()){ 
                String upwd=rs.getString("password"); 
                long id=rs.getLong("id"); 
                String uname=rs.getString("username") System.out.println(id+"\t"+uname+"\t"+upwd);
            } 
        }
    }
}
  • CallableStatement接口继承于PreparedStatement接口,主要用于调用执行存储过程一般在具体应用开发中不使用Statement,基本上都是使用PreparedStatement,主要原因就是PreparedStatement具有Statement的所有功能,同时支持预编译,还有避免SQL注入风险

Statement和PreparedStatement的区别

  • Statement只能处理静态SQL,PreparedStatement既能处理静态SQL,也能处理动态SQL,它继承于Statement接口

  • PreparedStatement支持预编译,所以适合执行连续多次相同结构的SQL语句,Statement不适合

SQL注入

客户端利用jdbc statement的缺点,传入非法参数,从而使jdbc返回不合法的值,通常称为sql注入

String username = "yanjun";
String password = "12345' or '1'='1";
boolean success = false; 
try (Connection conn = DriverManager.getConnection("jdbc:mysql:///test? serverTimezone=Asia/Shanghai", "root","123456"); 
     Statement stmt = conn.createStatement(); 
    ) { 
    // 通过字符串拼接生成对应的sql语句 
    String sql = "select * from tb_users where username='" + username + "' and password='" + password + "'";
    System.out.println(sql);
    success = stmt.executeQuery(sql).next(); 
} 
if (success)
    System.out.println("登录成功!"); 
else
    System.out.println("登录失败!");
ResultSet接口

ResultSet接口用于得到包含了执行SQL查询结果的结果集,要获取表中任何一个字段项都要先找到该字段项所处于的行,再获取对应的列

  • 获取数据要依靠一个指向当前行的指针,开始时指向满足条件的第一行之前,每次执行next方法,则指针后移,到达目标行后就可以使用getXxx方法获取对应列的值

  • boolean next()指针后移,如果有数据则返回true,否则false

  • getXxx(int索引序号/String列名称):Xxx 获取对应列值

时间类型处理

java.util.Date类型用于封装日期时间【具体实现实际上保存的是从1970-1-1 0:0:0到指定时刻的毫秒

值】;在java.sql中针对日期时间类型提供了3个java.util.Date类型的子类

  • java.sql.Date只有日期

  • java.sql.Time只有时间

  • java.sql.Timestamp具有日期时间

传入日期类型数据

String sql="select * from tb_users where birth=?"; //假设birth是日期类型
PreparedStatement ps = conn.prepareStatement(sql); 
//setDate方法要求参数必须是java.sql.Date类型,不能是java.util.Date类型 
java.sql.Date dd=new java.sql.Date(now.getTime());//now中实际包含年月日时分秒,dd 中只有年月日 
ps.setDate(1, dd); ResultSet rs = ps.executeQuery();

接收日期类型数据

while (rs.next()) { 
    long id = rs.getLong("id"); 
    java.util.Date birth=rs.getDate("birth");
    //rs.getDate获取的数据类型是 java.sql.Date,所以只有年月日,如果需要时分秒则使用getTime,如果需要年月日时分秒则使用 getTimestamp 
    System.out.println(id + "\t" + birth); 
}

tatement(sql);
//setDate方法要求参数必须是java.sql.Date类型,不能是java.util.Date类型
java.sql.Date dd=new java.sql.Date(now.getTime());//now中实际包含年月日时分秒,dd 中只有年月日
ps.setDate(1, dd); ResultSet rs = ps.executeQuery();


接收日期类型数据

```java
while (rs.next()) { 
    long id = rs.getLong("id"); 
    java.util.Date birth=rs.getDate("birth");
    //rs.getDate获取的数据类型是 java.sql.Date,所以只有年月日,如果需要时分秒则使用getTime,如果需要年月日时分秒则使用 getTimestamp 
    System.out.println(id + "\t" + birth); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值