数据库- JDBC

JDBC

基本概念

  • Java Database Connectivity:java访问数据库的解决方案
  • 希望用相同的方式访问不同的数据库,以实现与具体数据库无关的java操作界面
  • JDBC定义一套标准接口,即访问数据库的通用API,不同的数据库厂商根据各自数据库的特点去实现这些接口

JDBC 接口

  • 驱动管理器
    • DriverManager
  • 连接对象
    • Connection
    • DatabaseMetaData
  • SQL执行对象
    • Statement
    • PreparedStatement
    • CallableStatement
  • 查询结果集对象
    • ResultSet
    • ResultSetMetaData

JDBC 工作原理

  • JDBC定义接口
  • 数据库厂商实现接口
  • 程序员调用接口,实际调用的是底层数据库厂商的实现部分

JDBC API

JDBC工作过程

  1. 创建驱动,建立连接
  2. 创建语句执行对象
  3. 执行SQL语句
  4. 处理结果集
  5. 关闭连接

Driver 接口及驱动加载

  • 加载驱动类Driver
    • Driver是JDBC定义的接口,不同的数据库都提供了对应的实现类
    • 使用Class.forName(“驱动完全限定类”)方式加载驱动
    • 不同的数据库驱动类的完全限定名不同
  • 常见的数据库Driver实现类完全限定名
    • ORACLE:“oracle.jdbc.driver.OracleDriver”
    • MySQL:“com.mysql.cj.jdbc.Driver”
Class.forName("com.mysql.cj.jdbc.Driver");

Connection 接口

  • 使用驱动管理器与数据库建立连接
    • DriverManager是JDBC提供的类,用于与数据库建立连接
    • 建立连接时要指明数据库URL,用户名及密码
    • URL的格式:jdbc:subprotocol:subname
    • 不同的数据库URL格式不完全一致
Connection connection = DriverManager.getConnection(
        "jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true",
        "root", 		//用户名
        "root"			//密码
);        

Statemen 接口

  • Statement接口用于表示SQL语句执行对象
    • 通过Connection提供的方法createStatement()方法获取
    • boolean execute(String sql):可执行所有种类SQL,但通常用于执行DDL,如果执行的SQL语句返回了一个结果集则返回true,否则返回false
    • int executeUpdate(String sql):用于执行DML语句,返回值表示受 SQL 语句影响的行数
    • ResultSet executeQuery(String sql):用于执行DQL语句并获取查询结果集
  • 执行DDL语句

创建Userinfo表,包含id,username,password,nickname,age字段

Statement state = connection.createStatement();
String sql = "CREATE TABLE userinfo(\n" +
                "    id INT PRIMARY KEY AUTO_INCREMENT,\n" +
                "    username VARCHAR(30),\n" +
                "    password VARCHAR(30),\n" +
                "    nickname VARCHAR(30),\n" +
                "    age INT(3)\n" +
                ")";
state.execute(sql);
  • 执行DML语句

向Userinfo表中插入一条记录

Statement state = connection.createStatement();
String sql = "INSERT INTO userinfo(username,password,nickname,age)\n" +
                "VALUES('张三','123456','阿三',22)";
int num = state.executeUpdate(sql);
if(num>0){System.out.println("插入成功");}

修改Userinfo表中一条记录

Statement state = connection.createStatement();
String sql = "UPDATE userinfo " +
        "SET password='666666' " +
        "WHERE username='张三'";
int num = state.executeUpdate(sql);
System.out.println(num>0?"修改成功":"修改失败");

删除Userinfo表中一条记录

Statement state = connection.createStatement();
String sql = "DELETE FROM userinfo " +
                "WHERE id=1";
int num = state.executeUpdate(sql);
System.out.println(num>0?"删除成功":"删除失败");

ResultSet 接口

  • JDBC提供的接口ResultSet
  • ResultSet表示查询结果集,主要方法:
方法功能
boolean next()结果集向下移动一条记录,如果存在则返回true,否则返回false
String getString(int index)获取该记录指定下标对应的String类型字段值
String getString(String name)获取该记录指定字段名对应的String类型字段值
int getInt(int index)获取该记录指定下标对应的int类型字段值
int getInt(String name)获取该记录指定字段名对应的int类型字段值
如上格式还有其他数据类型字段值的getXXX(…)方法分别通过下标和字段名获取对应字段值的操作
  • 执行DQL语句
try(Connection connection = DBUtil.getConnection();){
	Statement statement = connection.createStatement();
	String sql = "SELECT id,name,salary,title "+
                 "FROM teacher";
	ResultSet rs = statement.executeQuery(sql);
	//遍历结果集
	while(rs.next()){
		int id = rs.getInt("id");
		String name = rs.getString("name");
		int salary = rs.getInt("salary");
		String title = rs.getString("title");
		System.out.println(id+","+name+","+salary+","+title);
		}
	} catch (SQLException e){
		e.printStackTrace();
}

PreparedStatement 接口

  • PreparedStatement用于执行预编译SQL语句
  • 预编译SQL
    • 预编译SQL可以使用占位符 " ? " 来表示需要使用的 " 值 "
    • 占位符内容仅表达需要使用的值,而不会影响SQL语义
    • 占位符不可以改变语义
  • 常用方法:
方法作用
setXxx(int parameterIndex, Xxx value)为指定位置的占位符设置具体的值,如 setInt、setString 等
executeQuery()执行查询语句,返回一个 ResultSet 对象
executeUpdate()执行插入、更新或删除语句,返回受影响的行数
execute()执行 SQL 语句,返回一个布尔值,表示是否返回了结果集

根据ID修改用户信息:密码,昵称,年龄:

