JDBC学习笔记-B站动力节点

1.JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

2.JDBC的本质是什么?

JDBC是SUN公司制定的一套接口(interface)
	java.sql.*; (这个软件包下有很多接口)

接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程?
	解耦合:降低程序的耦合度,提高程序的扩展力。
	解耦合:让程序更加灵活(面向接口编程就是面向抽象编程)
	灵活:比如下面喂养的方法:
		如果写Dog d 只能喂养狗,写Cat c 只能喂养猫
		而写Animal a 既能喂养狗也能喂养猫,更加灵活(解耦合)

	多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
		建议:
			Animal a = new Cat();  //父类型引用指向子类型对象
			Animal a = new Dog();
			
			// 喂养的方法
			public void feed(Animal a){ // 面向父类型(抽象)编程。
			
			}
			
		不建议:
			Dog d = new Dog();
			Cat c = new Cat();

思考:为什么SUN制定一套JDBC接口呢?
	因为每一个数据库的底层实现原理都不一样。
	Oracle数据库有自己的原理。
	MySQL数据库也有自己的原理。
	MS SqlServer数据库也有自己的原理。
	....
	每一个数据库产品都有自己独特的实现原理。

JDBC的本质到底是什么?
	一套接口。(通过JDBC来操纵不同的数据库)

在这里插入图片描述

模拟角色

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。

classpath=.;D:\course\06-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar

以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。
IDEA有自己的配置方式。

4.JDBC编程六步(需要背会)

第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)

第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)

第三步:获取数据库操作对象(专门执行sql语句的对象)

第四步:执行SQL语句(DQL DML....)

第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)

第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)

5.案例一:

