JDBC 常用类/接口详解(MySQL为例)
DriverManager类
java.sql.DriverManager 是用于管理一组JDBC驱动程序的基本服务。
注意: JDBC 2.0 API中新增的DataSource接口提供了另一种连接到数据源的方法。 使用DataSource对象是连接到数据源的首选方法。
DriverManager类功能
注册驱动
告诉程序该使用什么数据库驱动jar
可以直接使用DriverManager类的静态方法注册驱动:
static void registerDriver(Driver driver) // 注册与给定的驱动程序 DriverManager 。
也可以间接使用该方法,如下介绍直接和间接调用DriverManager类的静态方法注册驱动。
如我们要连接操作的是MySQL,那么我们就要注册驱动,告诉程序要使用MySQL驱动架包。如下就是注册MySQL数据库驱动:
注册驱动第一种方法:
Class<?> aClass = Class.forName("com.mysql.jdbc.Driver"); // 将字节码文件Driver.java加载进内存,返回Driver.class对象。
注册驱动第二种方法:
com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver(); DriverManager.registerDriver(driver);
com.mysql.jdbc.Driver类中存在静态代码块,如下:
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can‘t register driver!"); } }
这里可以看出来,两种驱动方法其实都是一样的,都要用到静态方法registerDriver()
备注:MySQL 5 之后的驱动jar包可以省略注册驱动的步骤。
获取数据库连接
方法
static Connection getConnection(String url, String user, String password)
方法说明
这是java.sql.DriverManager类的静态方法,用来获取数据库连接。该方法的返回值是Connection对象。 参数说明: url:指定连接的路径 user:用户名 password:密码 连接的是MySQL数据库时,参数url格式举例说明 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称 例子:jdbc:mysql://localhost:3306/Study 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
Connection接口
java.sql.Connection接口是一个数据库连接对象。它与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果。
Connection接口功能
获取执行SQL的对象
方法:
Statement createStatement() // 创建一个 Statement对象,用于将SQL语句发送到数据库。
PreparedStatement prepareStatement(String sql) // 创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
管理事务
方法:
开启事务
void setAutoCommit(boolean autoCommit) // 将此连接的自动提交模式设置为给定状态。参数为false,即开启事务
提交事务
void commit() // 使自上次提交/回滚以来所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。
回滚事务
void rollback() // 撤消在当前事务中所做的所有更改,并释放此 Connection对象当前持有的任何数据库锁。
Statement接口
java.sql.Statement接口,用于执行静态SQL语句并返回其生成的结果的对象。
默认情况下,每个Statement
对象只能有一个ResultSet
对象同时打开。 因此,如果一个ResultSet
对象的读取与另一个对象的读取交错,则ResultSet
对象必须由不同的Statement
对象生成。 在所有执行方法Statement
接口隐式关闭当前ResultSet
声明的对象,如果一个开放的存在。
Statement接口功能
执行静态SQL语句
方法:
boolean execute(String sql) // 执行给定的SQL语句,这可能会返回多个结果。该方法可以执行任意的SQL语句。
int executeUpdate(String sql) // 执行给定的SQL语句。执行的是DML(insert、update、delete)语句、DDL(create,alter、drop)语句// 返回值是一个int类型的数 ———— 执行的SQL语句影响的行数
ResultSet executeQuery(String sql) // 执行给定的SQL语句,该语句返回单个ResultSet对象。执行DQL(select)语句
举例
连接Study数据库,向account表中的id=1的balance字段值加上500。表数据如下:
Java代码实现:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class JdbcDemo01 { public static void main(String[] args) throws ClassNotFoundException, SQLException { // 1、导入jar包:mysql-connector-java-5.1.48.jar // Add as Library ... // 2、注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 3、获取数据库连接对象 Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/Study?useSSL=false", "用户名", "密码"); // 4、定义SQL语句 // 将account表中的id=1的balance字段值加上500 String sql = "UPDATE account Set balance = balance + 500 WHERE id = 1;"; // 5、获取执行sql的对象 Statement Statement statement = connection.createStatement(); // 6、执行SQL语句:执行成功,返回 1 int returnResult = statement.executeUpdate(sql); // 7、查看是否执行成功 System.out.println(returnResult); // 8、释放资源 statement.close(); connection.close(); } }
执行成功,控制台输出:1,说明影响的行数是一行。
查看表中现在的数据:
ResultSet接口
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。即该接口是结果集对象,用来封装查询结果。
<span>ResultSet</span>
对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。 next
方法将光标移动到下一行,并且由于在<span>ResultSet</span>
对象中没有更多行时返回false
,因此可以在while
循环中使用循环来遍历结果集。
下面介绍几个具代表性的方法
ResultSet接口功能
游标向下移动一行
boolean next() // 将光标从当前位置向前移动一行。
获取数据
getXxx(参数列表) 方法
String getString(int columnIndex) // 这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。 // int columnIndex:列的索引,索引从1开始,1对应第一列 String getString(String columnLabel) // 这个检索的当前行中指定列的值 ResultSet对象为 String的Java编程语言。 // String columnLabel:指定的列名称
int getInt(int columnIndex) // 这个检索的当前行中指定列的值 ResultSet作为对象 int在Java编程语言。 // int columnIndex:列的索引
举例
题目:查看account表中的记录,account表位于本地mysql的Study数据库下,端口号为3306
account表
account表如下: CREATE TABLE account ( id INT PRIMARY KEY AUTO_INCREMENT, -- id NAME VARCHAR(10), -- 名字 balance DOUBLE -- 余额 ); INSERT INTO account (NAME, balance) VALUES (‘LeeHua‘, 1500), (‘Tom‘, 1000);
Java程序实现:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.sql.ResultSet; public class JdbcDemo03 { public static void main(String[] args) { Statement statement = null; Connection connection = null; ResultSet resultSet = null; try { // 1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 2. 定义SQL语句 String sql = "SELECT * FROM account;"; // 3.获取Connection对象 connection = DriverManager.getConnection( "jdbc:mysql:///Study?useSSL=false", "LeeHua", "qq562246926" ); // 4.获取执行sql的对象 Statement statement = connection.createStatement(); // 5.执行sql,返回查询结果(一个表),用ResultSet对象来封装。 resultSet = statement.executeQuery(sql); // 6.处理结果 // 6.1 循环判断游标是否是最后一行末尾。 while(resultSet.next()){ // 获取数据 // 6.2 获取数据 // 获取 ID 字段的值 int id = resultSet.getInt(1); // 获取name字段的值 String name = resultSet.getString("name"); // 获取balance字段的值 double balance = resultSet.getDouble(3); // 输出 System.out.println(id + " --- " + name + " --- " + balance); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { // 7. 释放资源 releaseResources(resultSet); releaseResources(statement); releaseResources(connection); } } public static <T> void releaseResources (T t){ if(t != null){ try { // 利用反射,获取class对象 Class<?> aClass = t.getClass(); // 获取class对象中的方法对象 Method close = aClass.getMethod("close"); // 执行方法 close.invoke(t); } catch (Exception e) { e.printStackTrace(); } } } }
PreparedStatement接口
PreparedStatement接口 extends Statement接口,表示预编译的SQL语句的对象。SQL语句已预编译并存储在<span>PreparedStatement</span>
对象中。 然后可以使用该对象多次有效地执行此语句。
预编译的SQL:参数使用?作为占位符
如:
new Statement.executeQuery(sql1); new PreparedStatement.executeQuery(sql2);
两个对象都执行SQL语句,Statement接口是执行静态SQL的,即已经拼接好的SQL,而PreparedStatement接口是执行预编译的SQL语句的。
如:
sql1 = "SELECT * FROM account WHERE id = ID AND balance = 账户余额;" sql2 = "SELECT * FROM account WHERE id = ? AND balance = ?;"
sql1是静态的SQL语句,sql2是预编译SQL语句。
PreparedStatement接口的用法和其父接口Statement的用法差不多,只是父接口是执行静态SQL语句的,而PreparedStatement接口是传入并执行预编译的SQL语句的。
给?赋值
方法: setXxx(参数1,参数2)
参数说明:
参数1:?的位置编号 从1 开始 参数2:?的值
如:
void setInt(int parameterIndex, int x) // 将指定的参数设置为给定的Java int值。
void setString(int parameterIndex, String x) // 将指定的参数设置为给定的Java String值。
举例
需求:获取数据库中字段的记录,对比用户输入,模拟登陆。
实现
自定义一个注解,用于获取部分值:
package my.view.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) // 注解能作用于类上 @Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 public @interface PropertiesAnnotation { /* URL */ public abstract String url(); /* 用户 */ public abstract String user(); /* 密码 */ public abstract String password(); /* 驱动包 */ public abstract String driver(); }
创建一个MySQL表格:
CREATE TABLE login ( id INT PRIMARY KEY AUTO_INCREMENT, -- id user VARCHAR(30), -- 用户名 password VARCHAR(30) -- 密码 );
向表格中传入数据:
INSERT INTO login (user, password) VALUES (‘LeeHua‘, ‘123456‘), (‘Tom‘, ‘abcdef‘);
创建一个Jdbc工具类,用来注册驱动和获取连接对象:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; @PropertiesAnnotation( url = "jdbc:mysql:///Study", user = "mysql账号", password = "mysql密码", driver = "com.mysql.jdbc.Driver" ) public class JdbcUtils02 { private static String url; private static String user; private static String password; private static String driver; /* 文件的读取,只需要读取一次即可拿到这些值。利用反射和注解、使用静态代码块 */ static{ // 读取资源文件,获取值。 try { // 1. 解析注解 // 1.1 获取JdbcUtils02类的字节码文件对象 Class<JdbcUtils02> jdbcUtils02Class = JdbcUtils02.class; // 2. 获取上边的注解对象 PropertiesAnnotation annotation = jdbcUtils02Class.getAnnotation(PropertiesAnnotation.class); // 3. 调用注解中定义的抽象方法,获取返回值,赋值给静态成员变量 url = annotation.url(); user = annotation.user(); password = annotation.password(); driver = annotation.driver(); // 4. 注册驱动 Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连接 * @return 连接对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }
使用Statement接口,创建一个登录类:
public boolean login(String user, String password) { if (user == null || password == null) { return false; } Connection connection =null; Statement statement = null; ResultSet resultSet = null; try { // 获取数据库连接 connection = JdbcUtils02.getConnection(); // 定义SQL String sql = "SELECT * FROM login WHERE user = ‘" + user + "‘ AND password = ‘" + password + "‘;"; // 获取执行SQL对象 statement = connection.createStatement(); // 执行查询 resultSet = statement.executeQuery(sql); // 判断是否存在下一行数据 return resultSet.next(); } catch (SQLException e) { e.printStackTrace(); } return false; }
使用PreparedStatement接口,创建一个登录类:
public boolean login(String user, String password) { if (user == null || password == null) { return false; } Connection connection =null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 获取数据库连接 connection = JdbcUtils02.getConnection(); // 定义SQL String sql = "SELECT * FROM login WHERE user = ? AND password = ?;"; // 获取执行SQL语句对象 preparedStatement = connection.prepareStatement(sql); // 给?赋值 preparedStatement.setString(1, user); preparedStatement.setString(2, password); // 执行SQL语句 resultSet = preparedStatement.executeQuery(); // 判断是否存在下一行数据 return resultSet.next(); } catch (SQLException e) { e.printStackTrace(); } return false; }
模拟测试:登录方法在Demo类中。
public class Demo { public static void main(String[] args) { // 键盘录入,接收用户名和密码 Scanner scn = new Scanner(System.in); System.out.print("请输入用户名:"); String userName = scn.nextLine(); System.out.print("请输入密码:"); String password = scn.nextLine(); // 调用登录确认方法 Demo demo08 = new Demo(); if (demo.login(userName, password)) { System.out.println("登录成功!"); } else { System.out.println("登录失败!用户名或密码额错误。"); } } }
- 调用使用Statement接口创建的登录方法测试:
如果正常情况下输入,那么该方法判断用户账号密码是否正确是有效的。如果输入恒等式,那么该方法就失效了,无论输入什么恒等式,都可以登录,举例如:
请输入用户名:LeeHua 请输入密码:abc‘ OR ‘abc‘ = ‘abc
那么这个时候,被拼接好的SQL语句就如下:
SELECT * FROM login WHERE user = ‘LeeHua‘ AND password = ‘abc‘ OR ‘abc‘ = ‘abc‘;
这是一条恒等式,不论 password是否正确,都可以登录成功,这是相当危险的,就像别人能够随随便便登录个人微信一样,安全隐患很大,所以我们使用预编译的SQL语句,不使用静态SQL语句。
运行,控制台输出:登录成功!
- 调用使用PreparedStatement接口创建的登录方法测试:
这个时候无论输入什么,都会判断正确,因为这里传入的是一条预编译的SQL语句,并不是拼接的SQL语句。
举例如:
请输入用户名:LeeHua 请输入密码:abc‘ OR ‘abc‘ = ‘abc
那么这个时候实质传入的SQL语句是:
SELECT * FROM login WHERE user = ‘LeeHua‘ AND password = ‘abc‘ OR ‘abc‘ = ‘abc‘;
运行,控制台输出:登录失败!用户名或密码额错误。
所以,判断用户名和密码是准确的。