1、JDBC是什么
Java Database Connectivity (java语言连接数据库)
2、JDBC本质是什么
是sun公司制定的一套接口
java.sql.* ;(这个软件包下有许多的接口)
接口都有调用者和实现者
面向接口调用,面向接口写实现类,这都属于面向接口编程
为什么要面向接口编程?
解耦合,降低程序的耦合度,提高程序的扩展力
多态机制就是典型的面向抽象编程(不要面向具体编程)
思考:为什么要制定JDBC接口
因为每隔数据库的底层实现原理不一样。每个数据库产品都有自己的实现原理
实现者:数据库厂家
调用者:程序员
sun公司指定了接口,实现者和 调用者都要遵循这套接口规范
3、JDBC开发前的准备工作
首先下载对应的jar包,然后将其配置到环境变量中
classpath=.;jar路径
这里一定要加.;否则以后类加载器都会到jar路径下去找类
idea不需要配置这些,以上的配置是针对文本编辑器的
4、JDBC编程六步(需要背会)
第一步:注册驱动(作用:告诉JVM,即将要连接的数据库是哪个品牌的数据库)
第二步:获取连接(标识JVM和数据库进程之间的通道打开了,这属于进程之间的通信)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行sql语句(DQL,DML)
第五步:处理查询结果集(只有第四步是DQL的时候才会执行第五步)
第六步:释放资源(使用完后一定要关闭,java和数据库之间的通信属于进程之间的通信,开启后一定要关闭)
代码:
添加操作
/*
JDBC编程六步
*/
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest01{
public static void main(String[] args){
Connection conn = null;
Statement statement = null;
try{
//1、注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver();//多态,父类型应勇指向子类型对象
DriverManager.registerDriver(driver);
//2、获取连接
/*
url格式:协议://地址:端口号/数据库具体实例
*/
String url = "jdbc:mysql://localhost:3306/fhq?serverTimezone=UTC";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url,user,password);
System.out.println("数据库连接对象"+conn);
//3、获取数据库操作对象(Statement专门执行sql语句)
statement = conn.createStatement();
//4、执行sql语句
String sql = "insert into port(id,port) values(2,200)";
//专门执行DML语句的(insert delete update)
//返回值是:影响数据库中记录的条数
int result = statement.executeUpdate(sql);
System.out.println(result == 1?"保存成功":"保存失败");
//5、处理查询结果集
}catch(SQLException e){
e.printStackTrace();
}finally{
//6、释放资源
//为了保障资源一定释放,所以在finally语句块中关闭资源
//并且遵循从小到大的依次关闭
//分别try catch 否则第一个报错了之后,后面的就不会执行了
if(statement!=null){
try{
statement.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
删除和更新操作
/*
JDBC完成delete update
*/
import java.sql.*;
public class JDBCTest02{
public static void main(String[] args){
Connection connection = null;
Statement statement = null;
try{
//1、注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2、获取连接
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/fhq?serverTimezone=UTC","root","123456");
//3、获取数据库操作对象
statement = connection.createStatement();
//4、执行sql语句
String sql01 = "delete from port where port = 100";
String sql02 = "update port set port = 300 where port = 200";
int res01 = statement.executeUpdate(sql01);
int res02 = statement.executeUpdate(sql02);
System.out.println(res01 == 1?"删除成功":"删除失败");
System.out.println(res02 == 1?"更新成功":"更新失败");
}catch(SQLException e){
e.printStackTrace();
}finally{
//6、释放资源
if(statement != null){
try{
statement.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(connection != null){
try{
connection.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
5、使用类加载方式注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
实现原理:
类加载的时候会执行静态代码块
在Driver这个类中有一个静态代码块内容包含了
DriverManager.registerDriver(new Driver());
所以就注册了驱动
这种方式更加的常用
6、将连接数据库的所有配置信息都配置到配置文件中
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/fhq?serverTimezone=UTC
user=root
password=123456
java文件
/*
将数据库连接信息写到配置文件中
*/
import java.sql.*;
import java.util.*;
public class JDBCTest04{
public static void main(String[] args){
//使用资源绑定器绑定属性配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection connection = null;
Statement statement = null;
try{
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
connection = DriverManager.getConnection(url,user,password);
//3、获取数据库操作对象
statement = connection.createStatement();
//4、执行sql语句
String sql = "insert into port(port) values (400)";
int result = statement.executeUpdate(sql);
System.out.println(result == 1?"插入成功":"插入失败");
}catch(Exception e){
e.printStackTrace();
}finally{
if(statement != null){
try{
statement.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(connection != null){
try{
connection.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
7、处理查询结果集
/*
处理查询结果集合
*/
import java.sql.*;
import java.util.*;
public class JDBCTest05{
public static void main(String[] args){
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
try{
//1、注册驱动
Class.forName(driver);
//2、获取连接
connection = DriverManager.getConnection(url,user,password);
//3、创建数据库操作对象
statement = connection.createStatement();
//4、执行sql语句
String sql = "select * from port";
resultSet = statement.executeQuery(sql);
//5、处理结果集
while(resultSet.next()){
/*
这里有两种方式:
第一种:根据下标获取列的值,默认从1开始
String id = resultSet.getString(1);
第二种:根据列名称获取
String id = resultSet.getString("id");
*/
int id = resultSet.getInt("id");
int port = resultSet.getInt("port");
System.out.println(id+" "+port);
}
}catch(Exception e){
e.printStackTrace();
}finally{
//6、释放资源
if(resultSet != null){
try{
resultSet.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(statement != null){
try{
statement.close();
}catch(Exception e){
e.printStackTrace();
}
}
if(connection != null){
try{
connection.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
}
8、用户登录练习
package com.fjh.jdbc;
import javax.xml.transform.Result;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
public class UserLogin {
public static void main(String[] args) {
//初始化界面
Map<String,String > userLoginInfo = initUI();
//用户登录方法
boolean result = userLogin(userLoginInfo);
System.out.println(result == true?"登录成功":"登录失败");
}
/**‘
*
* @param userLoginInfo 用户的登录信息
* @return true 登录成功 false 登录失败
*/
private static boolean userLogin(Map<String, String> userLoginInfo) {
boolean userLoginFlag = false;
ResourceBundle bundle = ResourceBundle.getBundle("com.fjh.properties.userLogin");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
String userNameLogin = userLoginInfo.get("userName");
String passwordLogin = userLoginInfo.get("password");
try {
//1、注册驱动
Class.forName(driver);
//2、获取连接
connection = DriverManager.getConnection(url,user,password);
//3、创建数据库操作对象
statement = connection.createStatement();
//4、执行sql语句
String sql = "select * from userLogin where userName = '"+userNameLogin+"' and password = '"+passwordLogin+"'";
resultSet = statement.executeQuery(sql);
//5、处理结果集
if(resultSet.next()){
userLoginFlag = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
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();
}
}
}
return userLoginFlag;
//6、释放资源
}
/**
* 输入用户信息
* @return userloginInfo 用户名和密码
*/
private static Map<String, String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.printf("请输入用户名:");
String userName = scanner.nextLine();
System.out.printf("请输入密码:");
String password = scanner.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("userName",userName);
userLoginInfo.put("password",password);
return userLoginInfo;
}
}
9、sql注入
如果以上代码输入如下也会登录成功
请输入用户名:aaa
请输入密码:aaa' or '1'='1
登录成功
这种现象叫做sql注入
1、产生sql注入的原因
用户输入的信息含有sql与的关键字,并且这些关键字参与了sql语句的编译过程
10、解决sql注入
方法:使用preparedStatement(预编译数据库操作对象)
package com.fjh.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
/**
* 解决sql注入问题
*/
public class UserLogin02 {
public static void main(String[] args) {
//初始化界面
Map<String,String > userLoginInfo = initUI();
//用户登录方法
boolean result = userLogin(userLoginInfo);
System.out.println(result == true?"登录成功":"登录失败");
}
/**‘
*
* @param userLoginInfo 用户的登录信息
* @return true 登录成功 false 登录失败
*/
private static boolean userLogin(Map<String, String> userLoginInfo) {
boolean userLoginFlag = false;
ResourceBundle bundle = ResourceBundle.getBundle("com.fjh.properties.userLogin");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection connection = null;
//Statement statement = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String userNameLogin = userLoginInfo.get("userName");
String passwordLogin = userLoginInfo.get("password");
try {
//1、注册驱动
Class.forName(driver);
//2、获取连接
connection = DriverManager.getConnection(url,user,password);
//3、创建数据库操作对象
//使用占位符?,然后预先将要使用的sql语句编译
String sql = "select * from userLogin where userName = ? and password = ?";
preparedStatement = connection.prepareStatement(sql);
//statement = connection.createStatement();
//4、执行sql语句
//mysql的下标从1开始,这里的下标代表第一个问号
preparedStatement.setString(1,userNameLogin);
preparedStatement.setString(2,passwordLogin);
preparedStatement.executeQuery();
//resultSet = statement.executeQuery(sql);
//5、处理结果集
if(resultSet.next()){
userLoginFlag = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
if (resultSet == null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement == null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection == null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return userLoginFlag;
//6、释放资源
}
/**
* 输入用户信息
* @return userloginInfo 用户名和密码
*/
private static Map<String, String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.printf("请输入用户名:");
String userName = scanner.nextLine();
System.out.printf("请输入密码:");
String password = scanner.nextLine();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("userName",userName);
userLoginInfo.put("password",password);
return userLoginInfo;
}
}
11、Statement和preparedStatement的区别
Statement是将值直接拼接到sql语句中,值参与了sql的编译过程,容易造成sql注入
preparedStatement是先将要执行的sql语句框架编译好,传值的地方用?占位置,最后将值传入,值不参与sql的编译过程
注意:在有些时候必须使用Statement,就像排序,传入的值是desc或者asc 那么这个值就必须参与编译过程
12、JDBC的事务自动提交机制
JDBC中只要执行任意一条DML语句,就提交一次
idea块编辑:alt+shift+insert
将自动提交事务变为手动提交
//开启事务
conn.setAutoCommit(false);
//中间写要完成的事务
//提交事务
conn.commit()
//catch语句块中写回滚事务(如果发生异常,要保证数据的安全)
if(conn != null){
try{
conn.rollback();//手动回滚事务
}catch(Exception e){
e.printStackTrace();
}
}
最重要的就是这三行
单机事务简单
分布式事务难
12、悲观锁和乐观锁
悲观锁:加锁后其他线程就不能够访问,是单线程模式
乐观锁:加锁了之后依旧可以访问,不过会对加锁的内容添加一个序号,如果其他线程访问的时候,发现事务开始和结束的序号不一样就不会提交
代码测试:
加锁代码
package com.fjh.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCTest07 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DButil.getConnection("jdbctest","root","123456");
connection.setAutoCommit(false);//关闭自动提交
String sql = "select * from userLogin where userName = ? for update ";//这里加for update就是对查询到的数据加了悲观锁
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"fjh");
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("userName") +" "+resultSet.getString("password"));
}
connection.commit();//手动提交
} catch (Exception e) {
try {
if(connection != null){
connection.rollback();//事务回滚
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
}
在加锁情况下修改数据
package com.fjh.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JDBCTest08 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DButil.getConnection("jdbctest","root","123456");
connection.setAutoCommit(false);//关闭自动提交
String sql = "update userLogin set userName = 'root' where userName = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"fjh");
int count = preparedStatement.executeUpdate();
System.out.println(count);
connection.commit();//手动提交
} catch (Exception e) {
try {
connection.rollback();//事务回滚
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
}
测试方法:
在07类的提交代码处加断点观察08是否有输出,将断点打开后,08有了输出
实验结果:到断点时没有输出,打开后有输出,说明行级锁生效了
13、封装JDBC
package com.fjh.jdbc;
import java.sql.*;
public class DButil {
//一般工具类的构造方法都是私有的,因为不需要new对象
private DButil(){}
//静态代码块在类加载的时候只加载一次,并且只执行一次
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection(String dbName,String userName,String passwoord) throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/"+dbName+"?serverTimezone=UTC",userName,passwoord);
}
//关闭连接
public static void close(Connection connection, Statement statement, ResultSet resultSet){
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
14、以上测试所用到的表结构
id 自增
userName 用户名
password 密码