# jdbc **Repository Path**: xfanonymous/jdbc ## Basic Information - **Project Name**: jdbc - **Description**: JDBC的使用和实操 - **Primary Language**: Unknown - **License**: EPL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-09-21 - **Last Updated**: 2024-04-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JDBC - JDBC(Java Database Connectivity)Java 数据库连接技术,由 Java 语言规范接口、各数据库厂商的驱动 jar 包组成。 - JDBC 作用:只需要调用 JDBC 接口规定的方法,即可操作所有数据库软件。切换数据库时,只需要更新第三方驱动 jar 包,不需要更改代码。 - JDBC 的规范接口 api 存储在 ```java.sql``` 和 ```javax.sql``` 包中。 - MySQL 版本 8.0.26,驱动```com.mysql.jdbc.Driver```和```com.mysql.cj.jdbc.Driver```。 - 核心接口:```DriverManager、Connection、Statement、ResultSet```均保存在```java.sql```包中。 - ```Driver```是MySQL驱动包```com.mysql.cj.jdbc```中的实现类,实现了```java.sql.Driver```接口。 ## 数据库SQL ```sql CREATE DATABASE jdbc; USE jdbc; SHOW TABLES; -- 用户表 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户主键', 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','管理员'); -- 银行表 CREATE TABLE t_bank ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键', account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号', money INT UNSIGNED COMMENT '金额,不能为负值' ) ; INSERT INTO t_bank(account,money) VALUES ('ergouzi',1000),('lvdandan',1000); -- 员工表 CREATE TABLE t_customer ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '客户主键', name VARCHAR(20) COMMENT '客户名称', gender VARCHAR(4) COMMENT '客户性别', age INT COMMENT '客户年龄', salary DOUBLE(8,1) COMMENT '客户工资', phone VARCHAR(11) COMMENT '客户电话' ); insert into t_customer(name,gender,age,salary,phone) values(bb,m,10,1,123); -- DROP TABLE t_customer; ``` ## 步骤 1. 注册驱动:通过```DriverManager```注册```Driver```,通过反射方式防止重复注册。 2. 获取连接:通过```DriverManager```传入URL参数,获取连接```Connection```。 3. 创建对象:通过```Connection```创建发送SQL的对象```PreparedStatement | Statement```。 4. 发送SQL:通过```Statement```的```executeQuery(sql)```方法执行查询SQL并获取查询结果```ResultSet```,或```executeUpdate(sql)```执行非DQL语句获取影响的行数。 5. 结果解析:通过```ResultSet```的```next()```方法判断是否还有结果数据,通过```getObject(int columnIndex)```等获取字段值。 6. 关闭资源:关闭```ResultSet、Statement、Connection```。 ### 注册驱动 - ```DriverManager```将第三方数据库厂商的实现驱动jar注册到程序中,用于创建数据库连接。 ```java // 方法一:重复注册驱动,传参Driver类中的静态代码块同样调用了registerDriver()方法 DriverManager.registerDriver(new Driver()); // 方法二:依赖MySQL驱动的实现类Driver,不方便做动态变更,如果替换成其他数据库需要修改代码 new Driver(); // 方法三:通过反射方式,触发Driver类加载,触发静态代码执行,把字符串提取到外部配置文件,方便后续切换为其他数据库的驱动 Class.forName("com.mysql.cj.jdbc.Driver"); ``` ### 创建连接 - ```Connection``` 是数据库连接对象,通过 url、user、password 连接数据库。 - URL 格式: - ```jdbc:数据库管理软件名://ip地址|主机名:端口号/数据库名``` - ```jdbc:mysql://ip:port/dbname``` - ```jdbc:mysql://localhost:3306/jdbc?user=root&password=root&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true``` - ```jdbc:mysql://localhost:3306/jdbc?rewriteBatchedStatements=true``` 开启批量插入 ```java // 方法一:三个参数 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root"); // 方法二:两个参数,java.util.Properties是封装参数的Map容器,至少要包含两个key保存连接数据库的账号信息 Properties info = new Properties(); info.put("user", "root"); info.put("password", "root"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", info); // 方法三:一个参数 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc?user=root&password=root"); ``` ### 创建对象 - 通过```java.sql.Connection```创建能发送SQL到数据库的对象```Statement | PreparedStatement | CallableStatement``` - ```Statement```:执行静态SQL语句,不能动态赋值,执行拼接SQL存在SQL注入风险。 - ```PreparedStatement```:预编译SQL,可以动态赋值,推荐使用。 - 区别:Statement 执行SQL方法时传入SQL语句,PreparedStatement 创建时传入SQL,执行方法不需要。 ```java // 静态SQL语句 String sql = "select id,account,password,nickname from t_user;"; // 动态拼接语句,当输入的 account 为 ' or '1' = '1 时,整条SQL语句为true,发生SQL注入 String sql1 = "select * from t_user where id = '"+ id + "' and account = '" + account + "';"; // 动态赋值语句,DQL查询语句 String sql2 = "select * from t_user where id = ? and account = ? ;"; // 动态赋值语句,非DQL语句 String sql3 = "insert into t_user (account,password,nickname) values (?,?,?);"; // 方法一:存在SQL注入问题,执行方法时传入SQL语句 Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql1); // 方法二:预编译,动态赋值,解决SQL注入问题,创建时传入SQL语句,执行方法无需传参 PreparedStatement preparedStatement = connection.prepareStatement(sql2); preparedStatement.setObject(1, id); preparedStatement.setObject(2, account); ResultSet resultSet = preparedStatement.executeQuery(); ``` ### 执行SQL - 查询语句使用```Statement#executeQuery(sql)、PreparedStatement#executeUpdate()```返回结果集```ResultSet```。 - 非DQL语句用```Statement#executeUpdate(sql)、PreparedStatement#executeQuery()```返回影响的行数。 ```java // 执行DQL查询语句,返回查询的结果集 ResultSet resultSet = statement.executeQuery(sql); // 执行非DQL语句,返回影响的行数 int rows = statement.executUpdate(sql); // preparedStatement执行方法不需要传SQL语句 ResultSet resultSet = preparedStatement.executeQuery(); // 非DQL语句执行返回影响的行数 int rows = preparedStatement.executeUpdate(); ``` ### 结果解析 - ```resultSet.next()```判断是否还有下一行数据,方法执行会移动游标cursor,游标默认指向第一行结果的上边。方法返回结果为boolean类型,false: 没有数据,也不移动了,true: 有更多行,并且移动到下一行。一般在 if 或者 while 循环中嵌套。 - ```resultset.getObject(int columnIndex)``` 获取每行数据对应字段的值,传入列的下标,下标从1开始,从左到右,推荐使用此方法。 - ```resultset.getObject(String columnLabel)``` 获取每行数据对应字段的值,传入列名获取,输入结果集的字段名,有别名用别名 。 - ```resultset.getMetaData()```获取列信息对象```ResultSetMetaData```。通过```ResultSetMetaData```的```getColumnCount()```可以获取字段个数,```getColumnLabel(int)```获取字段名。注意数据库下标全是从1开始。 ```java 1. // 结果集遍历及字段值获取 while (resultSet.next()) { int id1 = resultSet.getInt("id"); Object account1 = resultSet.getObject("account"); String password = resultSet.getString(2); String nickname = resultSet.getString("nickname"); System.out.println("user id="+ id1 + "::" + account1 + "::" + password + "::nickname=" +nickname); } 2. // 结果集转存到List中 List mapList = new ArrayList<>(); // 通过结果集的 MetaData 获取列信息对象,可以获取字段名、字段个数 ResultSetMetaData metaData = resultSet.getMetaData(); // 获取字段个数 int columnCount = metaData.getColumnCount(); // 遍历结果集每行数据,一行数据对应一个Map while (resultSet.next()) { Map map = new HashMap(); // 推荐自动化,注意数据库下标都从1开始 for (int i = 1; i <= columnCount; i++) { // ResultSetMetaData通过下标获取字段名,有别名会获取别名,不要使用 getCloumnName() String columnLabel = metaData.getColumnLabel(i); // 推荐resultSet通过下标获取字段值 Object object = resultSet.getObject(i); map.put(columnLabel, object); } mapList.add(map); } ``` ### 关闭资源 ```java resultSet.close(); statement.close(); connection.close(); ``` ## 参考资料 - [JDBC在线文档](https://www.wolai.com/4SyaBmWpcQhVKae3oKjuNC) - [JDBC视频](https://www.bilibili.com/video/BV1sK411B71e/)