JDBCTest01
JDBC编程六步
JDBC完成insert
/*
	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 stmt = null;

			try{
					//1.注册驱动
					//使用java.sql.DriverManager(驱动管理器)中的方法:(static void) registerDiver(Driver driver) 传入一个驱动
					//但是jdk中的Driver是java.sql.Driver,是一个接口,无法new对象,所以需要它的实现类
					//它的实现类在mysql驱动中:com.mysql.cj.jdbc.Driver() com mysql cj jdbc 是包名。mysql8版本以上要加cj

					//registerDriver(...)这个方法存在异常 throw SQLException 受检异常,再往上抛不合适,需要try...catch...

					//需要导入包:java.sql.Driver;java.sql.DriverManager;java.sql.SQLException;

					Driver driver = new com.mysql.cj.jdbc.Driver();//多态,父类型的引用指向子类型的对象 java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();
						 																						//oracle驱动中:Driver driver = new oracle.jdbc.driver.OracleDriver();
					DriverManager.registerDriver(driver);



					//2.获取连接
					//使用java.sql.DriverManager类中的方法:(static Connection) getConnection(String url, String user, String password) 输入url,数据库的用户名和密码;返回一个java.sql.Connection对象(是一个接口)
					//该方法同样存在受检异常SQLException;需要导入Connection包

					/*
						url:统一资源定位符(网络中某个资源的绝对路径)
						https://www.baidu.com/ ---> 这就是url
						URL包括哪几部分?
							协议
							IP地址
							PORT(端口号)
							资源名

						http://182.61.200.7:80/index.html
							http://        通信协议
							182.61.200.7   服务器的IP地址
							80             服务器上软件的端口
							index.html     是服务器上某个资源名

						要访问某个服务器上的某个软件:需要知道该服务器的IP地址和该软件在该服务器上的端口号

						jdbc:mysql://127.0.0.1:3306/bjpowernode
							jdbc:mysql://  协议
							127.0.0.1      IP地址(要连接的服务器/电脑的IP地址)
							3306           mysql数据库端口号
							bjpowernode    具体的数据库实例名

						说明:localhost和127.0.0.1都是本机的IP地址
						jdbc:mysql://localhost:3306/bjpowernode 和上面相同

						什么是通信协议,有什么用?
							通信协议是通信之前就提前定好的数据传送格式
							数据包具体怎么传送数据,格式提前定好的。

						oracle的url:jdbc:oracle:thin:@localhost(IP地址):端口号:数据库名
						其他的百度搜
					*/

					//mysql的url:jdbc:mysql://ip:port/bjpowernode(ip地址 port端口号 bjpowernode数据库名)

					String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
					String user = "root";//数据库用户名
					String password = "333";//自己设置的数据库密码
					conn = DriverManager.getConnection(url,user,password);

					//这里是mysql数据库连接对象(conn) = com.mysql.cj.jdbc.ConnectionImpl@31304f14
					System.out.println("数据库连接对象 = " + conn);



					//3.获取数据库操作对象
					//第二步已经拿到了Connection对象,可以使用Connection的方法:(Statement) createStatement() 获取数据库的操作对象
					//该方法存在SQLException异常 并且 需要Statement导包
					//Statement专门执行sql语句的。

					stmt = conn.createStatement();



					//4.执行sql
					//例如以下语句
					//JDBC的sql语句结尾不需要加分号
					String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";

					//使用Statement中的方法:(int) executeUpdate(String sql)
					//该方法专门执行DML语句(insert delete update 对于 表中的数据 进行 增删改)
					//返回值是“影响数据库中的记录条数” ---> 插入2条返回2/删除3条返回3/更新5条返回5
					//即 影响了数据库中表的几条记录

					int count = stmt.executeUpdate(sql);
					System.out.println(count == 1 ? "保存成功" : "保存失败");


					//5.处理查询结果集

			}catch(SQLException e){
				e.printStackTrace();
			}finally{

				//6.释放资源
				//为了保证资源一定释放,在finally语句块中关闭资源 并且 要遵循 从小到大 依次关闭
				//后开先关
				//分别对其try..catch

				//因此 要把 Connection conn = null; 和 Statement stmt = null;写在try..catch外面

				try{
					if(stmt != null){
						stmt.close();
					}
				}catch(SQLException e){
					e.printStackTrace();
				}

				try{
					if(conn != null){
						conn.close();
					}
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
}

在这里插入图片描述

6.案例二(JDBC大致的步骤框架初步版):

JDBCTest02
大致的步骤 框架:

导包
import java.sql.*;

public class Test{
	public static void main(String[] args){

		Connection conn = null; 连接对象
		Statement stmt = null;  数据库操作对象
		
		try{
			1.注册驱动
			DriverManager(驱动管理器)类的registerDriver(Driver driver)方法
			
			多态(父类型的引用指向子类型的对象):
			mysql的驱动中的Driver类是对java.sql中的Driver接口的实现
			java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();
			
			2.获取连接
			通过DriverManager类的getConnection(url,user,password)方法
			获得连接对象Connection conn

			mysql的url是 jdbc:mysql://localhost:3306/bjpowernode
						jdbc:mysql://IP地址:端口号/数据库名
			
			3.获取数据库操作对象
			通过Connection类的createStatement()方法
				获得数据库的操作对象 Statement stmt
			Statement stmt = conn.createStatement();

			
			4.执行sql语句
			首先写出sql语句: String sql = "...";
			然后通过Statement类的executeUpdate(String sql)方法
				执行sql语句
				返回int:影响了数据库中的几条数据(插入2条返回2/删除3条返回3/更新5条返回5)
					
			
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
		
			6.释放资源
			遵循 后开先闭 规则
			先创建Connection conn,后创建Statement stmt
			先关闭 stmt,后关闭 conn
			
			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}
		
			try{

				if(conn != null){
					conn.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

		}
	}
}
JDBC完成delete
/*
	JDBC完成delete
*/
import java.sql.*;

public class JDBCTest02{
	public static void main(String[] args){

		Connection conn = null;
		Statement stmt = null;

		try{
			//1.注册驱动
			DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql语句
			String sql = "delete from dept where deptno = 40";
			int count = stmt.executeUpdate(sql);
			System.out.println(count == 1 ? "删除成功" : "删除失败");

		}catch(SQLException e){
			e.printStackTrace();
		}finally{

			//6.释放资源
			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

			try{

				if(conn != null){
					conn.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

		}
	}
}

在这里插入图片描述

JDBC完成update

将案例二的第四步改为:
String sql = "update dept set dname = '销售部' , loc = '芝加哥' where deptno = 30";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "更新成功" : "更新失败");

在这里插入图片描述

7.案例三:注册驱动的另一种方法

/*
	注册驱动的另一种方式(常用的)
*/
import java.sql.*;

public class JDBCTest03{
	public static void main(String[] args){
		try{
			//1.注册驱动
			//第一种写法:
			//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());

			//第二种写法
			//查看com.mysql.cj.jdbc.Driver类的源代码发现:该类中有一个静态代码块
			//静态代码块中存在第一种注册驱动写法的语句
			//所以不需要写第一种方法,只让该静态代码块执行就行了

			//如何让一个类的静态代码块执行?让这个类加载就行了
			//如何加载这个类?使用反射机制(与反射机制相关的java.lang.Class类的forName(String className)方法)
			//Class.forName(类名)这个方法的执行会导致类加载,类加载时静态代码块执行
			Class.forName("com.mysql.cj.jdbc.Driver");

			//为什么使用第二种方法?因为参数是一个字符串,而字符串可以写到xxx.properties配置文件中
			//注意:该方法存在ClassNotFoundException异常


			//2.获取连接
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
			//com.mysql.cj.jdbc.ConnectionImpl@a1153bc
			System.out.println(conn);

		}catch(SQLException e){
			e.printStackTrace();
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}
	}
}

8.案例四:将连接到数据库的所有信息配置到配置文件中

//将连接到数据库的所有信息 配置到 配置文件中
/*
	为什么这么做?
		实际开发中不建议把连接数据库的信息写死到java程序中
		以后修改,只需要修改属性配置文件xxx.properties中的内容就好了
*/

import java.sql.*;
import java.util.ResourceBundle;

public class JDBCTest04{
	public static void main(String[] args){

		//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties
		//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器
		//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据
		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 conn = null;
		Statement stmt = null;

		try{
			//1.注册驱动
			Class.forName(driver);

			//2.获取连接
			conn = DriverManager.getConnection(url,user,password);

			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql语句
			String sql = "update dept set dname = '销售部1' , loc = '芝加哥1' where deptno = 30";
			int count = stmt.executeUpdate(sql);
			System.out.println(count == 1 ? "更新成功" : "更新失败");

		}catch(Exception e){ //SQLException异常和ClassNotFoundException异常
			e.printStackTrace();
		}finally{

			//6.释放资源
			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

			try{

				if(conn != null){
					conn.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

		}
	}
}

在这里插入图片描述

9.案例五:处理查询结果集

JDBC完成查
//处理查询结果集
//此案例先不使用资源绑定器
import java.sql.*;

public class JDBCTest05{
	public static void main(String[] args){

		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null; //结果集对象 释放资源时先释放rs

		try{
			//1.注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");

			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql
			String sql = "select empno,ename,sal from emp";

			//专门执行DQL语句(select)的方法:Statement类的executeQuery(String sql)方法 返回ResultSet结果集对象
			rs = stmt.executeQuery(sql);

			//5.处理查询结果集(看下图)
			/*
				首先使用ResultSet类的next()方法,判断下一行有没有数据。有数据返回true,没有返回false
				boolean falg1 = rs.next();

				然后如果光标指向有数据,则取数据
				使用ResultSet类的getString(int index)方法,返回String类型:
					不管数据库中的数据是什么类型,都以String形式取出
					index从1开始,JDBC中所有下标从1开始

				if(flag1 = true){
					String empno = rs.getString(1); index = 1 取出第一列的数据
					String ename = rs.getString(2); index = 2 取出第二列的数据
					String sal = rs.getString(3);   index = 3 取出第三列的数据
					System.out.println(empno + "," + ename + "," + sal);
				}

			*/
			while(rs.next() == true){
				/*
				String empno = rs.getString(1);
				String ename = rs.getString(2);
				String sal = rs.getString(3);
				System.out.println(empno + "," + ename + "," + sal);
				*/

				//也可以-->getString(String 字段名/列名称) 这样更好
				//列名称不是表中的列名称,而是查询结果集的列名称(即字段名被修改,要输入修改后的字段名)
				String empno = rs.getString("empno");
				String ename = rs.getString("ename");
				String sal = rs.getString("sal");
				System.out.println(empno + "," + ename + "," + sal);

				/*
					注意:除了可以以String类型取出,也可以以特定的类型取出
					int empno = rs.getInt("empno");
					double sal = rs.getDouble("sal");
					根据该数据的实际类型
					这样更方便计算
				*/

			}

		}catch(Exception e){//SQLException异常和ClassNotFoundException异常
			e.printStackTrace();
		}finally{

			try{

				if(rs != null){
					rs.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

			try{

				if(conn != null){
					conn.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

		}
	}
}

在这里插入图片描述

10.使用IDEA开发JDBC代码配置驱动

对模块module右键,点击open module settings,再点击libraries(库)
再点击“+”号,点java,找到mysql的驱动jar包(我的是mysql-connector-java-8.0.28.jar)所在位置
点击应用,点OK

注意:每创建一个新的模块,都要配置一次驱动(导入jar包)

11.总结以上(JDBC代码最终版)

第一种:不使用 将数据库的信息配置到属性配置文件中
JDBC关于DML语句(insert delete update)

import java.sql.*;

public class Test{
	public static void main(String[] args){
		
		Connection conn = null;  //连接对象
		Statement stmt = null;   //数据库操作对象
		
		try{
			//1.注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");

			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
			
			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql语句 DML语句(insert delete update)
			//String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
			//String sql = "delete from dept where deptno = 40";
			String sql = "update dept set dname = '销售部' , loc = '芝加哥' where deptno = 30";
			int count = stmt.executeUpdate(sql);
			System.out.println(count == 1 ? "更新成功" : "更新失败");

		}
		catch(Exception e){
			e.printStackTrace();
		}finally{
			
			//6.释放资源
			try{
				if(stmt != null){
					stmt.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}

			try{
				if(conn != null){
					conn.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
JDBC关于DQL语句(select) 处理查询结果集

import java.sql.*;

public class Test{
	public static void main(String[] args){
		
		Connection conn = null;  //连接对象
		Statement stmt = null;   //数据库操作对象
		
		ResultSet rs = null; //结果集对象 释放资源时先释放rs
		
		try{
			//1.注册驱动
			Class.forName("com.mysql.cj.jdbc.Driver");

			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
			
			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql语句 
			String sql = "select empno,ename,sal from emp";
			//专门执行DQL语句(select)的方法:Statement类的executeQuery(String sql)方法 返回ResultSet结果集对象
			rs = stmt.executeQuery(sql);

			//5.处理查询结果集
			while(rs.next() == true){
				/*
					String empno = rs.getString(1); index = 1 取出第一列的数据
					String ename = rs.getString(2); index = 2 取出第二列的数据
					String sal = rs.getString(3);   index = 3 取出第三列的数据
					System.out.println(empno + "," + ename + "," + sal);
				*/

				//也可以-->getString(String 字段名/列名称) 这样更好
				//列名称不是表中的列名称,而是查询结果集的列名称(即字段名被修改,要输入修改后的字段名)
				String empno = rs.getString("empno");
				String ename = rs.getString("ename");
				String sal = rs.getString("sal");
				System.out.println(empno + "," + ename + "," + sal);

				/*
					注意:除了可以以String类型取出,也可以以特定的类型取出
					int empno = rs.getInt("empno");
					double sal = rs.getDouble("sal");
					根据该数据的实际类型
					这样更方便计算
				*/

			}

		}
		catch(Exception e){
			e.printStackTrace();
		}finally{
			
			//6.释放资源
			try{
				if(rs != null){
					rs.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}

		
			try{
				if(stmt != null){
					stmt.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}

			try{
				if(conn != null){
					conn.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
第二种:使用 将数据库的信息配置到属性配置文件中 (常用这种)
import java.sql.*;
import java.util.ResourceBundle;

public class JDBCTest04{
	public static void main(String[] args){

		//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties
		//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器
		//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据
		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 conn = null;
		Statement stmt = null;

		try{
			//1.注册驱动
			Class.forName(driver);

			//2.获取连接
			conn = DriverManager.getConnection(url,user,password);

			//3.获取数据库操作对象
			stmt = conn.createStatement();

			//4.执行sql语句
			String sql = "update dept set dname = '销售部1' , loc = '芝加哥1' where deptno = 30";
			int count = stmt.executeUpdate(sql);
			System.out.println(count == 1 ? "更新成功" : "更新失败");

		}catch(Exception e){ //SQLException异常和ClassNotFoundException异常
			e.printStackTrace();
		}finally{

			//6.释放资源
			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

			try{

				if(conn != null){
					conn.close();
				}

			}catch(SQLException e){
				e.printStackTrace();
			}

		}
	}
}
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=333
总结:
1.注册驱动
	Class.forName("com.mysql.cj.jdbc.Driver");
	
2.获取连接
	Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root",333);

3.获取数据库操作对象
	Statement stmt = conn.getStatement();

4.执行sql
	第一种:DML语句(insert delete update)
		String sql = "DML语句";
		int count = stmt.executeUpdate(sql); //影响数据库数据条数
	第二种:DQL语句(select)
		String sql ="DQL语句";
		ResultSet rs = stmt.executeQuery(sql);

5.处理查询结果集(有第四步的第二种才有第五步)
	while(rs.next() = true){
		第一种:
			String empno = rs.getString(1); //参数为index 从1开始 获取第一列的数据
			String ename = rs.getString(2);
			String sal = rs.getString(3);

		//偏向使用第二、三种
		第二种:
			String empno = rs.getString("empno"); //参数为字段名
			String ename = rs.getString("ename");
			String sal = rs.getString("sal");
		第三种:	
			String empno = rs.getString("empno");
			int empno = rs.getInt("empno");
			double sal = rs.getDouble("sal");	
	}
	
6.释放资源(后开先闭原则)
	try{
	
	}catch(Exception e){
		e.printStackTrace();
	}finally{
			
			//是第四步的第二种再写这个rs
			try{

				if(rs != null){
					rs.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

			try{

				if(stmt != null){
					stmt.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

			try{

				if(conn != null){
					conn.close();
				}

			}catch(Exception e){
				e.printStackTrace();
			}

		}


注意:推荐将连接到数据库的所有信息 配置到 配置文件中

实际开发中不建议把连接数据库的信息写死到java程序中
以后修改,只需要修改属性配置文件xxx.properties中的内容就好了
//使用资源绑定器java.util.ResourceBundle类 绑定 属性配置文件jdbc.properties
//第一步:使用ResourceBundle类的getBundle(String 属性配置文件路径(不加文件扩展名))方法获得资源绑定器
//第二步:通过getString(String ...)方法从属性配置文件中获得要用的数据
		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");

属性配置文件:jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=333

12.用户登录业务介绍

实现功能:
    1.需求:模拟用户登录功能的实现
    2.业务描述:
        程序运行的时候,提供一个用户输入的入口,可以让用户输入用户名和密码;
        用户输入用户名和密码之后,提交信息,java程序收集到用户信息;
        java程序连接数据库验证用户名和密码是否合法;
        合法:显示登录成功
        不合法:显示登录失败
    3.数据的准备:
        在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
        使用PD工具进行数据库表的设计

13.PowerDesigner工具的安装

打开PowerDesigner
选择create module
点击module types
点击physical data module 物理数据模型(建表)
选择DBMS mysql5.0 (新版本无影响)
model name:user-login
点ok
中间是画图区:按住ctrl键滑动滚轮,每一个小格子里都可以放很多张表

name
code:才是真正的用在建表语句的名字

使用这个工具对表进行设计

user-login.sql脚本文件如果太大,用记事本或者navicat打不开
可以使用resource+脚本文件路径名(拉进去) 在DOS命令窗口中打开

resource+脚本文件路径名(拉进去)--->这一步出了问题
总是:ERROR: Failed to open file xx, error: 2
然后发现原因:文件夹的名字不要用中文!

14.数据准备

在这里插入图片描述
在这里插入图片描述

15.用户登录功能界面的初始化

public class JDBCTest06 {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();

        System.out.println("密码:");
        String loginPwd = s.nextLine();
        
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        
        return userLoginInfo;
    }
}

16.登录方法的实现

package com.bjpowernode.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
实现功能:
    1.需求:模拟用户登录功能的实现
    2.业务描述:
        程序运行的时候,提供一个用户输入的入口,可以让用户输入用户名和密码;
        用户输入用户名和密码之后,提交信息,java程序收集到用户信息;
        java程序连接数据库验证用户名和密码是否合法;
        合法:显示登录成功
        不合法:显示登录失败
    3.数据的准备:
        在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
        使用PD工具进行数据库表的设计
 */
public class JDBCTest06 {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();

        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        //打标记
        boolean loginSuccess = false;

        //单独定义变量
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");

        //JDBC代码
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs =null;

        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

            //3.获取数据库操作对象
            stmt = conn.createStatement();

            //4.执行sql
            //String sql = "select * from t_user where loginName = 'xxxx' and loginPwd = 'yyyy'";
            String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
            rs = stmt.executeQuery(sql);

            //5.处理查询结构集(这里不需要使用while)
            if (rs.next() == true){
                //登录成功
                loginSuccess = true;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            try {
                if (rs != null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (stmt != null){
                    stmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();

        System.out.print("密码:");
        String loginPwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);

        return userLoginInfo;
    }
}

17.SQL注入现象

4.当前程序存在的问题:                 
    用户名:fdsa                 
    密码:fdsa' or '1'='1       
    登录成功                     
    这种现象称为SQL注入(安全隐患)(黑客经常使用)
5.导致SQL注入的根本原因:              
    用户输入的信息中含有sql语句的关键字,并且这些关
    导致sql语句的原意被扭曲,进而达到sql注入  

18.如何解决SQL注入问题

package com.bjpowernode.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
    1.解决SQL注入问题:
        只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
        即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,就不起作用。
        要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
        PreparedStatement接口继承了java.sql.Statement
        PreparedStatement是属于预编译的数据库操作对象
        PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”
    2.测试结果:
        用户名:fdsa
        密码:fdsa' or '1'='1
        登录失败
    3.解决SQL注入的关键是什么?
        用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。
 */
public class JDBCTest07 {
    public static void main(String[] args) {
        //初始化一个界面
        Map<String,String> userLoginInfo = initUI();

        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        //打标记
        boolean loginSuccess = false;

        //单独定义变量
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");

        //JDBC代码
        Connection conn = null;
        PreparedStatement ps = null;  //使用PreparedStatement
        ResultSet rs =null;

        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");


            //3.获取预编译的数据库操作对象
            //sql语句提前,sql语句的框子,其中一个?表示一个占位符,一个?将来接收一个“值”;注意:占位符不能使用单引号括起来
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";

            //使用Connection的prepareStatement(String sql)方法
            //程序执行到此处,会发送SQL语句框子给DBMS,然后DBMS进行SQL语句的预先编译
            ps = conn.prepareStatement(sql);

            //给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中的所有下标从1开始)
            ps.setString(1,loginName); //如果传值是int 那么setInt(index,...)
            ps.setString(2,loginPwd);



            //4.执行sql
            rs = ps.executeQuery();//不需要再传sql语句了

            //5.处理查询结构集(这里不需要使用while)
            if (rs.next() == true){
                //登录成功
                loginSuccess = true;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            try {
                if (rs != null){
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (ps != null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();

        System.out.print("密码:");
        String loginPwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);

        return userLoginInfo;
    }
}

19.Statement和PreparedStatement对比

-Statement存在SQL注入问题;PreparedStatement解决了SQL注入问题
-Statement是编译一次执行一次;PreparedStatement是编译一次可执行N次;PreparedStatement执行效率较高一些
-PreparedStatement会在编译阶段做类型的安全检查

综上所述:PreparedStatement使用较多,极少数情况下使用Statement
 
什么情况下必须使用Statement?
    业务方面要求必须支持SQL注入时。(PreparedStatement无法支持SQL注入)
    Statement支持SQL注入,凡是业务方面需要进行sql语句拼接时,必须使用Statement    
    只需要给sql语句传值时使用:PreparedStatement 

20.演示Statement的用途

如果使用PreparedStatement,使用ps.setString(...)给占位符?传值(字符串)时,编译时会自动给传的值加上单引号
比如:String sql = "select * from t_user where loginName = ? and loginPwd = ?";
	如果给?传的是一个值,字符串jack和字符串333,那么会自动加上单引号,即:
	ps.setString(1,"jack");
	ps.setString(2,"333");
	String sql = "select * from t_user where loginName = 'jack' and loginPwd = '333'";
	这样是对的
	注意:使用ps.setInt(1,100);--->会把第一个问号换成100,不加单引号,因为不是字符串

但是如果传的是desc,想要降序,也会给desc加上单引号,这样就不对了,只能使用Statement
比如:String sql = "select ename from emp order by ename ?";
ps.setString(1,"desc");
String sql = "select ename from emp order by ename 'desc'"; //错误
package com.bjpowernode.jdbc;

import java.sql.*;
import java.util.Scanner;

/*
    演示Statement用途:
        业务方面要求必须支持SQL注入时。(PreparedStatement无法支持SQL注入)
        Statement支持SQL注入,凡是业务方面需要进行sql语句拼接时,必须使用Statement
            String sql = "select ename from emp order by ename " + keyWords;
            + keyWords 就是sql语句拼接
 */
public class JDBCTest08 {
    public static void main(String[] args) {
        //用户在控制台输入desc是降序,输入asc是升序
        Scanner s = new Scanner(System.in);
        System.out.println("输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入:");
        String keyWords = s.nextLine();

        //sql
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs =null;

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

/*          错误:
            String sql = "select ename from emp order by ename ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,keyWords);

            rs =ps.executeQuery();*/

            stmt = conn.createStatement();
            String sql = "select ename from emp order by ename " + keyWords;
            rs = stmt.executeQuery(sql);

            while (rs.next() == true){
                System.out.println(rs.getString("ename"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {

            if (rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (stmt != null){
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

21.PreparedStatement完成增删改

package com.bjpowernode.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

//PreparedStatement完成增删改:insert delete update
public class JDBCTest09 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

            //3.获取预处理数据库操作对象
/*          String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,60);
            ps.setString(2,"销售部");
            ps.setString(3,"上海");*/

/*          String sql = "update dept set dname = ?,loc = ? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"研发一部");
            ps.setString(2,"北京");
            ps.setInt(3,60);*/

            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,60);

            //4.执行sql
            int count = ps.executeUpdate();
            System.out.println(count);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {

            //6.释放资源
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

22.总结:JDBC最最终版写法

在以前总结的基础上,再加上:
	大部分情况下使用PreparedStatement
	
	要根据情况判断是使用PreparedStatement还是Statement
	凡是业务方面需要进行sql语句拼接时,必须使用Statement

23.JDBC的事务自动提交机制的演示

package com.bjpowernode.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/*
    JDBC事务机制:
        1.JDBC中的事务是自动提交的,什么是自动提交?
            只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为
            但在实际的业务中,通常都是N条DML语句共同联合才能完成,必须保证这些DML语句在同一个事务当中同时成功或同时失败
        2.以下程序先来验证一下JDBC的事务是自动提交机制
            测试结果:JDBC只要执行任意一条DML语句,则自动提交一次
 */
public class JDBCTest10 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

            //3.获取预处理数据库操作对象
            String sql = "update dept set dname = ? where deptno = ?";
            ps = conn.prepareStatement(sql);

            //第一次给占位符传值
            ps.setString(1,"x部门");
            ps.setInt(2,30);
            int count = ps.executeUpdate();//执行第一条update语句
            System.out.println(count);

            //重新给占位符传值
            ps.setString(1,"y部门");
            ps.setInt(2,20);
            count = ps.executeUpdate();//执行第二条update语句
            System.out.println(count);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {

            //6.释放资源
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

24.演示JDBC事务自动提交机制会引发的问题和解决办法

package com.bjpowernode.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/*
    演示JDBC事务自动提交机制会引发的问题:
        账户转账业务

    sql脚本:
        drop table if exists t_act;
        create table t_act(
            actno int,
            balance double(7,2) //注意:7表示有效数字的个数,2表示小数位的个数
        );
        insert into t_act(actno,balance) values(111,20000);
        insert into t_act(actno,balance) values(222,0);
        commit;
        select * from t_act;


     重点三行代码:将自动提交改为手动提交
        conn.setAutoCommit(false);
        conn.commit();
        conn.rollback();

 */
public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");

            //将自动提交机制改为手动提交 Connection的setAutoCommit(false/true)方法
            conn.setAutoCommit(false);//开启事务

            //3.获取预处理数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);

            //111账户给222账户转10000元

            //给?传值
            ps.setDouble(1,10000);
            ps.setInt(2,111);
            int count = ps.executeUpdate();

            //这里设置一个空指针异常
            //正常来说,上下两个sql语句(一个事务)都执行之后,转账才会成功,
            //但是因为JDBC的自动提交机制,只提交了上一个sql语句,导致111账户钱少了,但是222账户钱没变
            //所以要将jdbc的自动提交机制改为手动提交
            String s = null;
            s.toString();

            //给?传值
            ps.setDouble(1,10000);
            ps.setInt(2,222);
            count += ps.executeUpdate();

            System.out.println(count == 2 ? "转账成功" : "转账失败");

            //程序能够执行到这里说明以上程序没有异常,事务结束,手动提交数据
            //Connection的commit()方法
            conn.commit();//提交事务

        } catch (Exception e) {

            //为了保证数据的安全性,遇到异常手动回滚
            if(conn != null){
                try {
                    conn.rollback();//回滚事务
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }

            e.printStackTrace();
        }finally {

            //6.释放资源
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                if (conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

25.总结:JDBC最最最终版写法

在以上的基础上:还要考虑将JDBC的自动提交机制改为手动提交机制

26.JDBC工具类的封装

package com.bjpowernode.jdbc.utils;

import java.sql.*;

//JDBC工具类,简化JDBC编程
public class DBUtil {
    /**
     * 工具类中的构造方法一般都是私有的
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用
     */
    private DBUtil(){}

    //静态代码块在类加载时执行,并且只执行一次
    static {
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据连接对象
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {

            //有可能调用多次连接对象,但注册驱动只需要一次,所以将注册驱动写在静态代码块中
            //Class.forName("com.mysql.cj.jdbc.Driver");
            //Class.forName(类名)这个方法的执行会导致类加载,类加载时静态代码块执行

            //2.获取连接
            //把异常扔出去,不要try..catch,因为自己写代码时会写try..catch
            //Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode");
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
    }

    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps 数据库操作对象 用Statement而不用PreparedStatement 因为PreparedStatement继承Statement
     * @param rs 结果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

27.JDBC实现模糊查询+JDBC工具类DBUtil的使用

package com.bjpowernode.jdbc.utils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 这个程序有两个任务:
 *  第一:测试DBUtil
 *  第二:模糊查询怎么写?
 */
public class JDBCTest12 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            //获取连接
            conn = DBUtil.getConnection();

            //获取预编译的数据库连接对象
            String sql = "select ename from emp where ename like ?";
            ps =conn.prepareStatement(sql);
            ps.setString(1,"_A%");

            //执行sql
            rs = ps.executeQuery();

            //处理查询结果集
            while (rs.next() == true){
                System.out.println(rs.getString("ename"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //释放资源
            DBUtil.close(conn,ps,rs);
        }
    }
}

28.总结:JDBC最最最最终版写法

在以前的基础上+JDBC封装的工具类DBUtil
	优先使用PreparedStatement
	再考虑使用Statement(涉及sql语句的拼接时)

    涉及事务(要多个sql语句同时成功/失败)时:将JDBC的自动提交机制改为手动提交机制
    和事务相关的语句只有:DML语句(insert delete update)

	不是很清楚到底什么时候使用手动提交机制,但是有24中的案例的情况必须使用手动提交

29.悲观锁和乐观锁的概念

行级锁(又叫悲观锁):
	select ename,job,sal from emp where job = 'MANAGER';
	+-------+---------+---------+
	| ename | job     | sal     |
	+-------+---------+---------+
	| JONES | MANAGER | 3272.50 |
	| BLAKE | MANAGER | 3135.00 |
	| CLARK | MANAGER | 2695.00 |
	+-------+---------+---------+

	select ename,job,sal from emp where job = 'MANAGER' for update;
	在select语句后加 for update 就是行级锁
	代表在当前事务结束之前,其他事务都无法修改这一行数据
	上面select语句表示:那三行数据(其实是对应emp表中的三行数据)都无法修改

乐观锁:
	多线程并发 都可以对这行数据修改

在这里插入图片描述

30.演示行级锁(悲观锁)机制

package com.bjpowernode.jdbc;

import com.bjpowernode.jdbc.utils.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

//演示行级锁(悲观锁)机制
//这个程序开启一个事务,这个事务专门进行查询,并且使用行级锁(悲观锁),锁住相关记录(数据)
public class JDBCTest13 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();

            //开启事务(关闭JDBC自动提交机制)
            conn.setAutoCommit(false);

            String sql = "select ename,job,sal from emp where job = ? for update";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");

            rs = ps.executeQuery();
            while (rs .next()== true){
                System.out.println(rs.getString("ename")+","+rs.getString("job")+","+rs.getDouble("sal"));
            }

            //提交事务(事务结束)
            conn.commit();
        } catch (Exception e) {
            //回滚事务(事务结束)
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }

            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,rs);
        }
    }
}

package com.bjpowernode.jdbc;

import com.bjpowernode.jdbc.utils.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

//演示行级锁(悲观锁)机制
//这个程序负责修改被锁定的记录
public class JDBCTest14 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = DBUtil.getConnection();

            //开启事务(关闭JDBC自动提交机制)
            conn.setAutoCommit(false);

            String sql = "update emp set sal = sal * 1.1 where job = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");
            int count = ps.executeUpdate();
            System.out.println(count);


            //提交事务(事务结束)
            conn.commit();
        } catch (Exception e) {
            //回滚事务(事务结束)
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }

            e.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);
        }
    }
}
通过断点控制test13事务不提交,然后运行test14,发现test14无法输出结果

注:该文章部分转载自动力节点

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值