JDBC
**JDBC规范定义接口,具体的实现由各大数据库厂商来实现。**JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。数据库驱动由数据库厂商提供。
使用 JDBC 的好处:
程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
使用 JDBC 开发使用到的包:
会使用到的包 | 说明 |
---|---|
java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
javax.sql | 数据库扩展包,提供数据库额外的功能。如:连接池 |
数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类 |
JDBC 的核心 API
接口或类 | 作用 |
---|---|
DriverManager 类 | 1) 管理和注册数据库驱动 2) 得到数据库连接对象 |
Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 |
Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器。 |
PreparedStatemen 接口 | 一个 SQL 语句对象,是 Statement 的子接口 |
ResultSet 接口 | 用于封装数据库查询的结果集,返回给客户端 Java 程序 |
加载和注册驱动
加载和注册驱动的方法 | 描述 |
---|---|
Class.forName(数据库驱动实现类) | 加载和注册数据库驱动,数据库驱动由 mysql 厂商"com.mysql.jdbc.Driver" |
com.mysql.jdbc.Driver 源代码:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());//注册数据库驱动
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName这句话可以省略。
DriverManager 类
DriverManager 作用:
-
管理和注册驱动
-
创建数据库的连接
DriverManager类中的静态方法:
- **Connection getConnection (String url, String user, String password):**通过连接字符串,用户名,密码来得到数据库的连接对象
- **Connection getConnection (String url, Properties info):**通过连接字符串,属性对象来得到连接对象
JDBC 连接数据库的四个参数 | 说明 |
---|---|
用户名 | 登录的用户名 |
密码 | 登录的密码 |
连接字符串 URL | 不同的数据库 URL 是不同的,mysql 的写法jdbc:mysql://localhost:3306/数据库[?参数名=参数值] |
驱动类的字符串名 | com.mysql.jdbc.Driver |
连接数据库的 URL 地址格式:协议名:子协议://服务器名或IP地址:端口号/数据库名?参数=参数值
前提:必须是本地服务器,端口号是 3306
jdbc:mysql:///数据库名
乱码的处理
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
Connection 接口
Connection 作用:
Connection 接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
Connection 接口中的方法:
Statement createStatement(): 创建一条 SQL 语句对象
Statement 接口
Statement 作用:
代表一条语句对象,用于发送 SQL 语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
Statement 接口中的方法:
int executeUpdate(String sql): 用于发送 DML 语句,增删改的操作,insert、update、delete
参数:SQL 语句
返回值:返回对数据库影响的行数
ResultSet executeQuery(String sql): 用于发送 DQL 语句,执行查询的操作。select
参数:SQL 语句
返回值:查询的结果集
JDBC 访问数据库的步骤
-
注册和加载驱动(可以省略)
-
获取连接
-
Connection 获取 Statement 对象
-
使用 Statement 对象执行 SQL 语句
-
返回结果集
-
释放资源
释放资源
-
需要释放的对象:ResultSet 结果集,Statement 语句,Connection 连接
-
释放原则:先开的后关,后开的先关。ResultSet Statement Connection
-
放在哪个代码块中:finally 块
ResultSet 接口:
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
ResultSet 接口中的方法:
boolean next()
-
游标向下移动 1 行
-
返回 boolean 类型,如果还有下一条记录,返回 true,否则返回 false
数据类型 getXxx()
-
通过字段名,参数是 String 类型。返回不同的类型
-
通过列号,参数是整数,从 1 开始。返回不同的类型
java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
数据库工具类 JdbcUtils
public class JdbcUtil {
private static final String USER = "root";
private static final String PWD = "root";
private static final String URL = "jdbc:mysql://localhost:3306/db3";
private static final String DRIVER= "com.mysql.jdbc.Driver";
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConn() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
public static void closeAll(ResultSet resultSet, Statement statement, Connection connection){
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public class Demo {
public static void main(String[] args) throws SQLException {
Connection conn = JdbcUtil.getConn();
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery("select * from student2");
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int anInt = resultSet.getInt(3);
String string = resultSet.getString(4);
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(anInt);
student.setSex(string);
System.out.println(student);
}
JdbcUtil.closeAll(resultSet,statement,conn);
}
}
案例:用户登陆
public class LoginDemo {
public static void main(String[] args) throws SQLException {
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) throws SQLException {
Connection conn = JdbcUtil.getConn();
Statement statement = conn.createStatement();
String s = "select * from user where name='" + name + "' and password='" + password + "'";
System.out.println(s);
ResultSet resultSet = statement.executeQuery(s);
if(resultSet.next()){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
JdbcUtil.closeAll(resultSet,statement,conn);
}
}
SQL 注入问题
当select * from user where name=‘newboy’ and password=‘a’ or ‘1’='1’时,
‘1’=‘1’ 真
相当于
select * from user where true; 查询了所有记录
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
PreparedStatement 接口
PreparedStatement 是 Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句
-
因为有预先编译的功能,提高 SQL 的执行效率。
-
可以有效的防止 SQL 注入的问题,安全性更高。
Connection 创建 PreparedStatement 对象:
Connection 接口中的方法 | 描述 |
---|---|
PreparedStatement prepareStatement(String sql) | 指定预编译的 SQL 语句,SQL 语句中使用占位符?创建一个语句对象 |
PreparedStatement 接口中的方法:
PreparedStatement 接口中的方法 | 描述 |
---|---|
int executeUpdate() | 执行 DML,增删改的操作,返回影响的行数。 |
ResultSet executeQuery() | 执行 DQL,查询的操作,返回结果集 |
PreparedSatement 的好处
-
prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
-
安全性更高,没有 SQL 注入的隐患。
-
提高了程序的可读性
使用 PreparedStatement 的步骤:
-
编写 SQL 语句,未知内容使用?占位:“SELECT * FROM user WHERE name=? AND password=?”;
-
获得 PreparedStatement 对象
-
设置实际参数:setXxx(占位符的位置, 真实的值)
-
执行参数化 SQL 语句
-
关闭资源
PreparedStatement 中设置参数的方法 | 描述 |
---|---|
void setDouble(int parameterIndex, double x) | 将指定参数设置为给定 Java double 值。 |
void setFloat(int parameterIndex, float x) | 将指定参数设置为给定 Java REAL 值。 |
void setInt(int parameterIndex, int x) | 将指定参数设置为给定 Java int 值。 |
void setLong(int parameterIndex, long x) | 将指定参数设置为给定 Java long 值。 |
void setObject(int parameterIndex, Object x) | 使用给定对象设置指定参数的值。 |
void setString(int parameterIndex, String x) | 将指定参数设置为给定 Java String 值。 |
public class TestJdbc1 {
@Test
public void testJdbc() throws ClassNotFoundException, SQLException {
//1.注册驱动
// DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver" );
//2.获取连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
System.out.println(connection);//com.mysql.jdbc.JDBC4Connection@1f89ab83
//3.获取语句执行者
PreparedStatement preparedStatement = connection.prepareStatement("insert into student2(name,age,sex) values(?,?,?)");
preparedStatement.setString(1,"王五");
preparedStatement.setInt(2,20);
preparedStatement.setString(3,"女");
int b= preparedStatement.executeUpdate();
System.out.println(b);
}
}
JDBC 事务的处理
Connection 接口中与事务有关的方法 | 说明 |
---|---|
void setAutoCommit(boolean autoCommit) | 参数是 true 或 false 如果设置为 false,表示关闭自动提交,相当于开启事务 |
void commit() | 提交事务 |
void rollback() | 回滚事务 |
public class Transaction {
public static void main(String[] args) throws SQLException {
Connection conn = JdbcUtil.getConn();
PreparedStatement preparedStatement1 = null;
PreparedStatement preparedStatement = null;
try{
conn.setAutoCommit(false);
preparedStatement = conn.prepareStatement("update student2 set age = age - ? where id=?");
preparedStatement.setInt(1,10);
preparedStatement.setInt(2,2);
preparedStatement.executeUpdate();
System.out.println(100/0);
preparedStatement1 = conn.prepareStatement("update student2 set age = age + ? where id=?");
preparedStatement1.setInt(1,10);
preparedStatement1.setInt(2,3);
preparedStatement1.executeUpdate();
conn.commit();
System.out.println("成功");
}catch (Exception e){
e.printStackTrace();
try {
//事务的回滚
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
System.out.println("转账失败");
}finally {
JdbcUtil.closeAll(null,preparedStatement,conn);
JdbcUtil.closeAll(null,preparedStatement1,null);
}
}
}