目录
1 JDBC和数据库驱动程序
- Java提供访问数据库规范称为JDBC;
- 生产厂商提供规范的实现类称为驱动;
- JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库。
- 每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生产厂商提供。
2 准备数据
2.1 创建数据库和表结构
#创建数据库
create database mybase;
#使用数据库
use mybase;
#创建分类表
create table sort(
sid int PRIMARY KEY AUTO_INCREMENT,
sname varchar(100),
sprice DOUBLE,
sdesc VARCHAR(500)
);
2.2 向表中插入数据
#初始化数据
insert into sort(sname,sprice,sdesc) values('家电',2000, '优惠的促销');
insert into sort(sname,sprice,sdesc) values('家具',8900, '家具价格上调,原材料涨价');
insert into sort(sname,sprice,sdesc) values('儿童玩具',290, '赚家长的钱');
insert into sort(sname,sprice,sdesc) values('生鲜',500.99, '生鲜商品');
insert into sort(sname,sprice,sdesc) values('服装',24000, '换季销售');
insert into sort(sname,sprice,sdesc) values('洗涤',50, '洗发水促销');
3 JDBC的开发步骤
- 注册驱动
告知JVM使用的是哪一个数据库的驱动 - 获得连接
使用JDBC中的类,完成对MySQL数据库的连接 - 获得语句执行平台
通过连接对象获取对SQL语句的执行者对象 - 执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果 - 处理结果
- 释放资源
close()
3.1 导入数据库驱动程序jar包
- 创建lib目录,用于存放当前项目需要的所有jar包;
- 选择jar包,放到lib目录下,这里我们选择mysql-connector-java-8.0.16;
3.2 注册数据库驱动
public class JDBCDemo {
public static void main(String[] args)throws ClassNotFoundException,SQLException{
//1 注册驱动,利用反射技术将驱动类加入到内容
Class.forName("com.mysql.cj.jdbc.Driver");
}
}
3.3 获取数据库连接对象
public class JDBCDemo {
public static void main(String[] args)throws ClassNotFoundException,SQLException{
//1 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获得数据库连接,利用DriverManager类中的静态方法getConnection
//public static Connection getConnection(String url, String user, String password)
//url: 数据库地址 jdbc:mysql://连接主机IP:端口号//数据库名字
//用户名和密码自己设置
//返回值是Connection接口的实现类对象,即数据库连接对象
//MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。
//allowPublicKeyRetrieval=true 允许客户端从服务器获取公钥。
//最后还需要设置 CST。
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username="root";
String password="123456";
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
}
}
3.4 获取SQL语句的执行对象
public class JDBCDemo {
public static void main(String[] args)throws ClassNotFoundException,SQLException{
//1 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2 获取连接对象
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username="root";
String password="123456";
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
//3 通过数据库连接对象,获取到SQL语句的执行对象
//con对象调用方法Statement createStatement(),返回值是Statement接口的实现类对象
Statement stat = con.createStatement();
System.out.println(stat);
}
}
3.5 执行insert语句获取结果
public class JDBCDemo {
public static void main(String[] args)throws ClassNotFoundException,SQLException{
//1 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2 获取连接对象
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username="root";
String password="123456";
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
//3 获取执行SQL语句的对象
Statement stat = con.createStatement();
System.out.println(stat);
//4 执行sql语句
//通过执行对象调用方法执行SQL语句,获取结果
//int executeUpdate(String sql) ,返回值int,操作成功数据表多少行
int row = stat.executeUpdate("INSERT INTO sort(sname,sprice,sdesc) VALUES('汽车用品',50000,'疯狂涨价')");
System.out.println(row);
//5.释放资源
stat.close();
con.close();
}
}
3.6 执行select语句获取结果集
public class JDBCDemo1 {
public static void main(String[] args) throws Exception{
//1 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2 获取连接对象
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username="root";
String password="123456";
Connection con = DriverManager.getConnection(url, username, password);
//3 获取执行SQL语句的对象
Statement stat = con.createStatement();
//4 调用执行者对象方法,执行SQL语句获取结果集
// ResultSet executeQuery(String sql),返回值是ResultSet接口的实现类对象
String sql = "SELECT * FROM sort";
ResultSet rs = stat.executeQuery(sql);
//5 处理结果集
//ResultSet接口方法boolean next():返回true有结果集,返回false没有结果集
while(rs.next()){
//获取每列数据,使用是ResultSet接口的方法 getXX方法参数中,建议写String列名
System.out.println(rs.getInt("sid")+" "+rs.getString("sname")+ " "+rs.getDouble("sprice")+" "+rs.getString("sdesc"));
}
//6 释放资源
//与IO流一样,使用后的东西都需要关闭
//关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close();
stat.close();
con.close();
}
}
3.7 处理结果集(执行insert、update、delete无需处理)
ResultSet实际上就是一张二维的表格,我们可以调用其boolean next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int col)方法(与索引从0开始不同,列从1开始)来获取指定列的数据:
rs.next();//指向第一行
rs.getInt(1);//获取第一行第一列的数据
常用方法:
- Object getObject(int index) / Object getObject(String name) 获得任意对象
- String getString(int index) / Object getObject(String name) 获得字符串
- int getInt(int index) / Object getObject(String name) 获得整型
- double getDouble(int index) / Object getObject(String name) 获得双精度浮点型
4 SQL注入攻击
4.1 SQL注入攻击语句
假设有登录案例SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:'XXX’ OR ‘a’=’a时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = 'XXX' AND PASSWORD ='XXX' OR 'a'='a';
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
案例演示
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100),
PASSWORD VARCHAR(100)
);
INSERT INTO users (username,PASSWORD) VALUES ('a','1'),('b','2');
SELECT * FROM users;
// SQL注入攻击
SELECT * FROM users WHERE username='dsfsdfd' AND PASSWORD='wrethiyu' OR 'a'='a';
4.2 SQL注入攻击用户登录案例
public class JDBCDemo {
public static void main(String[] args)throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username = "root";
String password = "123456";
Connection con = DriverManager.getConnection(url, username, password);
Statement stat = con.createStatement();
Scanner sc = new Scanner(System.in);
//String user = sc.nextLine();
//String pass = sc.nextLine();
//执行SQL语句,数据表,查询用户名和密码,如果存在,登录成功,不存在登录失败
String sql = "SELECT * FROM users WHERE username='dsfsdfd' AND PASSWORD='wrethiyu' OR 1=1";
//String sql = "SELECT * FROM users WHERE username='"+user+"' AND PASSWORD='"+pass+"'";
System.out.println(sql);
ResultSet rs = stat.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
stat.close();
con.close();
}
}
SELECT * FROM users WHERE username='dsfsdfd' AND PASSWORD='wrethiyu' OR 1=1
a 1
b 2
为此,我们使用PreparedStatement来解决对应的问题。
5 PrepareStatement接口预编译SQL语句
5.1 获取预处理对象
// SQL语句中的参数全部采用问号占位符
String sql = "insert into sort(sid,sname) values(?,?)";
PreparedStatement pst = conn.prepareStatement(sql)
5.2 执行SQL语句的方法介绍
int executeUpdate()
:执行insert update delete语句ResultSet executeQuery()
:执行select语句boolean execute()
:执行select返回true,执行其他的语句返回false
5.3 设置实际参数
void setXxx(int index, Xxx xx)
:将指定参数设置为xx。
例:
//把SQL语句中第2个位置的占位符?替换成实际参数 "家用电器"。
setString(2, "家用电器");
5.4 演示:PrepareStatement接口预编译SQL语句执行查询
public class JDBCDemo3 {
public static void main(String[] args)throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username = "root";
String password = "123456";
Connection con = DriverManager.getConnection(url, username, password);
//设计sql查询语句
String sql = "SELECT * FROM users WHERE username=? AND PASSWORD=?";
//调用Connection接口的方法prepareStatement,获取PrepareStatement接口的实现类
PreparedStatement pst = con.prepareStatement(sql);
System.out.println(pst);
// 由终端输入用户名和密码
Scanner sc = new Scanner(System.in);
String user = sc.nextLine();
String pass = sc.nextLine();
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1, user);
pst.setObject(2, pass);
//调用方法,执行SQL,获取结果集
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
rs.close();
pst.close();
con.close();
}
}
a
1
com.mysql.cj.jdbc.ClientPreparedStatement: SELECT * FROM users WHERE username=** NOT SPECIFIED ** AND PASSWORD=** NOT SPECIFIED **
a 1
6 编写JDBC获得数据库连接类
“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
该工具类提供方法:public static Connection getConn ()。
代码如下:
public class JDBCUtils {
private static Connection con;
static{
try{
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mybase?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String username = "root";
String password = "123456";
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException(ex+"数据库连接失败");
}
}
/*
* 定义静态方法,返回数据库的连接对象
*/
public static Connection getConnection(){
return con;
}
public static void close(Connection con,Statement stat){
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
public static void close(Connection con,Statement stat , ResultSet rs){
if(rs!=null){
try{
rs.close();
}catch(SQLException ex){}
}
if(stat!=null){
try{
stat.close();
}catch(SQLException ex){}
}
if(con!=null){
try{
con.close();
}catch(SQLException ex){}
}
}
}
//测试JDBCUtils工具类的代码
public class TestJDBCUtils {
public static void main(String[] args)throws Exception {
Scanner sc = new Scanner(System.in);
String user = sc.nextLine();
String pass = sc.nextLine();
Connection con = JDBCUtils.getConnection();
PreparedStatement pst = con.prepareStatement("SELECT * FROM users WHERE username=? AND PASSWORD=?");
//调用pst对象set方法,设置问号占位符上的参数
pst.setObject(1, user);
pst.setObject(2, pass);
ResultSet rs = pst.executeQuery();
while(rs.next()){
System.out.println(rs.getString("username")+" "+rs.getString("password"));
}
JDBCUtils.close(con, pst, rs);
}
}
a
1
a 1
7 编写数据表数据的存储类
定义实体类Sort:
public class Sort {
private int sid;
private String sname;
public Sort(int sid, String sname) {
this.sid = sid;
this.sname = sname;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Sort [sid=" + sid + ", sname=" + sname + "]";
}
}
/*
* JDBC读取数据表sort,每行数据封装到Sort类的对象中
* 很多个Sort类对象,存储到List集合中
*/
public class JDBCDemo {
public static void main(String[] args) throws Exception{
//使用JDBC工具类,直接获取数据库连接对象
Connection con = JDBCUtils.getConnection();
//连接获取数据库SQL语句执行者对象
PreparedStatement pst = con.prepareStatement("SELECT * FROM sort");
//调用查询方法,获取结果集
ResultSet rs = pst.executeQuery();
//创建集合对象
List<Sort> list = new ArrayList<Sort>();
while(rs.next()){
//获取到每个列数据,封装到Sort对象中
Sort s = new Sort(rs.getInt("sid"),rs.getString("sname"));
//封装的Sort对象,存储到集合中
list.add(s);
}
JDBCUtils.close(con, pst, rs);
//遍历List集合
for(Sort s : list){
System.out.println(s);
}
}
}
Sort [sid=1, sname=家电]
Sort [sid=2, sname=服饰]
Sort [sid=3, sname=化妆品]
8 properties配置文件
8.1 简介
开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
通常情况下,我们习惯使用properties文件,此文件我们将做如下要求:
- 文件位置:任意,建议src下
- 文件名称:任意,扩展名为properties
- 文件内容:一行一组数据,格式是“key=value”
8.2 properties文件的创建和编写
- properties文件的创建
src路径下建立database.properties(其实就是一个文本文件)
- properties文件的编写
内容如下:
driverClass=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybase useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
username=root
password=123456
8.3 加载配置文件并连接数据库
/*
* 加载properties配置文件
* IO读取文件,键值对存储到集合
* 从集合中以键值对方式获取数据库的连接信息,完成数据库的连接
*/
public class PropertiesDemo {
public static void main(String[] args) throws Exception{
//使用类的加载器
InputStream in = PropertiesDemo.class.getClassLoader().getResourceAsStream("database.properties");
System.out.println(in);
Properties pro = new Properties();
pro.load(in);
//获取集合中的键值对
String driverClass=pro.getProperty("driverClass");
String url = pro.getProperty("url");
String username = pro.getProperty("username");
String password = pro.getProperty("password");
Class.forName(driverClass);
Connection con = DriverManager.getConnection(url, username, password);
System.out.println(con);
}
}
java.io.FileInputStream@74a14482
java.io.BufferedInputStream@1540e19d
com.mysql.cj.jdbc.ConnectionImpl@3f2a3a5
8.4 编写读取配置文件的工具类
/*
* 编写数据库连接的工具类,JDBC工具类
* 获取连接对象采用读取配置文件方式
* 读取文件获取连接,执行一次,static{}
*/
public class JDBCUtilsConfig {
private static Connection con ;
private static String driverClass;
private static String url;
private static String username;
private static String password;
static{
try{
readConfig();
Class.forName(driverClass);
con = DriverManager.getConnection(url, username, password);
}catch(Exception ex){
throw new RuntimeException("数据库连接失败");
}
}
private static void readConfig()throws Exception{
InputStream in = JDBCUtilsConfig.class.getClassLoader().getResourceAsStream("database.properties");
Properties pro = new Properties();
pro.load(in);
driverClass = pro.getProperty("driverClass");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
}
public static Connection getConnection(){
return con;
}
}
public class TestJDBCUtils {
public static void main(String[] args) {
Connection con = JDBCUtilsConfig.getConnection();
System.out.println(con);
}
}
com.mysql.cj.jdbc.ConnectionImpl@60addb54