1.什么是JDBC
JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。
JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
使用 JDBC 的好处:
1)程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
2)使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
JDBC的核心API
接口或类 作用 | |
DriverManager 类 | 1) 管理和注册数据库驱动 2) 得到数据库连接对象 |
Connection 接口 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 | |
Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器。 |
PreparedStatemen 接口 一个 SQL 语句对象,是 Statement 的子接口 | |
ResultSet 接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
2.代码实现
1.创建一个空的项目,创建一个新的模块,在工程目录下创建一个libs文件用于管理驱动jar包
2.把mysql对应版本的驱动jar包复制到libs文件下(驱动jar可去官网下载MySQL :: Download Connector/J,实在不会可参考官网下载Java连接MySql驱动jar包 - !O0O! - 博客园),然后右键libs文件选择【Add as Library】添加成库
3.编写执行方法(连接的是我本地的数据库,执行sql语句随意写的)
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JdbcDemo1 {
public static void main(String[] args) throws Exception {
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库的连接对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb", "root", "yourpassword");
//4.定义sql语句
String sql = "update account set balance = 5000 where id =1 ";
//5.获取执行sql执行的对象,Statement
Statement stmt = conn.createStatement();
//6.执行sql
int count = stmt.executeUpdate(sql);//count为受影响的行数
//7.处理结果
System.out.println(count);
//8.释放资源
stmt.close();
conn.close();
}
}
4.执行main方法,打印结果受影响为1行,代表sql执行成功,到数据库查看sql执行结果是否生效
上面为不太规范的写法,下面这种相对来说较为规范一点
更新表数据
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcDemo2 {
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.定义sql
String sql1 = "insert into account values(null,'王武',25000)";
String sql2 = "update account set balance = 600 where id =1";
//3.获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/wystest", "root", "password");
//4.获取执行sql对象 Statement
stmt = conn.createStatement();
//5.执行sql
int count = 0;
count += stmt.executeUpdate(sql1);//受影响的行数
count += stmt.executeUpdate(sql2);
//6.处理结果
System.out.println(count);
if (count > 0){
System.out.println("执行成功!");
}else{
System.out.println("执行失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//7.最后释放资源
//判断一下,避免空指针异常
if(stmt != null){
try {
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try {
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
}
查询表数据
package cn.itcast.jdbc;
import java.sql.*;
public class selectData {
public static void main(String[] args) throws SQLException {
//1) 得到连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/wystest","root","password");
//2) 得到语句对象
Statement statement = connection.createStatement();
//3) 执行SQL语句得到结果集ResultSet对象
ResultSet rs = statement.executeQuery("select * from student");
//4) 循环遍历取出每一条记录
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
boolean gender = rs.getBoolean("gender");
Date birthday = rs.getDate("birthday");
//5) 输出的控制台上
System.out.println("编号:" + id + ", 姓名:" + name + ", 性别:" + gender + ", 生日:" + birthday); }
//6) 释放资源
rs.close();
statement.close();
connection.close();
}
}
创建表
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class createTable {
public static void main(String[] args) {
//1. 创建连接
Connection conn = null;
Statement statement = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/wystest", "root", "password");
//2. 通过连接对象得到语句对象
statement = conn.createStatement();
//3. 通过语句对象发送 SQL 语句给服务器
//4. 执行 SQL
statement.executeUpdate("create table student (id int PRIMARY key auto_increment, " + "name varchar(20) not null, gender boolean, birthday date)");
//5. 返回影响行数(DDL 没有返回值)
System.out.println("创建表成功");
} catch (SQLException e) {
e.printStackTrace();
}
//6. 释放资源 finally {
//关闭之前要先判断
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
statement:执行sql的对象
1.boolean execute(String sql) 可以执行任意的sql
2.int executeUpdate(String sql) 执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句 *返回值为影响的行数,可通过这个影响的行数判断语句是否执行成功,返回值大于0的则执行成功,反之则执行失败
3.ResultSet executeQuery(String sql) 执行DQL(select)语句
上面代码中出现了很多重复的代码,可以把这些公共代码抽取出来
创建类 JdbcUtil 包含 3 个方法:
1) 可以把几个字符串定义成常量:用户名,密码,URL,驱动类
2) 得到数据库的连接:getConnection()
3) 关闭所有打开的资源:close(Connection conn, Statement stmt),close(Connection conn, Statement stmt, ResultSet rs)
package cn.itcast.jdbc.util;
import java.sql.*;
/**
* 访问数据库的工具类
*/
public class JDBCUtils {
//可以把几个字符串定义成常量:用户名,密码,URL,驱动类
private static final String USER = "root";
private static final String PWD = "password";
private static final String URL = "jdbc:mysql://localhost:3306/wystest"; private static final String DRIVER= "com.mysql.cj.jdbc.Driver";
/**
* 注册驱动 */
static { try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 得到数据库的连接 */
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
/**
* 关闭所有打开的资源 */
public static void close(Connection conn, Statement stmt) { if (stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
} }
if (conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
/**
* 关闭所有打开的资源 */
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
close(conn, stmt);
}
}
3.案例一:用户登录
3.1创建一张表,添加几条用户数据
create table user (
id int primary key auto_increment,
name varchar(20),
password varchar(20)
);
insert into user values (null,'jack','123'),(null,'rose','456');
3.2使用 Statement 字符串拼接的方式实现用户的登录, 用户在控制台上输入用户名和密码
3.3步骤
1) 得到用户从控制台上输入的用户名和密码来查询数据库
2) 写一个登录的方法
通过工具类得到连接
创建语句对象,使用拼接字符串的方式生成 SQL 语句
查询数据库,如果有记录则表示登录成功,否则登录失败
释放资源
package cn.itcast.jdbc;
import cn.itcast.jdbc.util.JDBCUtils;
import javax.xml.transform.Result;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
public class loginAuth {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
login(name, password);
}
/**
* 登录的方法 */
public static void login(String name, String password) { //a) 通过工具类得到连接
Connection connection = null;
Statement statement = null;
ResultSet rs = null;
try {
connection = JDBCUtils.getConnection();
//b) 创建语句对象,使用拼接字符串的方式生成SQL语句
statement = connection.createStatement();
//c) 查询数据库,如果有记录则表示登录成功,否则登录失败
String sql = "select * from user where name='" + name + "' and password='" + password + "'";
// System.out.println(sql);
rs = statement.executeQuery(sql);
if (rs.next()) {
System.out.println("登录成功,欢迎您:" + name);
} else {
System.out.println("登录失败");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//d) 释放资源
JDBCUtils.close(connection, statement, rs);
}
}
}
sql注入问题
当我们输入以下密码,可以发现账号密码都不对的情况下竟然登录成功了
问题分析:
select * from user where name='sdaf' and password='a' or '1'='1'
name='sdaf' and password='a' 为假 然而 '1'='1' 为真
相当于:
select * from user where true; 查询了所有记录
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了 原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进 行简单的字符串拼接。
4.案例二:转账
之前我们是使用 MySQL 的命令来操作事务。接下来我们使用 JDBC 来操作银行转账的事务
4.1准备数据
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME, balance) VALUES ('Jack', 1000), ('Rose', 1000);
4.2步骤
-
1) 获取连接
-
2) 开启事务
-
3) 获取到 PreparedStatement
-
4) 使用 PreparedStatement 执行两次更新操作
-
5) 正常情况下提交事务
-
6) 出现异常回滚事务
-
7) 最后关闭资源
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import cn.itcast.jdbc.util.JDBCUtils;
public class Transaction {
//没有异常,提交事务,出现异常回滚事务
public static void main(String[] args) {
//1) 注册驱动
Connection connection = null; PreparedStatement ps = null; try {
//2) 获取连接
connection = JDBCUtils.getConnection();
//3) 开启事务
connection.setAutoCommit(false);
//4) 获取到PreparedStatement
//从 jack 扣钱
ps = connection.prepareStatement("update account set balance = balance - ? where name=?");
ps.setInt(1, 500); ps.setString(2,"Jack"); ps.executeUpdate();
//出现异常 System.out.println(100 / 0);
// 给 rose 加钱
ps = connection.prepareStatement("update account set balance = balance + ? where name=?");
ps.setInt(1, 500); ps.setString(2,"Rose"); ps.executeUpdate();
//提交事务
connection.commit(); System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
try {
//事务的回滚
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
System.out.println("转账失败"); }
finally {
//7) 关闭资源
JDBCUtils.close(connection,ps);
}
}
}