public class JDBCDemo10 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户ID:");
        String id = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();
        System.out.println("请输入昵称:");
        String nickname = scanner.nextLine();
        System.out.println("请输入年龄:");
        int age = scanner.nextInt();
        try (Connection connection = DBUtil.getConnection()) {
            String sql = "UPDATE userinfo SET password=?,nickname=?,age=? "+
                         "WHERE id="+"'"+id+"'";
            PreparedStatement ps = connection.prepareStatement(sql);
            ps.setString(1,password);
            ps.setString(2,nickname);
            ps.setInt(3,age);

            int num = ps.executeUpdate();
            System.out.println(num>0?"修改成功":"修改失败");
        }catch(SQLException e){
            e.printStackTrace();
        }
    }
}

关闭操作

在 JDBC 操作中,Connection、Statement 、PreparedStatement 以及 ResultSet 这些资源在使用完毕后都需要关闭,从而避免资源泄漏

  • 可以在 finally 块中,分别检查它们是否为 null,如果不为 null 则用 close() 方法关闭
finally {
	// 关闭资源
	if (ps != null) {
		try {
			ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	if (connection != null) {
		try {
			connection.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

而在Java 7 及以上版本,可以在 try 语句后面的括号中声明和初始化资源,当 try 块结束时,无论是否发生异常,这些资源都会自动调用 close() 方法关闭

try (
    Connection connection = DBUtil.getConnection();
    PreparedStatement ps = connection.prepareStatement(
        "UPDATE userinfo SET password=?,nickname=?,age=? WHERE id=?"
    )
) {
    ps.setString(1, password);
    ps.setString(2, nickname);
    ps.setInt(3, age);
    ps.setString(4, id);

    int num = ps.executeUpdate();
    System.out.println(num > 0 ? "修改成功" : "修改失败");
} catch (SQLException e) {
    e.printStackTrace();
}

工具类

在 Java 中进行 JDBC 操作时,为了提高代码的复用性和可维护性,通常会创建一个工具类来封装数据库连接、关闭等操作

public class DBUtil {
   
    static{
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(
                "jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true",
                "root",
                "root");

    }
}

这样在每次创建Connection时只需要

Connection connection = DBUtil.getConnection()

数据库连接池

数据库连接池在初始化时会创建一定数量的数据库连接对象,并将这些连接对象存储在一个池中。当应用程序需要与数据库进行交互时,它会从连接池中请求一个连接。如果池中有可用的连接,就直接将其分配给应用程序使用;如果池中没有可用连接,并且当前连接数尚未达到最大连接数限制,连接池会创建新的连接对象并分配给应用程序。当应用程序使用完连接后,需要将连接归还给连接池,而不是直接关闭连接。连接池会对归还的连接进行检查和清理,以便后续再次使用。

以常见的 Druid 数据库连接池为例

  • 要使用 Druid 连接池,首先需要在项目中添加相应的依赖。以 Maven 为例,在 pom.xml 中添加以下依赖
<!-- 数据库连接池 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>1.1.21</version>
</dependency>
  • 创建并配置 Druid 数据源

通过设置一系列参数来初始化 DruidDataSource 对象,这些参数包括数据库连接的 URL、用户名、密码,以及连接池的一些基本配置,如初始连接数、最大连接数等

public class DBUtil {
    private static DruidDataSource ds;
    static{
        //设置用于连接数据库的基本信息
        ds.setUrl("jdbc:mysql://localhost:3306/tedu?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");
        ds.setUsername("root");
        ds.setPassword("root");

        ds.setMaxActive(20);    //最大连接数
        ds.setInitialSize(5);   //设置初始容量

    }
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
}

JDBC中的事务控制

  • JDBC中在执行DML语句时,默认是自动提交事务的
  • 事务在JDBC中是依靠链接对象Connection来维护的
  • JDBC中控制事务的相关方法
方法作用
setAutoCommit(boolean autoCommit)设置事务是否自动提交
commit()提交事务
rollback()回滚事务
public class JDBCDemo11 {
    public static void main(String[] args) { 
        try (Connection connection = DBUtil.getConnection()){
            connection.setAutoCommit(false);	//不要自动提交
            String sql = "DELETE FROM userinfo WHERE id=3";
            Statement statement = connection.createStatement();
            int num = statement.executeUpdate(sql);
            System.out.println(num>0?"删除成功":"删除失败");
            /*
                无论执行的是提交事务,还是回滚事务,都会导致本次事务结束
             */
            connection.commit();//COMMIT
            System.out.println("事务提交了");
//            connection.rollback();//ROLLBACK
//            System.out.println("事务回滚了");
            //如果下面又开始执行其他DML操作时,会自动开启一个新的事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

设置事务的隔离级别

  • 事物的隔离级别有4种
    • 读未提交:SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    • 读已提交:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED
    • 可重复读:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ
    • 序列化:SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE
  • 读未提交:允许一个事务读取另一个事务未提交的数据
  • 读已提交:只能读取已提交的数据
  • 可重复读:在一个事务内多次读取同一数据时,结果是一致的
  • 序列化:强制事务串行执行,可避免所有的并发问题,但性能低

产生的问题:

隔离级别脏读不可重复幻读加锁读
读未提交×
读已提交××
可重复读×××
序列化×××

在Java中可以通过 Connection 接口的 setTransactionIsolation(int level) 方法设置事务隔离级别

参数:

名称
读未提交Connection.TRANSACTION_READ_UNCOMMITTED1
读已提交Connection.TRANSACTION_READ_COMMITTED2
可重复读Connection.TRANSACTION_REPEATABLE_READ4
序列化Connection.TRANSACTION_SERIALIZABLE8
不支持事务Connection.TRANSACTION_NONE0

示例(可重复读):

connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值