网友整理老杜2020版本JDBC:
https://blog.csdn.net/qq_32480989/article/details/120876779
JDBC 基础概念
JDBC是什么?
Java DataBase Connectivity( Java语言连接数据库)
JDBC的本质是什么?
JDBC是SUN公司制定的一套接口(interface),java.sql.*; (这个软件包下有很多接口。)
接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。为什么要面向接口编程?
**解耦合:降低程序的耦合度,提高程序的扩展力。**<br /> 多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)<br /> 建议:
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数据库也有自己的原理,每一个数据库产品都有自己独特的实现原理。<br />
JDBC的本质到底是什么?
**一套接口**
JDBC 开发流程
开发流程
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有自己的配置方式。
JDBC编程六步(需要背会)
第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行SQL语句(DQL DML….)
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)
模拟JDBC的本质
- JDBC的本质
JDBC 课堂测试源码
- java.SQL包下:
- java 中 sql 源码路径:
java帮助文档——>java.SQL包下———->类、接口
复习:除了java.lang包,其余包都需要导包
- JDBC开发的步骤详细说明:
- 第一步:注册驱动
有一个类 :DriverManager 驱动管理器 Driver 的意思是驱动
里面有一个方法:static void registerDriver( Driver driver) 注册驱动的方法
这个方法的参数的说明:Driver是一个接口 无法new对象,就得找Driver的实现类,这个实现类是有数据库厂商写的,得去new产商写的那个实现类Driver
格式:new 包名.类名();
说明:厂商的实现类是公开的,需要包名.方式去访问这个实现类,然后再创建这个类的对象,去厂商的jar包找这个实现类。
- 这个方法的异常说明:
说明:继承了 exception 说明了 是受检异常,需要处理,
- 第二步:获取连接
url:统一资源定位符(网络中某个资源的绝对路径)
https://www.baidu.com/ 这就是URL。
URL包括哪几部分?
协议
IP
PORT
资源名
[http://182.61.200.7:80/index.html](http://182.61.200.7:80/index.html)<br /> http:// 通信协议<br /> 182.61.200.7 服务器IP地址<br /> 80 服务器上软件的端口<br /> index.html 是服务器上某个资源名<br /> <br /> <br /> **jdbc:mysql://127.0.0.1:3306/bjpowernode**<br /> jdbc:mysql:// 协议<br /> 127.0.0.1 IP地址<br /> 3306 mysql数据库端口号<br /> bjpowernode 具体的数据库实例名。<br /> <br /> 说明:localhost和127.0.0.1都是本机IP地址。
jdbc:mysql://192.168.151.27:3306/bjpowernode<br />
什么是通信协议,有什么用?
通信协议是通信之前就提前定好的数据传送格式。<br /> 数据包具体怎么传数据,格式提前定好的。<br />
oracle的URL:
jdbc:oracle:thin:@localhost:1521:orcl
- JDBCTest01源码
编程六步:
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、注册驱动
Driver driver = new com.mysql.jdbc.Driver(); // 多态,父类型引用指向子类型对象。
// Driver driver = new oracle.jdbc.driver.OracleDriver(); // oracle的驱动。
DriverManager.registerDriver(driver);
//2、获取连接
String url = "jdbc:mysql://192.168.151.9:3306/bjpowernode";
String user = "root";
String password = "981127";
conn = DriverManager.getConnection(url,user,password);
// com.mysql.jdbc.JDBC4Connection@41cf53f9
System.out.println("数据库连接对象 = " + conn);
//3、获取数据库操作对象(Statement专门执行sql语句的)
stmt = conn.createStatement();
//4、执行sql
String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
// 专门执行DML语句的(insert delete update)
// 返回值是“影响数据库中的记录条数”
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "保存成功" : "保存失败");
//5、处理查询结果集
}catch(SQLException e){
e.printStackTrace();
}finally{
//6、释放资源
// 为了保证资源一定释放,在finally语句块中关闭资源
// 并且要遵循从小到大依次关闭
// 分别对其try..catch
try{
if(stmt != null){
stmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn != null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
JDBC完成 delete、updata
学习方法:下去背代码
JDBCtest02
注册驱动的另一种方式
- JDBCTest03 ```java import java.sql.*;
public class JDBCTest03{ public static void main(String[] args){ try{ //1、注册驱动 // 这是注册驱动的第一种写法。 // DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 注册驱动的第二种方式:常用的。 // 为什么这种方式常用?因为参数是一个字符串,字符串可以写到xxx.properties文件中。 // 以下方法不需要接收返回值,因为我们只想用它的类加载动作。 Class.forName(“com.mysql.jdbc.Driver”); //反射机制进行类加载 //Class.forName()方法的执行会导致,方法里面的类进行类加载,然后静态代码块执行,完成驱动的注册 //2、获取连接 Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/bjpowernode”,”root”,”333”); // com.mysql.jdbc.JDBC4Connection@41cf53f9 System.out.println(conn);
}catch(SQLException e){
e.printStackTrace();
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
}
**将连接数据库的所有信息配置到配置文件当中**
<a name="x5Voq"></a>
## 从属性资源文件中读取连接数据库信息
Test04
```java
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bjpowernode
user=root
password=123456
配置文件:jdbc.properties
package jdbc;
import java.sql.*;
import java.util.ResourceBundle;
/*
实际开发中不建议把连接数据库的信息写死在java程序中。
*/
public class jdbc04 {
public static void main(String[] args) {
// 使用资源绑定器绑定属性配置文件
ResourceBundle bundle =ResourceBundle.getBundle("jdbcc");
String driver=bundle.getString("key");
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='销售部2',loc='天津'where deptno=20";
int count=stmt.executeUpdate(sql);
System.out.println(count==1?"修改成功":"修改失败");
} catch (SQLException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
//6、释放资源
if(stmt!=null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
处理查询结果集
笔记: 查询完的结果封装在 ResultSet里面
Test05
package jdbc;
import javax.swing.*;
import java.sql.*;
/*
处理查询结果集
*/
public class jdbc05 {
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
//1、注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode","root","123456");
//3、获取数据库操作对象
stmt=conn.createStatement();//
//4、执行sql
String sql="select empno,ename,sal from emp";
//ResultSet executeQuery(String sql) throws SQLEXception
//执行给定的SQL语句,该语句返回单词ResultSet对象。
// 参数sql 为要发送给数据库的SQL语句,通常为静态SQL select语句。
//int executeUpdate(insert/delete/update)
//ResultSet executeQuery(select)//ResultSet是执行sql语句后的查询结果集
rs=stmt.executeQuery(sql);
//5、处理查询结构集
/*理解过程
boolean flag1=rs.next();//本来在第0行,现在next到了第一行,如果有数据就返回true
// System.out.println(flag1); //true
if(flag1){
//光标指向的行有数据
//取数据
//getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出
String empno=rs.getString(1);//跟的是下标,第一列,第二列,第三列
String ename=rs.getString(2);//jdbc中所有的下标从1开始,不是从0开始的
String sal=rs.getString(3);
System.out.println(empno+","+ename+","+sal);
}
//rs.next();
//boolean next() throws SQLException将光标从当前位置向前移一行。
// ResultSet 光标最初位于第一行之前;第一次调用 next
// 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。
// 返回:如果新的当前行有效,则返回 true;如果不存在下一行,则返回 false
//抛出:SQLException - 如果发生数据库访问错误或在关闭的结果集上调用此方法
*/
while(rs.next()){
/*
String empno=rs.getString(1);
String ename=rs.getString(2);
String sal=rs.getString(3);
System.out.println(empno+","+ename+","+sal);
*/
/*另一种写法,不是以列的下标获取,以列的名字获取
String empno=rs.getString("empno");
String ename=rs.getString("ename");
String sal=rs.getString("sal");
System.out.println(empno+","+ename+","+sal);
*/
int empno=rs.getInt(1);//之所以这能用int 是因为底层数据是int类型的,不然要以String类型
String ename=rs.getString(2);
double sal=rs.getDouble(3);
System.out.println(empno+","+ename+","+(sal-100));//double类型 方便进行运算
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
//6、释放资源
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();
}
}
}
}
}
ResultSet rs 存的是执行sql语句后的查询结果集
遍历结果集:
IDEA工具开发JDBC
将JDBC的jar包导入idea步骤
- 第一种方式:
- 打开模块设置
- 鼠标点的这个英文单词为库
- 添加Java库,选择 JDBC.jar 包路径 导入
说明:如果建立新的模块需要将,jar包重新导入
第二种方式:
在需要导入的模块,建立目录,然后将JDBC的jar包粘贴到此目录
第二步:将jar包,加入到这个模块就行
快捷键
- ctrl +shift+F12
- ifn
idea中第一个JDBC程序
数据库表的设计阶段
- PowerDesigner 数据库表设计 工具的安装
作用:物理建模的,系统设计时用 PowerDesigner 工具
- 将激活文件到安装目录进行,替换
- 使用步骤:
快捷键:按住ctrl键+鼠标滑轮,就可以看见很多单元格
双击新建的表,在新的窗口进行设计
小项目
需求:模拟用户登录功能的实现
描述:
开发经验:能抽象成一个方法的最好抽象成一个方法
SQL注入问题
JDBCTest06 存在SQL注入问题
package jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class jdbc06 {
public static void main(String[] args) {
//初始化一个界面,可以让用户输入用户名和密码
Map<String,String>userLoginInfo=initUI();
//连接数据库验证用户和密码是否正确
boolean ok=checkNameAndPwd(userLoginInfo.get("loginName"),userLoginInfo.get("loginPwd"));
System.out.println(ok?"登陆成功":"登陆失败");
}
private static boolean checkNameAndPwd(String loginName, String loginPwd) {
boolean ok=false;//默认登录是失败的,
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
// 1、注册驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
// 3、获取数据库操作对象
stmt = conn.createStatement();
// 4、执行sql语句
String sql = "select * from t_user where login_name='"+loginName+"'and login_pwd='"+loginPwd+"'";
System.out.println(sql);
//程序执行到此处,才会将以上的sql语句发送到DBMS上,DBMS进行sql语句的编译。
rs = stmt.executeQuery(sql);//sql处理后会返回一个处理集,
//如果以上sql语句中用户名和密码是正确的,那么结果集最多也就查询出一条记录,所以以下不需要while循环。if就够了
if(rs.next()){//此条件如果成立,表示登录成功
ok=true;
}
// 5、处理结果集
} catch (Exception e) {
e.printStackTrace();
}finally {
// 6、释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn!= null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return ok;
}
private static Map<String, String> initUI(){
System.out.println("欢迎使用该系统,请输入用户名和密码进行身份认证");
Scanner s=new Scanner(System.in);
System.out.println("用户名:");
String loginName=s.nextLine();
System.out.println("密码:");
String loginPwd=s.nextLine();
//将用户名和密码放在map集合中
Map<String,String> useLoginInfo=new HashMap<>();
useLoginInfo.put("loginName",loginName);
useLoginInfo.put("loginPwd",loginPwd);
//返回map
return useLoginInfo;
}
/*
随便输入一个用户名的密码,登录成功了,这种现象被称为SQL注入现象!
导致SQL注入的根本原因是:用户不是一般的用户,用户是懂程序的,输入的用户名信息以及密码信息中,
含有SQL语句的关键词,这个SQL语句的关键字和底层的SQL语句进行“字符串拼接”,导致原SQL语句的含义
被扭曲了。最最主要的原因是:用户提供的信息参与了SQL语句的编译。
主要因素,这个程序是先进行字符串的拼接,然后再进行sql语句的编译,正好彼注入。
用户名:
fdsa
密码:
fdsa' or '1'='1
select * from t_user where login_name='fdsa'and login_pwd='fdsa' or '1'='1'
*/
}
解决SQL注入问题(重点学习)
JDBCTest07
package jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
怎么避免sql注入?
sql注入的根本原因是,先进行了字符串的拼接,然后在进行的编译。
java.sql.Statement接口的特点,先进行字符串的拼接。然后再进行sql语句的编译。
优点:使用Statement可以进行sql语句的拼接。
缺点:因为拼接的存在,导致可能给不法分子机会。
java.sql.PreparedStatement接口的特点,先进行sql语句的编译,然后在进行sql语句的传值。
优点:避免sql注入。
缺点:没有办法进行sql语句的拼接,只能给sql语句传值。
PreparedStatement预编译的数据库操作对象
*/
public class jdbc07 {
public static void main(String[] args) {
//初始化一个界面,可以让用户输入用户名和密码
Map<String,String>userLoginInfo=initUI();
//连接数据库验证用户和密码是否正确
boolean ok=checkNameAndPwd(userLoginInfo.get("loginName"),userLoginInfo.get("loginPwd"));
System.out.println(ok?"登陆成功":"登陆失败");
}
private static boolean checkNameAndPwd(String loginName, String loginPwd) {
boolean ok=false;//默认登录是失败的,
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
// 1、注册驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
// 3、获取预编译的数据库操作对象
//一个问号是一个占位符,一个占位符只能接收一个值/数据
String sql = "select * from t_user where login_name=? and login_pwd=?";
stmt=conn.prepareStatement(sql);//此时会发送sql给DBMS,进行sql语句的编译
//给占位符?传值
//怎么解决sql注入的?即使用户信息中有sql关键字,但是不参加编译就没事。
//stmt.setInt(1,100);//这样必须是填数字,↓下面那个必须填字符串。
stmt.setString(1,loginName);//给第一个占位符传值
stmt.setString(2,loginPwd);//给第二个占位符传值
// 4、执行sql语句
rs = stmt.executeQuery();//这个方法不需要将sql语句传递进去,不能是这样:rs = stmt.executeQuery(sql);因为上面已经把sql传过去了。
if(rs.next()){//此条件如果成立,表示登录成功
ok=true;
}
// 5、处理结果集
} catch (Exception e) {
e.printStackTrace();
}finally {
// 6、释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn!= null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return ok;
}
private static Map<String, String> initUI(){
System.out.println("欢迎使用该系统,请输入用户名和密码进行身份认证");
Scanner s=new Scanner(System.in);
System.out.println("用户名:");
String loginName=s.nextLine();
System.out.println("密码:");
String loginPwd=s.nextLine();
//将用户名和密码放在map集合中
Map<String,String> useLoginInfo=new HashMap<>();
useLoginInfo.put("loginName",loginName);
useLoginInfo.put("loginPwd",loginPwd);
//返回map
return useLoginInfo;
}
}
一个 ? 表示一个占位符
规律:就是第三步写好SQL语句,用占位符,然后给占位符传值
知识点的补充:当写第一个SQL语句时,会编译然后执行,若你后面在写一模一样的SQL语句就不会编译,直接执行。
Statement使用场景
JDBCTest8 必须使用SQL注入
先使用PreparedStatement-
这个程序输入desc或者asc会出错,因为
select ename,sal from emp order by sal ?会被换成select ename,sal from emp order by sal ‘desc’而不是select ename,sal from emp order by sal desc ,
PreparedStatement比较适合传值,Statement比较适合进行字符串的拼接。
京东升降序那不让用户输入,就是不给你注入的机会。
package jdbc;
import java.sql.*;
import java.util.Scanner;
/*
*需求:用户再控制台上输入desc则降序,输入asc则升序。
*思考以下,这个应该选择Statement还是PreparedStatement
选Statement,y因为PreparedStatement只能传值,不能进行sql语句的拼接。
*/
public class jdbc08 {
public static void main(String[] args){
Scanner s=new Scanner(System.in);
System.out.print("请输入desc或asc[desc是表示降序,asc表示升序]:");
//用户输入的
String orderKey=s.next();
//先使用PreparedStatement
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
//3、获取预编译的数据库操作对象
String sql="select ename,sal from emp order by sal ?";
ps=conn.prepareStatement(sql);
//给?传值
ps.setString(1,orderKey);
//4、执行sql语句
rs=ps.executeQuery();
while(rs.next()){
String ename=rs.getString("ename");
String sal=rs.getString("sal");
System.out.println(ename+","+sal);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBCTest09
再使用statement,此时可以执行了
package jdbc;
import java.sql.*;
import java.util.Scanner;
/*
*需求:用户再控制台上输入desc则降序,输入asc则升序。
*思考以下,这个应该选择Statement还是PreparedStatement
*/
public class jdbc09 {
public static void main(String[] args){
Scanner s=new Scanner(System.in);
System.out.print("请输入desc或asc[desc是表示降序,asc表示升序]:");
//用户输入的
String orderKey=s.next();
//再使用Statement
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
//3、获取数据库操作对象
stmt = conn.createStatement();
//4、执行sql语句
String sql="select ename,sal from emp order by sal "+orderKey;
rs=stmt.executeQuery(sql);
while(rs.next()){
String ename=rs.getString("ename");
String sal=rs.getString("sal");
System.out.println(ename+","+sal);
}
} 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();
}
}
}
}
}
使用PreparedStatement完成增删改
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class jdbc10 {
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://127.0.0.1:3306/bjpowernode", "root", "123456");
//3、获取预编译的数据库操作对象
//增加操作
/*String sql="insert into dept(deptno,dname,loc) values(?,?,?)";
ps=conn.prepareStatement(sql);
//给?传值
ps.setInt(1,50);
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,50);*/
//删除操作
String sql="delete from dept where deptno = ?";
ps=conn.prepareStatement(sql);
ps.setInt(1,50);
// 4、执行sql
int count=ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
}finally {
//6、释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
模糊查询
package jdbc;
import java.sql.*;
public class jdbc11 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
//3、获取预编译的数据库操作对象
//找出名字中含有O的
//以下第一种方法是错误的,因为?不能放在单引号内
/*String sql="select ename from emp where ename like '%?%'";
ps=conn.prepareStatement(sql);
ps.setString(1, "O");*/
String sql="select ename from emp where ename like ?";
ps=conn.prepareStatement(sql);
ps.setString(1, "%O%");
// 4、执行sql
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//6、释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBC 里面的事务
JDBCTest10 测试JDBC里面的事务自动提交【需要手动关掉自动提交】
JDBCTest11 账户转账演示事务【JDBC的事务控制,基于单机事务】
知识点补充:有效数字,从左边起,不为0的数字为有效数字
快捷键补充:ait+shift+inster 可以复制前面不带 * 号的内容 也可以批量编辑
如果程序在执行过程中,有异常,就得手动回滚,保证数据的安全性
JDBC工具类的封装
外部文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bjpowernode
user=root
password=123456
封装类 ```css package jdbc.utils; //在一个没有结束的程序中,DBUtil这个类只加载一次
import java.sql.*; import java.util.ResourceBundle;
/ 数据库工具类,便于JDBC的代码编写 / public class DBUtil { //静态变量和静态代码块的执行时候是一样的,先出现的先执行 //构造方法私有化是为了防止new对象,为什么要防止用对象? //因为工具类中的方法都是静态的,不需要new对象,直接用“类名.”的方式调用 private DBUtil(){};//工具类的构造方法私有化的 //类加载时绑定属性资源文件 private static ResourceBundle bundle=ResourceBundle.getBundle(“jdbcc”);
//保证注册驱动在类加载的时执行且只执行一次,则使用静态代码块
static{ try { Class.forName(bundle.getString(“driver”)); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
//获得数据库连接对象
public static Connection getConnection() throws SQLException {
String url=bundle.getString("url");
String user=bundle.getString("user");
String password=bundle.getString("password");
Connection conn=DriverManager.getConnection(url,user,password);
return conn;
}
/**
* 释放资源
* conn 连接对象
* stmt 数据库操作对象
* rs 查询结果集
*/
public static void close(Connection conn, Statement stmt,ResultSet rs){
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();
}
}
}
}
测试类
```css
package jdbc;
import java.sql.*;
public class jdbc11 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode", "root", "123456");
//3、获取预编译的数据库操作对象
//找出名字中含有O的
//以下第一种方法是错误的,因为?不能放在单引号内
/*String sql="select ename from emp where ename like '%?%'";
ps=conn.prepareStatement(sql);
ps.setString(1, "O");*/
String sql="select ename from emp where ename like ?";
ps=conn.prepareStatement(sql);
ps.setString(1, "%O%");
// 4、执行sql
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//6、释放资源
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBC 里面的行级锁
1.行级锁的理解:
在 select 语句后面添加了 for update 就表示 emp 表 中工作岗位等于 MANAGER 的行级记录谁都不能动,这叫行级锁
当前事务没有结束的时候,表中那三行记录,就不能动,其它事务无法对着三行记录,进行操作。
行级锁,又被称为悲观锁
关于DQL语句的悲观锁
对于一个DQL语句来说,末尾是可以添加这样一个关键字的:for update
select ename,sal from emp where job=‘MANAGER’ for update ;
以上sql语句的含义是:
在本次事务的执行过程中,job=’MANAGER’的记录被查询,
这些记录在我查询的过程中,任何人,任何事务都不能对
这些记录进行修改操作,直到我当前事务结束。
这种机制被称为:行级锁机制(又叫做悲观锁!)
在mysql中是这样的,
当使用select…where…for updare…时,mysql进行row lock(行锁)还是table lock(表锁)只取决于是否能使用索引(例如主键,unique字段),能则为行锁,否则为表锁:
未查到数据则无锁,而使用’<>’,’like’等操作时,索引会失效,自然进行的是table lock。
所以慎用for update。
整个表锁住的时候会导致性能性能降低,谨慎使用。
使用for update的时候,最好是锁主键值,或者具有unique约束的字段,
锁别的字段可能会导致整个表锁住
逛敲逛练,不用 idea 开发工具,手写出JDBC开发的六步,熟练掌握JDBC里面的代码
挑选一道作业题写到 Java 语句里面