简介
JDBC 是 Java 提供的一种 API,用于毗连和操纵关系性数据库。它为 Java 提供了一种同一的接口,允许开发者通过一套 Java 代码与不同的数据库进行交互。涉及 SPI 机制,由 Java 语言提供一套标准的 JDBC 接口,由各个数据库服务厂商(MySQL、Oracle 等)做具体实现(驱动)。
其优点是可以随意替换底层的数据库,访问数据库的 Java 代码根本不变。
快速入门
起首必要确保 MySQL 已经下载而且启动服务。
- 创建工程,导入数据库驱动 jar 包
- 使用 jdbc 实行 sql
//注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
//获取毗连
String url = “jdbc:mysql://localhost:3306/test”;
String username = “root”;
String password = “1234”;
Connection conn = DriverManager.getConnection(url, username, password);
//界说sql
String sql = “update account set money = 1000 where id = 1;”;
//获取实行sql的对象Statement
Statement stmt = conn.createStatement();
//实行sql
int count = stmt.executeUpdate(sql);
System.out.println(count > 0 ? “修改成功” : “修改失败”);
//释放资源
stmt.close();
conn.close();
处理查询语句
- // 数据库连接参数
- String url = "jdbc:mysql://localhost:3306/test";
- String username = "your_username";
- String password = "your_password";
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
- try {
- // 1. 获取数据库连接
- conn = DriverManager.getConnection(url, username, password);
- // 2. 创建 Statement 对象
- stmt = conn.createStatement();
- // 3. 执行查询语句
- String query = "SELECT * FROM your_table";
- rs = stmt.executeQuery(query); // 执行查询并返回结果集
- // 4. 处理查询结果
- while (rs.next()) {
- System.out.println("Column 1: " + rs.getString(1));
- System.out.println("Column 2: " + rs.getString(2));
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- // 5. 关闭资源
- if (rs != null) rs.close();
- if (stmt != null) stmt.close();
- if (conn != null) conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
复制代码 API 详解
DriverManager
DriverManager 是用于管理一组数据库驱动的类。它负责选择符合的数据库驱动程序,并建立与数据库的毗连。DriverManager 是 JDBC 毗连池的一部分,它提供了一个简朴的接口来得到数据库毗连。
驱动注册
我们可以查看注册驱动时使用的 Driver 类:
Ctrl B 查看其源码发现,其内部有一个静态代码块中正是调用了 DriverManager 中的注册方法,而这个静态代码块会随着类的加载而加载。
其着实 MySQL 5 的版本之后,可以不显式使用 DeiverManager.register() 方法也能成功注册驱动,即将 Class.forName() 这一行注释掉依然能够运行成功。这是因为 JDBC 4.0 引入了主动加载驱动程序的机制。只要 JDBC 驱动的 JAR 文件存在于类路径中,而且该驱动的 META-INF/services/java.sql.Driver 文件已经设置好,JDBC 驱动就会主动注册到 DriverManager。
获取毗连
通过查看 DriverManager 的源码可以发现其中有一个 getConnection 方法,正是用于获取数据库毗连。
其中必要传入三个参数:
- String url:毗连路径
- 语法:jdbc:mysql://IP地点:端口号/数据库名称参数(键值对,多个使用 & 毗连)
- 示例:jdbc:mysql://localhost:3306/test
- 本机 3306 端口的url可以简写:jdbc:mysql:///test
- 可以在后面用 毗连 useSSL=False 表示不使用 SSL 安全套件,避免告诫提示:jdbc:mysql://localhost:3306/testuseSSL=False
- user:数据库用户名
- password:数据库暗码
Connection
在 JDBC 中,Connetion 代表了与数据库的毗连。通过 Connection 对象,应用程序可以实行 SQL 查询、更新数据库、管理事务等操纵。
它的功能包罗:
- 创建 Statement、PreparedStatement 和 CallableStatement 对象以实行 SQL 查询和更新操纵
- 启动和管理数据库事务
- 管理数据库毗连的属性(主动提交、隔离级别等)
- 提供数据库元数据等(数据库版本、支持的功能等)
获取数据库毗连
- 使用 DriverManager 获取毗连
Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb”, “root”, “password”);
- 使用 DataSource 获取毗连
DataSource 是一个更机动的毗连池实现,实用于高性能应用。你可以从毗连池中获取毗连。
DataSource dataSource = new MysqlDataSource();
dataSource.setURL(“jdbc:mysql://localhost:3306/mydb”);
dataSource.setUser(“root”);
dataSource.setPassword(“password”);
Connection conn = dataSource.getConnection();
Connection 方法
获取实行 SQL 的对象
- 普通实行 SQL 对象
Statement stmt = conn.createStatement();
- 预编译 SQL 的实行 SQL 对象
用于防止 SQL 注入
PreparedStatement pstmt = conn.prepareStatement(“SELECT * FROM users WHERE id = ?”);
- 实行存储过程的对象
CallableStatement cstmt = CallableStatement prepareCall(sql)
事务方法
- 主动提交模式
默认情况下,JDBC 毗连的事务是主动提交的,即每一条 SQL 语句都会立刻提交到数据库,可以通过 setAutoCommit(false) 禁用主动提交,并在事务竣事时手动提交事务。
conn.setAutoCommit(boolean autoCommit);
- 提交事务
conn.commit();
- 回滚事务
conn.rollback();
事务管理演示
先摆设好事务管理的代码:
- //1. 注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- //2. 获取连接
- String url = "jdbc:mysql:///test?useSSL=false";
- String username = "root";
- String password = "1234";
- Connection conn = DriverManager.getConnection(url, username, password);
- //3. 定义sql
- String sql1 = "update account set money = 3000 where id = 1";
- String sql2 = "update account set money = 3000 where id = 2";
- //4. 获取执行sql的对象 Statement
- Statement stmt = conn.createStatement();
- //开启事务
- conn.setAutoCommit(false);
- try {
- //5. 执行sql
- int count1 = stmt.executeUpdate(sql1);//受影响的行数
- //6. 处理结果
- System.out.println(count1 > 0 ? "修改成功" : "修改失败");
- //5. 执行sql
- int count2 = stmt.executeUpdate(sql2);//受影响的行数
- //6. 处理结果
- System.out.println(count2 > 0 ? "修改成功" : "修改失败");
- } catch (Exception e) {
- //回滚事务
- conn.rollback();
- System.out.println("出错已回滚");
- e.printStackTrace();
- }
- //提交事务
- conn.commit();
- //7.释放资源
- stmt.close();
- conn.close();
复制代码 此时程序是没有非常的,我们来看实行结果:
如今,在 sql1 的实行结果后面加上一条非常语句:
此时可以看到,IDEA 已经主动将 sql2 的处理语句变灰,提示不会实行下面代码,来看实行结果:
果然,在抛出非常的同时,事务进行了回滚。回滚之后,由于事务的原子性,此时 sql1 对数据库的修改也是不成功的。
Statement
Statement 是 JDBC 中用于实行 SQL 查询、更新和其他数据库操纵的接口。它用于向数据库发送 SQL 语句,并吸收数据库返回的结果。Statement 是 JDBC 最根本的接口之一,通常用于实行简朴的 SQL 语句。
常见方法
- executeQuery(String sql)
用于实行 SELECT 查询,并返回一个 ResultSet 对象,ResultSet 存储了查询结果。
String sql = “SELECT * FROM employees”;
ResultSet res = stmt.executeQuery(sql);
- executeUpdate(String sql)
用于实行 SQL 更新(如 INSERT、UPDATE、DELETE),返回受影响的行数。
String sql = “UPDATE employees SET salary = 50000 WHERE id = 1”;
int rowsAffected = stmt.executeUpdate(sql);
- execute(String sql)
用于实行任何 SQL 语句。返回一个 boolean 值,表示结果是否为 ResultSet(如果是查询语句则返回 true,否则返回 false)。
String sql = “DROP TABLE employees”;
boolean isResultSet = stmt.execute(sql);
- getGeneratedKeys()
在实行插入操纵时,如果数据库支持生成主动主键,可以通过这个方法获取插入操纵生成的主键。
String sql = “INSERT INTO employees (name, age) VALUES (‘John’, 30)”;
int rowsInserted = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if (rs.next()) {
int generatedId = rs.getInt(1);
System.out.println("Generated ID: " + generatedId);
}
常见实现
Statement
用于实行没有参数的静态 SQL 查询。每次实行时都会重新编译 SQL 语句,因此效率较低,尤其是在必要重复实行雷同 SQL 时。
- Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery("SELECT * FROM employees");
复制代码 PreparedStatement
用于实行带有参数的预编译 SQL 语句。相比 Statement,它进步了实行效率并避免了 SQL 注入攻击。
- String sql = "SELECT * FROM employees WHERE id = ?";
- PreparedStatement pstmt = conn.prepareStatement(sql);
- pstmt.setInt(1, 1); // 设置参数
- ResultSet rs = pstmt.executeQuery();
复制代码 CallableStatement
用于调用存储过程。
- String sql = "{call getEmployeeDetails(?)}"; // 存储过程调用
- CallableStatement cstmt = conn.prepareCall(sql);
- cstmt.setInt(1, 1); // 设置存储过程参数
- ResultSet rs = cstmt.executeQuery();
复制代码 ResultSet
在 JDBC 中,ResultSet 是用于表示查询结果的数据布局,它存储从数据库查询返回的数据,并提供方法来访问和处理这些数据。
方法
- boolean() next
将光标从当前位置向前移动一行;判定当前行是否为有效行。
返回 true:有效行,当前行有数据;返回 false:无效行,当前行没有数据。
- xxx getxxx(参数)
xxx 表示数据范例,参数有 int 或 String,int 表示列的编号,从 1 开始;String 代表列的名称。
示例
- //1. 注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- //2. 获取连接
- String url = "jdbc:mysql:///test?useSSL=false";
- String username = "root";
- String password = "1234";
- Connection conn = DriverManager.getConnection(url, username, password);
- //3. 定义sql
- String sql = "select * from account";
- //4. 获取执行sql的对象 Statement
- Statement stmt = conn.createStatement();
- //5.执行sql
- ResultSet resultSet = stmt.executeQuery(sql);
- //6. 处理结果
- while(resultSet.next()){
- int id = resultSet.getInt("id");
- String name = resultSet.getString("name");
- int money = resultSet.getInt("money");
- System.out.println(id + " " + name + " " + money);
- }
复制代码 查询结果
PreparedStatement
PreparedStatement 是 JDBC 中用于实行预编译 SQL 查询的接口,它相较于 Statement 更高效、安全。通过 PreparedStatement,SQL 查询在实行前被编译,而且可以多次实行,得当重复实行传入不同参数的雷同的 SQL。
使用
- 获取 PreparedStatement 对象
// SQL语句中的参数值,使用?占位符替代
String sql = “SELECT * FROM users WHERE username = ? AND password = ?”;
// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
- 设置参数值
Sql 语句使用 ?作为占位符,在实行 SQL 之前必要调用方法设置这些参数值。使用 setXXX(int parameterIndex, XXX x) 给 赋值。
pstmt.setString(1, “root”); //设置第一个参数为String root
pstmt.setString(2, “1234”); //设置第二个参数为String 1234
- 实行 SQL
executeUpdate() 实行DDL语句和DML语句;executeQuery() 实行DQL语句。
不必要传递 SQL 语句,因为获取实行对象的时候就已经对语句进行预编译了。
SQL 注入
SQL 注入发生在应用程序没有正确处理用户输入的情况下,攻击者通过注入恶意的 SQL 语句来干扰数据库的正常实行逻辑。通常,注入点存在于拼接 SQL 查询字符串的地方。
假设有一条 sql:
- String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
复制代码 如果用户输入:username = admin 和 password = ' OR '1' = '1,那么生成的 SQL 查询将酿成:
- SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1' = '1'
复制代码 该查询总是返回结果,因为 ‘1’ = ‘1’ 总为真,攻击者通过这种方式绕过了暗码验证。
预编译防止 SQL 注入
PreparedStatement 的一个关键特性是 SQL 查询在实行之前会被预编译,而且用户输入的参数(如用户名、暗码等)不会直接拼接进查询字符串中,而是通过参数占位符 传递给数据库。数据库引擎会将这些占位符与传入的参数分别处理,避免了直接在查询中插入用户提供的恶意 SQL 代码。
防止 SQL 注入的重点在于实行之前的编译以及 占位符,对应的参数值会根据其数据范例安全地传递到数据库,不会被当作 SQL 代码实行。
数据库毗连池
数据库毗连池是一种能够复用毗连来进步数据库访问效率的技能,可以看作是一个容器,负责分配、管理数据库毗连。它通过创建一组数据库毗连并在多个请求之间共享这些毗连,避免了频仍的建立和关闭数据库毗连所带来的性能开销。
工作原理
数据库毗连池的根本思想是事先创建一定数量的数据库毗连,将这些毗连保存在池中。当应用程序必要访问数据库时,它从毗连池中获取一个毗连,实行完操纵后,将毗连返回毗连池,而不是直接关闭毗连。这样,下次必要数据库毗连时,应用程序可以直接从池中获取,而不必要重新建立毗连。
如果新请求到来时毗连池毗连已经用完怎么办?
- 等待(Blocking)
常见计谋是阻塞等待,即当毗连池中没有空闲毗连时,新的请求会被阻塞,直到有可用毗连为止。毗连池会等待,直到某个毗连被归还到池中。如果设置了最大等待时间,则在等待超时后,新的请求将会失败。
- 拒绝(Failing)
当毗连池中没有可用毗连时,新的数据库请求会直接失败,通常会抛出非常。这种方式实用于对于并发请求要求严酷的系统,拒绝额外请求可以避免数据库过载。
- 扩容(Dunamic Sizing)
一些毗连池支持动态扩展,即当毗连池中的毗连用完时,毗连池会主动增长新的毗连,直到达到设置的最大毗连数。
- 其他计谋
一些毗连池实现提供了更为复杂的设置选项,如等待队列、毗连重试等。
毗连实现
DataSource 是 sun 公司提供的数据库毗连池标准接口,由第三方来实现次接口。
常见的数据库毗连池:
- Apache DBCP:Apache 提供的数据库毗连池实现,简朴且广泛使用。
- C3P0:提供了一些更为高级的毗连池特性,如主动测试毗连、毗连池大小主动调整等。
- HikariCP:一个高性能的 JDBC 毗连池,被广泛认为是当前最良好的毗连池实现之一,特殊是在性能方面。
- Druid:阿里巴巴开源的数据库毗连池项目,功能强盛,性能良好,是Java语言最好的数据库毗连池之一。
Druid 演示
- 导入 Druid 驱动
- 界说设置文件
先在 src 包下 new 一个 properties 文件:
添加设置信息:
注意核对本身的用户名、暗码和数据库名称
- 创建毗连池
文件路径填本身创建的 properties 文件路径
//加载设置文件
Properties prop = new Properties();
prop.load(new FileInputStream(“D:\Data\code\IDEA\JavaWeb\JDBC\src\druid.properties”));
//获取毗连池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
- 获取数据库毗连
//获取数据库毗连
Connection conn = dataSource.getConnection();
//验证
System.out.println(conn);
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |