文章目录
数据库的访问过程如下:
客户端与Mysql服务器之间建立连接
客户端向Mysql服务器发送数据库请求
Mysql服务器处理客户端请求,并返回结果给客户端
客户端接受Mysql服务器的响应,并按照自己的业务逻辑做响应处理。
释放相关资源
1 JDBC入门(Java Database Connection:Java数据库连接)
1.1 客户端操作MySQL数据库的方式
1.1.1什么是JDBC
JDBC规范定义接口,具体的实现由各大数据库厂商来实现。
JDBC是Java访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用JDBC接口中的方法即可,数据库驱动由数据库厂商提供。
Java帮助我们制定的与数据库之间建立连接
Java操作数据库的规范
1.1.2 使用JDBC开发使用到的包
1.2 JDBC的核心API
1.3 导入驱动jar包
1.4 加载和注册驱动
1.5 如何使用JDBC(0324 AM)
1.5.1 第一个JDBC程序
1.5.1.1 搭建环境
如何导入数据库的驱动包
1.在我们项目的根目录下建立一个文件夹,起名lib
2.把我们的数据库驱动程序放到lib文件夹中
3.把我们的文件夹标记为library
成功否?
对于我们的项目来说,导包了有什么作用呢?
导包的目的是使用别人给我们写好的一些方法或者接口
搭建环境
获取
1.5.1.2 编写程序
demo1:基础写法
import java.sql.*;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 14:44
*/
public class JDBCDemo1 {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/db0323?useSSL=false";
String user = "root";
String password = "123456";
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//注册驱动(可以省略 -SPI技术)
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//获取连接
connection = DriverManager.getConnection(url, user, password);
//获取statement对象,发送sql语句
statement = connection.createStatement();
//执行sql语句,获取返回结果集
resultSet = statement.executeQuery("select * from graduate1");
//解析返回的结果集,获取我们想要的数据
//这个结果集是一个类似于游标的结构
resultSet.next();
String name = resultSet.getString("name");
System.out.println("name:" + name);
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//关闭资源 connection statement resultSet
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
demo2:解析结果集优化
public class graduate1 {
private Integer id;
private String name;
@Override
public String toString() {
return "Clazz{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//解析返回的结果集,获取我们想要的数据
//这个结果集是一个类似于游标的结构
ArrayList<graduate1> list = new ArrayList<>();
while (resultSet.next()){
graduate1 graduate1 = new graduate1();
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
graduate1.setId(id);
graduate1.setName(name);
list.add(graduate1);
}
System.out.println("list: " + list);
// resultSet.next();
//
// String name = resultSet.getString("name");
// System.out.println("name:" + name);
demo3:使用配置文件
啊啊啊!!!
错误的配置文件
url="jdbc:mysql://localhost:3306/db0323?useSSL=false"
user=root
password=123456
把连接、用户名、密码都配置到配置文件中
创建一个jdbc.properties配置文件
配置文件
url=jdbc:mysql://localhost:3306/db0323?useSSL=false
user=root
password=123456
//创建properties配置文件
Properties properties = new Properties();
try {
//读取文件
FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
//装载
properties.load(fileInputStream);
} catch (IOException e) {
e.printStackTrace();
}
String url = properties.getProperty("url");
String user = properties.getProperty("user");
// System.out.println(user);
String password = properties.getProperty("password");
// System.out.println(password);
demo4:通过反射去加载Driver,完成驱动程序的注册
让类加载执行静态代码块
通过反射加载这个类
这么做的好处:帮助我们解耦
写到配置文件中去
配置文件中增加
driverClass=com.mysql.jdbc.Driver
获取
String driverClass = properties.getProperty("driverClass");
//注册驱动(可以省略 -SPI技术)
//这种做法其实不太好,假如我们数据库的驱动要换的话(比如换成Oralce等),那么就需要我们来修改这个代码
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 这么做的好处是可以帮助我们解耦
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
demo5:重复代码,写成工具类,代码重复性没了
最终的完善版本
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.Properties;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 14:44
*/
public class JDBCDemo5 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//获取连接
connection = JDBCUtils.getConnection();
//获取statement对象,发送sql语句
statement = connection.createStatement();
//执行sql语句,获取返回结果集
resultSet = statement.executeQuery("select * from graduate1");
//解析返回的结果集,获取我们想要的数据
//这个结果集是一个类似于游标的结构
ArrayList<graduate1> list = new ArrayList<>();
while (resultSet.next()) {
graduate1 graduate1 = new graduate1();
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
graduate1.setId(id);
graduate1.setName(name);
list.add(graduate1);
}
System.out.println("list: " + list);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeSorce(resultSet, statement, connection);
}
}
}
工具类JDBCUtils
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 16:24
*/
public class JDBCUtils {
/**
* 获取连接
*
* @return
* @throws ClassNotFoundException
* @throws SQLException
*/
public static Connection getConnection() throws ClassNotFoundException, SQLException {
//创建properties配置文件
Properties properties = new Properties();
try {
//读取文件
FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
//装载
properties.load(fileInputStream);
} catch (IOException e) {
e.printStackTrace();
}
String url = properties.getProperty("url");
String user = properties.getProperty("user");
// System.out.println(user);
String password = properties.getProperty("password");
// System.out.println(password);
String driverClass = properties.getProperty("driverClass");
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
//注册驱动(可以省略 -SPI技术)
//这种做法其实不太好,假如我们数据库的驱动要换的话(比如换成Oralce等),那么就需要我们来修改这个代码
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 这么做的好处是可以帮助我们解耦
Class.forName(driverClass);
//获取连接
connection = DriverManager.getConnection(url, user, password);
return connection;
}
public static void closeSorce(ResultSet resultSet, Statement statement, Connection connection) {
//关闭资源 connection statement resultSet
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
2 DriverManager类
2.1 DriverManager作用
1)管理和注册驱动
2)创建数据库的连接
2.2 类中的方法
2.3 使用JDBC连接数据库的四个参数
2.4 连接数据库的URL地址格式
MySQL中可以简写:
前提:必须是本地服务器,端口号是3306
如果数据库出现乱码,可以指定参数: ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数据。
jdbc:mysql://localhost:3306/数据库?characterEncoding=utf8
2.5 案例:得到MySQL的数据库连接对象
3 Connection接口:客户端与数据库所有交互都是通过connection对象完成的
它用于代表数据库的连接(桥梁), Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的
3.1 Connection作用
Connection接口,具体的实现类由数据库的厂商实现,代表一个连接对象。
3.3 Connection方法
4 Statement:Statement对象用于向数据库发送SQL语句
4.1 JDBC访问数据库的步骤
1.注册和加载驱动(可以省略)
2.获取连接
3.Connection获取Statement对象
4.使用Statement对象执行SQL语句
5.返回结果集
6.释放资源
4.2 Statement作用
代表一条语句对象,用于发送SQL语句给服务器,用于执行静态 SQL 语句并返回它所生成结果的对象。
4.3 Statement中的方法
4.4 释放资源
需要释放的对象:ResultSet结果集,Statement语句,Connection连接
释放原则:先开的后关,后开的先关。ResultSet --> Statement --> Connection
放在哪个代码块中:finally块
4.5 执行DDL(数据定义语言:建表,建库等)操作
需求:使用JDBC在MySQL的数据库中创建一张StudentTest2 学生表
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 17:24
*/
public class JDBCDemo1 {
public static void main(String[] args) {
//1.创建连接
//Connection接口:客户端与数据库所有交互都是通过connection对象完成的
Statement statement = null;
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db0323", "root", "123456");
//2.创建向数据库发送sql的statement对象
statement = connection.createStatement();
//3.通过statement对象发送SQL语句给服务器
//4.执行SQL
int i = statement.executeUpdate("create table StudentTest2 (id int PRIMARY key auto_increment, " +
"name varchar(20) not null, gender boolean, birthday date)");
//System.out.println(i);
//返回影响行数(DDL没有返回值)
System.out.println("创建表成功");
} catch (SQLException e) {
e.printStackTrace();
}
//释放资源
finally {
//关闭之前要先判断
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
4.6 执行DML(数据操纵语言)操作
需求:向学生表中添加4条记录,主键是自动增长
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 17:45
*/
public class JDBC2DML {
public static void main(String[] args) throws SQLException {
// 1) 创建连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql:///db0323?characterEncoding=utf8", "root", "123456");
// 2) 创建Statement语句对象
Statement statement = connection.createStatement();
// 3) 执行SQL语句:executeUpdate(sql)
int count = 0;
// 4) 返回影响的行数
count += statement.executeUpdate("insert into StudentTest2 values(null, '孙悟空', 1, '1993-03-24')");
count += statement.executeUpdate("insert into StudentTest2 values(null, '白骨精', 0, '1995-03-24')");
count += statement.executeUpdate("insert into StudentTest2 values(null, '猪八戒', 1, '1903-03-24')");
count += statement.executeUpdate("insert into StudentTest2 values(null, '嫦娥', 0, '1993-03-11')");
System.out.println("插入了" + count + "条记录");
// 5) 释放资源
statement.close();
connection.close();
}
}
4.7 执行DQL(数据查询语言)操作
4.7.1 ResultSet接口:
作用:封装数据库查询的结果集,对结果集进行遍历,取出每一条记录。
4.7.2 常用数据类型转换表
java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
5 数据库工具类JdbcUtils
5.3 案例:用户登陆
1)有一张用户表
2)添加几条用户记录
create table user324 (
id int primary key auto_increment,
name varchar(20),
password varchar(20)
)
insert into user324 values (null,'jack','123'),(null,'rose','456');
SELECT * FROM USER324
-- 登录, SQL中大小写不敏感
select * from user324 where name='JACK' and password='123';
-- 登录失败
select * from user324 where name='JACK' and password='333';
3)使用Statement字符串拼接的方式实现用户的登录, 用户在控制台上输入用户名和密码。
步骤:
1)得到用户从控制台上输入的用户名和密码来查询数据库
2)写一个登录的方法
a)通过工具类得到连接
b)创建语句对象,使用拼接字符串的方式生成SQL语句
c)查询数据库,如果有记录则表示登录成功,否则登录失败
d)释放资源
package test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
import java.util.Scanner;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 19:25
*/
public class JDBALogin {
//从控制台上输入的用户名和密码
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) {
//通过工具类得到连接
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
//创建语句对象,使用拼接字符串的方式生成SQL语句
statement = connection.createStatement();
//查询数据库,如果有记录则表示登录成功,否则登录失败
String sql = "select * from user324 where name='" + name + "' and password='" + password + "'";
System.out.println(sql);
resultSet = statement.executeQuery(sql);
if (resultSet.next()) {
System.out.println("登录成功,欢迎您:" + name);
} else {
System.out.println("登录失败");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
//d) 释放资源
JDBCUtils.closeSorce(resultSet, statement, connection);
}
}
}
5.4 SQL注入问题
当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了
请输入用户名:
newboy
请输入密码:
a' or '1'='1
select * from user where name='newboy' and password='a' or '1'='1'
登录成功,欢迎您:newboy
6 PreparedStatement接口(开发中使用的是这个,而非Statement)
仔细分析一下,数据库注入成功的根本原因是,我们把sql语句中的参数(用户的输入)和sql命令拼接成了一个sql语句,因为一个sql语句中既可以有sql的命令又可以有参数,因此,用户的输入也可以被当做sql的语句来解析执行。
那么,既然知道了sql注入成功的原因,我们就反其道而行之,不让用户输入的参数被当做sql命令解析,而是只把它当做普通字符串来解析。
由此,java中引入了prepareStatement,利用preparestatement来防止sql注入的核心思想就是,不把用户的输入当做sql命令来解析和执行。
6.1 继承结构与作用:
PreparedStatement继承自Statement,可以通过Connection的prepareStatement方法得到。
6.2 PreparedSatement的执行原理
prepareStatement很明显的将sql命令语句与参数分开处理,其执行过程是,首先在sql语句真正执行之前,先把sql命令送到数据库中进行预编译,
生成相应 的数据库命令,然后在获取sql中的参数,然后真正执行该sql语句。
这样一来,用户输入的参数,只被当做参数而非命令来解析,就可以避免数据库注入这样的问题发生。
不过,这样一来,单次执行PreparedStatement需要与数据库通信两次,效率,比之于单词执行Statement要低。
6.3 Connection创建PreparedStatement对象
6.4 PreparedStatement接口中的方法
6.5 PreparedSatement的好处
1.prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。可以多次传入不同的参数
给PreparedStatement对象并执行。减少SQL编译次数,提高效率。
2.安全性更高,没有SQL注入的隐患。
3.提高了程序的可读性
6.6 使用PreparedStatement的步骤
- 编写SQL语句,未知内容使用?占位:“SELECT * FROM user WHERE name=? AND password=?”;
- 获得PreparedStatement对象
- 设置实际参数:setXxx(占位符的位置, 真实的值)
- 执行参数化SQL语句
- 关闭资源
使用PreparedStatement改写上面的登录程序,看有没有SQL注入的情况
private static void login(String name, String password) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
//写成登录SQL语句,没有单引号
String sql = "select * from user324 where name=? and password=?";
//得到语句对象
PreparedStatement ps_statement = connection.prepareStatement(sql);
//设置参数
ps_statement.setString(1, name);
ps_statement.setString(2,password);
ResultSet resultSet = ps_statement.executeQuery();
if (((ResultSet) resultSet).next()) {
System.out.println("登录成功:" + name);
}
else {
System.out.println("登录失败");
}
//释放资源,子接口直接给父接口
JDBCUtils.closeSorce(resultSet,ps_statement,connection);
}
6.7 表与类的关系
6.7.1 案例:使用PreparedStatement查询一条数据,封装成一个学生Student对象。循环输出每条数据
package test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @AUTHOR ZHANG
* @data 2021/3/24 21:31
*/
public class Demo9List {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
//创建一个集合
List<Student> students = new ArrayList<>();
Connection connection = JDBCUtils.getConnection();
PreparedStatement ps_statement = connection.prepareStatement("select * from studenttest2");
//没有参数替换
ResultSet resultSet = ps_statement.executeQuery();
while (resultSet.next()) {
//每次循环是一个学生对象
Student student = new Student();
//封装成一个学生对象
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setGender(resultSet.getBoolean("gender"));
student.setBirthday(resultSet.getDate("birthday"));
//把数据放到集合中
students.add(student);
}
//关闭连接
JDBCUtils.closeSorce(resultSet, ps_statement, connection);
//使用数据
for (Student stu : students) {
System.out.println(stu);
}
}
}