一、JDBC是什么?
Java DataBase Connectivity(Java语言连接数据库)
二、JDBC的本质是什么?
JDBC是SUN公司制定的一套接口(interface)。
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。
三、为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
四、为什么SUN制定一套JDBC接口呢?
因为每一个数据库产品都有自己独特的实现原理
五、JDBC编程六步(需要背会)
1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
2.获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)。
3.获取数据库操作对象(专门执行sql语句的对象)
4.执行SQL语句(DQL,DML…)
5.处理查询结果集 (只有当第四步执行的是select语句的时候,才有本步)
6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
六、IDEA编写JDBC连接MySQL
6.1 mysql-connector-java的不同版本对比
MySQL Connector / J 5.1 5.1版本支持java5及其以上的版本,支持5.6、5.7、8.0版本的mysql数据库,支持3.0、4.0、4.1、4.2版本的jdbc。在5.1中,Driver的实现类的全路径名是com.mysql.jdbc.Driver。
MySQL Connector / J 8.0 8.0版本支持java8及其以上的版本,支持5.6、5.7、8.0版本的mysql数据库,支持4.2版本的jdbc。在8.0中,Driver的实现类的全路径名是com.mysql.cj.jdbc.Driver。
下图是官网上mysql-connector-java的版本对应的mysql版本和jdk的版本。
6.2 下载驱动jar包 mysql-connector-java
要使用mysql连接器,就要先下载它。如果是一般的项目,那我们需要下载jar包,然后放到项目的lib目录下。如果使用maven构建的项目,我们可以通过maven直接安装。不同的下载方式有不同的操作,常见的有直接官网下载和maven下载(下周讲解maven)。 下载jar包最直接的方式是从官网下载,官网地址是:MySQL :: Download MySQL Connector/J (Archived Versions)。直接点链接进入mysql官网,选择所需的版本和操作系统(要下载jar包就要选:Platform Independent),然后点击download按钮就可以下载了。为java提供的连接器是Connector / J,也就是mysql-connector-java,它分为5.1版本和8.0版本。Connector / J实现了JDBC,为使用java开发的程序提供连接,方便java程序操作数据库。
此处我们使用5.1.49版本,网盘链接也一并贴在这里
2、从maven安装 使用maven安装mysql-connector-java就简单很多,直接打开maven的中央仓库地址,输入mysql-connector-java就可以找到不同版本的依赖。地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java
6.3 IDEA导入jar包
先检查jar包位置:在目录里新建一个文件夹 libs,把jar包复制进去
然后右键选择新建文件夹libs转成library
然后就可以写代码啦!
根据六步编写代码
6.4 JDBC连接mysql 程序编写
这里在数据库中有一个dept表,结构如下:
6.4.1 第一种注册驱动方式
package com.learn;
/*
1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
2.获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,使用完后记得关闭通道)。
3.获取数据库操作对象(专门执行sql语句的对象)
4.执行SQL语句(DQL,DML…)
5.处理查询结果集 (只有当第四步执行的是select语句的时候,才有本步)
6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
*/
import com.mysql.jdbc.Driver;
import java.sql.*;
public class JDBCDemo1 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
//1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
//第一种注册驱动的方式
//jdk本身提供了一个工具类DriverManager来给我们使用
// 其中有一个方法:registerDriver(Driver driver)用于注册驱动
//通过观察发现,jdk本身并没有实现Driver接口的类,但mysql驱动中有实现该接口的驱动类。
try {
DriverManager.registerDriver(new Driver());
//2.获取连接(表示JVM的进程和数据库进程之间的通道被打开了,这属于进程之间的通信,使用完后要关闭通道)
//jdk中提供了一个工具类DriverManager,其中有一个静态的方法,可以让我们调用并且获取与要使用的数据的连接对象
//static Connection getConnection(String url, String user, String password)
//尝试建立与给定数据库URL的连接。
/**
* url: 统一资源定位系统
* http/https 通过网络去请求网络上的资源
* jdbc:mysql 驱动包提供的请求头
* 请求的地址 指定mysql数据库安装的服务器地址:192.168.169.100
* 端口号 3306
* useUnicode=true&characterEncoding=utf8
*
* jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false
*/
// Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&useSSL=false", "root", "123456");
String url = "jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
//如果连接不上可能导致的问题
//1、检查虚拟机,服务器是否启动
//2、检查防火墙是否关闭,指的是虚拟机或者服务器的防火墙
//3、检查url地址,用户名,密码
System.out.println("与数据库<" + conn + ">连接成功!!!");
//3.获取数据库操作对象(专门执行sql语句的对象)
state = conn.createStatement();
//4.执行SQL语句(DQL,DML…)
//DQL
//ResultSet executeQuery(String sql)
//执行sql语句,该语句返回查询的结果,只能是查(select)
//DML
//int executeUpdate(String sql)
//执行给定的SQL语句,这里可以是增(insert),删(delete),更新(update)
//该方法的返回值指的是受影响的行数
String sql1 = "insert into dept values(99,'董事会','安徽合肥')";
int count = state.executeUpdate(sql1);
if (count == 1) {
System.out.println("数据插入成功!");
} else {
System.out.println("数据插入失败,请重试。");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
}
6.4.2 第二种注册驱动方式
使用反射的方式加载驱动类
package com.learn;
//驱动注册的第二种方式
import com.mysql.jdbc.Driver;
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) throws Exception{
//使用反射的第三种方式直接获取mysql数据库的驱动类,今后最常用的方式
Class.forName("com.mysql.jdbc.Driver");
Connection conn = null;
Statement state = null;
//1.注册驱动(告诉Java程序,即将连接的是哪个品牌的数据库)
//第一种注册驱动的方式
//jdk本身提供了一个工具类DriverManager来给我们使用
// 其中有一个方法:registerDriver(Driver driver)用于注册驱动
//通过观察发现,jdk本身并没有实现Driver接口的类,但mysql驱动中有实现该接口的驱动类。
try {
DriverManager.registerDriver(new Driver());
//2.获取连接(表示JVM的进程和数据库进程之间的通道被打开了,这属于进程之间的通信,使用完后要关闭通道)
//jdk中提供了一个工具类DriverManager,其中有一个静态的方法,可以让我们调用并且获取与要使用的数据的连接对象
//static Connection getConnection(String url, String user, String password)
//尝试建立与给定数据库URL的连接。
/**
* url: 统一资源定位系统
* http/https 通过网络去请求网络上的资源
* jdbc:mysql 驱动包提供的请求头
* 请求的地址 指定mysql数据库安装的服务器地址:192.168.169.100
* 端口号 3306
* useUnicode=true&characterEncoding=utf8
*
* jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false
*/
// Connection conn = DriverManager.getConnection("jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&useSSL=false", "root", "123456");
String url = "jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
//如果连接不上可能导致的问题
//1、检查虚拟机,服务器是否启动
//2、检查防火墙是否关闭,指的是虚拟机或者服务器的防火墙
//3、检查url地址,用户名,密码
System.out.println("与数据库<" + conn + ">连接成功!!!");
//3.获取数据库操作对象(专门执行sql语句的对象)
state = conn.createStatement();
//4.执行SQL语句(DQL,DML…)
//DQL
//ResultSet executeQuery(String sql)
//执行sql语句,该语句返回查询的结果,只能是查(select)
//DML
//int executeUpdate(String sql)
//执行给定的SQL语句,这里可以是增(insert),删(delete),更新(update)
//该方法的返回值指的是受影响的行数
// String sql1 = "insert into dept values(98,'保卫科','安徽合肥')";
// int count = state.executeUpdate(sql1);
// if (count == 1) {
// System.out.println("数据插入成功!");
// } else {
// System.out.println("数据插入失败,请重试。");
// }
//修改数据
// String sql2 = "update dept set loc='安徽淮南' where deptno = 98";
// int count2 = state.executeUpdate(sql2);
// if (count2 == 1) {
// System.out.println("数据修改成功!");
// } else {
// System.out.println("数据修改失败,请重试。");
// }
//删除数据
String sql3 = "delete from dept where deptno = 98";
int count3 = state.executeUpdate(sql3);
if (count3 == 1) {
System.out.println("数据删除成功!");
} else {
System.out.println("数据删除失败,请重试。");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//6.释放资源(使用完资源后一定要关闭资源,Java和数据库之间属于进程间的通信,开启之后一定要记得关闭)
if (state != null) {
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
}
//mysql5.7之后警告用户不要直接访问数据库服务器,于是运行时会有红色警告
解决方法:连接数据库时在url中添加参数&useSSL=false
String url = "jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false";
6.4.3 将连接数据库的所有信息配置到配置文件中
为了方便日后修改数据库信息,我们将数据库参数存到配置文件中。
我们另外创建一个配置文件:mysqlinfo.properties
url = jdbc:mysql://192.168.111.11:3306/bigdata23?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456
package com.learn;
import com.mysql.jdbc.Driver;
import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;
/*
通过读取配置文件获取数据库连接信息
*/
public class JDBCDemo3 {
public static void main(String[] args) throws Exception{
//1、加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//读取配置文件获取url,用户名以及密码
Properties prop = new Properties();
prop.load(new BufferedReader(new FileReader("E:\\Project\\IDEAProject\\bigdata23-learn\\bigdata23-jdbc\\src\\main\\java\\com\\learn\\utils\\mysqlinfo.properties")));
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//2、获取数据库连接对象
Connection conn = DriverManager.getConnection(url, username, password);
//3、获取数据库操作对象
Statement state = conn.createStatement();
//4、编写sql语句,执行sql
String sql = "insert into dept values(97,'保洁','安徽淮南')";
int count = state.executeUpdate(sql);
if (count==1){
System.out.println("数据插入成功");
}else {
System.out.println("数据插入失败");
}
//6、释放资源
state.close();
conn.close();
}
}
使用工具类改进获取连接代码
package com.learn.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
public class MysqlTool {
private MysqlTool(){}
public static Connection getConnection(){
Connection conn = null;
try{
//1、加载数据库的驱动
Class.forName("com.mysql.jdbc.Driver");
//读取配置文件获取url,用户名和密码
Properties prop = new Properties();
prop.load(new BufferedReader(new FileReader("E:\\Project\\IDEAProject\\bigdata23-learn\\bigdata23-jdbc\\src\\main\\java\\com\\learn\\utils\\mysqlinfo.properties")));
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//2、获取数据库的连接对象
conn = DriverManager.getConnection(url, username, password);
}catch (Exception e){
e.printStackTrace();
}
if (conn!=null){
System.out.println("与数据库建立连接成功");
}else {
System.out.println("建立连接失败,请重试!");
}
return conn;
}
}
6.4.4 处理查询结果集
package com.learn;
import com.learn.utils.MysqlTool;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCDemo4 {
public static void main(String[] args) {
Connection conn = null;
Statement state = null;
try{
//获取连接对象
conn = MysqlTool.getConnection();
//获取数据库操作对象
state = conn.createStatement();
//查询
String sql = "select deptno,dname,loc from dept";
ResultSet rs = state.executeQuery(sql);
while (rs.next()){
//第一种方式:通过索引获取列(索引下标从1开始)
// String deptno = rs.getString(1);
// String dname = rs.getString(2);
// String loc = rs.getString(3);
//第二张方式:通过列名来获取
//如果查询语句中有别名的列存在,第二种方式查询的时候,使用别名获取这一列
String deptno = rs.getString("deptno");
String dname = rs.getString("dname");
String loc = rs.getString("loc");
System.out.println("部门编号:"+deptno+",部门名称:"+dname+",地址:"+loc);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (state!=null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
登录注册案例
package com.learn;
import com.learn.utils.MysqlTool;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import java.util.UUID;
public class JDBCDemo5 {
public static void main(String[] args) throws SQLException {
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("====================欢迎来到美联储后台管理系统!=====================");
System.out.println("请输入您的用户名:");
String username = sc.nextLine();
System.out.println("请输入您的密码:");
String pwd = sc.nextLine();
//查询数据库中的users表,判断该用户是否存在
//获取数据库的连接对象
Connection conn = MysqlTool.getConnection();
//获取数据库操作对象
Statement state = conn.createStatement();
//编写sql查询语句
String sql = "select username,password from users where username='"+username+"'and password='"+pwd+"'";
//执行sql语句
ResultSet rs = state.executeQuery(sql);
boolean flag = rs.next();
if (flag){
System.out.println("登陆成功,您的余额为36415481518.25美元,祝您生活愉快!");
}else {
System.out.println("登陆失败,是否注册?(Y/N)");
String s = sc.nextLine();
if ("Y".equals(s)){
System.out.println("请输入您的姓名:");
String name =sc.nextLine();
System.out.println("请输入您的用户名:");
String uname =sc.nextLine();
System.out.println("请输入您的密码:");
String password =sc.nextLine();
System.out.println("请确认您的密码:");
String password2 =sc.nextLine();
if (password.equals(password2)){
//开始进行注册(即把数据插入到数据库中)
//Java中提供一个类用于生成随机id:UUID
UUID uuid = UUID.randomUUID();
String id = uuid.toString();
String sql2 = "insert into users values('"+id+"','"+name+"','"+uname+"','"+password+"')";
// System.out.println(sql2);
int count = state.executeUpdate(sql2);
if (count==1){
System.out.println("注册成功,系统随机赠送您36415481518.25美元初始资金,祝您生活愉快!");
}else {
System.out.println("系统繁忙,请您稍后再试!");
}
}
}else {
System.out.println("欢迎下次光临。");
}
}
// System.out.println(sql);
conn.close();
}
}
可以实现简单的注册登录。
6.4.5 解决sql注入问题
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
在上面案例中,我们查询登录语句是
String sql = "select username,password from users where username='"+username+"'and password='"+pwd+"'";
此时我们登录,应该是用户名为president,密码为666
然而读者观察以下结果:
我们神奇的发现,输入的密码并不是666,竟然也能登陆成功,这就是SQL注入。
此时我们再将登陆语句拿过来
select username,password from users where username='"+username+"'and password='"+pwd+"'
我们将输入的内容拼接上去
select username,password from users where username='president'and password='123' or '1'
此时语句最后变成了password='123' or '1',而'1'是个字符串,显然恒为true,导致password='123' or '1'也为true,于是就登陆进了系统
我们再换种方式:
同理:
select username,password from users where username='president'and password='123' or '1'='1'
如果我们是先进行sql拼接,然后再对sql整体做编译的话,会将一些拼接的内容作为sql语法关键字进行处理。但是我们实际上输入的字符就是普通的字符串,不应该当作sql语法的编译。 于是java和mysql团队提供了另一种方式获取数据库操作对象。 解决方案:将原来的先拼接再编译sql的步骤换成:先编译再拼接。
//提前编译好sql,将需要传参数的地方使用问号进行处理 PreparedStatement state = conn.prepareStatement("select name,password from users where name=? and password=?"); state.setString(1,username); state.setString(2,pwd);
改进后的代码如下:
package com.learn;
import com.learn.utils.MysqlTool;
import java.sql.*;
import java.util.Scanner;
import java.util.UUID;
public class JDBCDemo5_2 {
public static void main(String[] args) throws SQLException {
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
System.out.println("====================欢迎来到美联储后台管理系统!=====================");
System.out.println("请输入您的用户名:");
String username = sc.nextLine();
System.out.println("请输入您的密码:");
String pwd = sc.nextLine();
//查询数据库中的users表,判断该用户是否存在
//获取数据库的连接对象
Connection conn = MysqlTool.getConnection();
//获取数据库操作对象
// Statement state = conn.createStatement();
// //编写sql查询语句
// String sql = "select username,password from users where username='"+username+"'and password='"+pwd+"'";
//提供了预编译对象进行处理
//提前编译好sql,将需要传参数的地方使用问号进行处理
PreparedStatement state = conn.prepareStatement("select name,password from users where name=? and password=?");
state.setString(1,username);
state.setString(2,pwd);
//执行sql语句
ResultSet rs = state.executeQuery();
boolean flag = rs.next();
if (flag){
System.out.println("登陆成功,您的余额为36415481518.25美元,祝您生活愉快!");
}else {
System.out.println("登陆失败,是否注册?(Y/N)");
String s = sc.nextLine();
if ("Y".equals(s)){
System.out.println("请输入您的姓名:");
String name =sc.nextLine();
System.out.println("请输入您的用户名:");
String uname =sc.nextLine();
System.out.println("请输入您的密码:");
String password =sc.nextLine();
System.out.println("请确认您的密码:");
String password2 =sc.nextLine();
if (password.equals(password2)){
//开始进行注册(即把数据插入到数据库中)
//Java中提供一个类用于生成随机id:UUID
UUID uuid = UUID.randomUUID();
String id = uuid.toString();
String sql2 = "insert into users values('"+id+"','"+name+"','"+uname+"','"+password+"')";
// System.out.println(sql2);
int count = state.executeUpdate(sql2);
if (count==1){
System.out.println("注册成功,系统随机赠送您36415481518.25美元初始资金,祝您生活愉快!");
}else {
System.out.println("系统繁忙,请您稍后再试!");
}
}
}else {
System.out.println("欢迎下次光临。");
}
}
// System.out.println(sql);
conn.close();
}
}
测试:
发现已经无法使用原来的漏洞登录了。
JDBC事物机制: 1.JDBC中的事务自动提交的,什么是自动提交? 只要执行任意一条 DML
语句,则自动提交一次。这是JDBC默认的事务行为。 但是在实际的业务中,通常都是N条DML语句共同联合才能完成,必须 保证这些DML语句在同一个事务中同时成功或者同时失败 解决方案:三行重要的代码 conn.setAutoCommit(false);//手动提交事务 conn.commit();//提交事务 conn.rooback;当发生异常时或者程序错误时,进行回滚。
6.4.6 JDBC实现模糊查询
package com.learn;
import com.learn.utils.MysqlTool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCDemo6 {
public static void main(String[] args) throws Exception {
//获取数据库连接对象
Connection conn = MysqlTool.getConnection();
//获取预编译数据库操作对象
PreparedStatement pps = conn.prepareStatement("select id,name,password from users where name like ?");
pps.setString(1,"川%");
ResultSet resultSet = pps.executeQuery();
while (resultSet.next()){
String name = resultSet.getString(1);
String password = resultSet.getString(2);
System.out.println(name+"--"+password);
}
pps.close();
conn.close();
}
}
6.5 悲观锁和乐观锁的概念
事务1–>读取到版本号1.1 事务2—>读取到版本号1.1
其中事务1先修改了,修改之后看了版本号是1.1 ,于是提交修改的数据,将版本号修改为1.2 其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2 ,和它最初读的版本号不一致。回滚。
悲观锁:事务必须排队执行。数据锁住了,不允许并发。 (行级锁: select后面添加for update ) 乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。