すのはら荘春原庄的雪

JDBC的学习笔记

Toretto·2022-11-18 16:26·26 次阅读

JDBC的学习笔记

1、JDBC是什么?

1.1 JDBC:Java DataBase Connectivity(Java语言连接数据库),是SUN公司制定的一套接口(interface)
思考:为什么sun制定一套JDBC的接口呢?
​ 因为每一个数据库的底层实现原理都不一样

1.2 JDBC的接口的实现者与调用者之间的关系
image

1.3 JDBC的本质:一套接口

2、JDBC编程六步(需要背会)

image-20221119150515984

2.1 注册驱动#

​ 有两种方式:

​ 第一种:

​ //先导包

​ import java.sql.*;

​ Driver driver = new com.mysql.jdbc.Driver();

​ DriverManager.registerDriver(driver);

​ 第二种(常用):

​ //通过类加载去注册驱动,因为在MySQL的驱动中的Driver方法里的静态代码块已经实现了注册驱动

​ Class.forname("com.mysql.jdbc.Driver");

2.2 获取连接#

​ Connection conn = DriverManager.getConnection(String url, String user, String password);

​ System.out.println("数据库连接对象", + conn);

2.3 获取数据库操作对象(用来执行sql语句)#

​ Statement stmt = conn.createStatement();

2.4 执行sql语句#

​ String sql = "DML语句";

​ //executeUpdate();这个方法专门执行DML语句,返回值是”影响数据库中的记录条数“

​ int count = stmt.executeUpdate(String sql);

​ 例:System.out.println(count == 1 ? "保存成功" : "保存失败");

2.5 处理查询结果集#

​ String sql = "DQL语句";

​ //executeQuery(); 这个方法专门执行DQL语句,其返回值是一个ResultSet集合

​ ResultSet rs = stmt.executeQuery(String sql);

这个ResultSet集合的图示:

image-20221119215158377

Copy
while(rs.next()){ String empno = rs.getString("deptno"); String ename = rs.getString("dname"); String sal = rs.getString("loc"); System.out.println(empno + "," + ename + "," + sal); }

​ getString("deptno"); 这个方法里的列表名是以查询结果里的列表名为准,而不是实际的表格里的列表名。

2.6 释放资源#

​ 写在finally的子句中,释放资源应该遵循从小到大的顺序释放,并且分别对其捕捉异常

Copy
finally { //6、释放资源(遵循从小到大原则,并且分别对其进行捕捉异常) try{ if (stmt != null) { stmt.close(); } }catch (SQLException e){ e.printStackTrace(); } try{ if (conn != null) { conn.close(); } }catch (SQLException e){ e.printStackTrace(); } }

编程六步代码:#

Copy
package jdbc; import java.sql.*; import java.util.ResourceBundle; public class JdbcTest05 { 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 conn = null; Statement stmt = null; ResultSet rs = null; try{ //1、注册驱动 Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,password); //3、获取数据库操作对象 stmt = conn.createStatement(); //4、执行sql语句 String sql = "select * from dept"; rs = stmt.executeQuery(sql); //5、处理查询结果集 while(rs.next()){ String empno = rs.getString("deptno"); String ename = rs.getString("dname"); String sal = rs.getString("loc"); System.out.println(empno + "," + ename + "," + sal); } } catch (ClassNotFoundException | SQLException 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(); } } } } }

3、SQL注入现象

3.1 用户登录业务代码#

image-20221120111730402

Copy
package jdbc; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; public class JdbcTest06 { public static void main(String[] args) { //用户登录,输入用户名和密码 Map<String,String> userLoginInfo = userLogin(); //接收到用户名和密码之后,验证用户名和密码是否正确 boolean LoginFlag = login(userLoginInfo); System.out.println(LoginFlag ? "登录成功" : "登录失败"); } /** * * @param userLoginInfo 用户的登录信息 * @return true表示登录成功 * false表示登录失败 */ private static boolean login(Map<String, String> userLoginInfo) { boolean LoginFlag = false; ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ //注册驱动 Class.forName(bundle.getString("driver")); //获取连接 String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password"); conn = DriverManager.getConnection(url,user,password); //获取数据库操作对象 stmt = conn.createStatement(); //执行sql语句 String userName = userLoginInfo.get("userName"); String userPassword = userLoginInfo.get("userPassword"); String sql = "select * from t_user where userName = '"+userName+"' and userPassword = '"+userPassword+"'"; rs = stmt.executeQuery(sql); LoginFlag = rs.next(); }catch (ClassNotFoundException | SQLException 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(); } } } return LoginFlag; } /** * * @return 返回用户输入的用户名和密码,返回值是一个Map集合 */ private static Map<String, String> userLogin() { Scanner s = new Scanner(System.in); //输入用户名 System.out.println("请输入用户名:"); String userName = s.nextLine(); //输入密码 System.out.println("请输入密码:"); String userPas = s.nextLine(); Map<String,String> userLogInfo = new HashMap<>(); userLogInfo.put("userName",userName); userLogInfo.put("userPassword",userPas); return userLogInfo; } }

以上代码存在SQL注入问题:用户输入的中带有sql关键字,编译的时候将sql关键字执行了,曲解了密码的原意

​ 请输入用户名:
​ fdsa
​ 请输入密码:
​ fdsa' or '1'='1
​ 登录成功

这种情况导致不法分子可以随便登录某一个账户,不安全,需要解决

3.2 解决SQL注入#

image-20221120111642577

image-20221120112341676

代码演示

Copy
package jdbc; import java.sql.*; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.Scanner; public class JdbcTest07 { public static void main(String[] args) { //用户登录,输入用户名和密码 Map<String,String> userLoginInfo = userLogin(); //接收到用户名和密码之后,验证用户名和密码是否正确 boolean LoginFlag = login(userLoginInfo); System.out.println(LoginFlag ? "登录成功" : "登录失败"); } /** * * @param userLoginInfo 用户的登录信息 * @return true表示登录成功 * false表示登录失败 */ private static boolean login(Map<String, String> userLoginInfo) { boolean LoginFlag = false; ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try{ //注册驱动 Class.forName(bundle.getString("driver")); //获取连接 String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password"); conn = DriverManager.getConnection(url,user,password); //获取数据库操作对象 String userName = userLoginInfo.get("userName"); String userPassword = userLoginInfo.get("userPassword"); String sql = "select * from t_user where userName = ? and userPassword = ?"; ps = conn.prepareStatement(sql); //给占位符?传值 ps.setString(1,userName); ps.setString(2,userPassword); //执行sql语句 rs = ps.executeQuery(); LoginFlag = rs.next(); }catch (ClassNotFoundException | SQLException 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(); } } } return LoginFlag; } /** * * @return 返回用户输入的用户名和密码,返回值是一个Map集合 */ private static Map<String, String> userLogin() { Scanner s = new Scanner(System.in); //输入用户名 System.out.println("请输入用户名:"); String userName = s.nextLine(); //输入密码 System.out.println("请输入密码:"); String userPas = s.nextLine(); Map<String,String> userLogInfo = new HashMap<>(); userLogInfo.put("userName",userName); userLogInfo.put("userPassword",userPas); return userLogInfo; } }

测试结果:

​ 请输入用户名:
​ fdas
​ 请输入密码:
​ fdas' or '1'='1
​ 登录失败

以上代码利用PreparedStatement接口解决了SQL注入的问题

3.3 用PreparedStatement进行crud#

Copy
package jdbc; import java.sql.*; import java.util.ResourceBundle; /** * 用PreparedStatement进行crud */ public class JdbcTest08 { public static void main(String[] args) { Update(); } private static void Update() { ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); Connection conn = null; PreparedStatement ps = null; try { //注册驱动 Class.forName(bundle.getString("driver")); //获取连接 String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password"); conn = DriverManager.getConnection(url, user, password); //获取预编译数据库操作对象 //String sql = "insert into dept values(?,?,?)"; 插入一条数据 //String sql = "Update dept set deptno = ?,dname = ?,loc = ? where deptno = 50"; 修改一条数据 String sql = "delete from dept where deptno = ?"; //删除一条数据 ps = conn.prepareStatement(sql); //给占位符?传值 ps.setInt(1,60); /*ps.setString(2,"销售部"); ps.setString(3,"永州");*/ //执行sql语句 int count = ps.executeUpdate(); System.out.println(count); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }

3.4 演示JDBC的自动提交事务#

Copy
package jdbc; import java.sql.*; import java.util.ResourceBundle; public class JdbcTest09 { 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 conn = null; PreparedStatement ps = null; try{ //1、注册驱动 Class.forName(driver); //2、获取连接 conn = DriverManager.getConnection(url,user,password); conn.setAutoCommit(false); //3、获取数据库操作对象 String sql = "Update t_act set balance = ? where account = ?"; ps = conn.prepareStatement(sql); ps.setInt(1,10000); ps.setDouble(2,10); //4、执行sql语句 int count = ps.executeUpdate(); String str = null; str.length(); sql = "Update t_act set balance = ? where account = ?"; ps = conn.prepareStatement(sql); ps.setInt(1,10000); ps.setDouble(2,20); count += ps.executeUpdate(); conn.commit(); System.out.println(count); } catch (ClassNotFoundException | SQLException e) { if(conn != null) { try { conn.rollback(); } catch (SQLException ex) { e.printStackTrace(); } } e.printStackTrace(); }finally { if (ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }

​ JDBC中是默认自动提交事务的,但是这并不符合编程的业务。比如上面的银行转账事务演示代码中账号10在向账号20转账10000元的时候遇到了异常,程序终止。这个时候这10000应该是没有转出去才对,可是因为JDBC的事务是自动提交的,在遇到异常之前账号10的10000元就已经转出去了,可是却没有落到账号20的账上。少了10000元钱!!!

​ 写JDBC程序的时候,需要将自动提交事务关闭

​ Connection接口中有关事务的三个方法:

​ conn.setAutoCommit(false); 默认为自动提交事务,false即为关闭自动提交

​ conn.commit(); 提交事务,在执行完一个事务之后,手动进行提交,一般放在事务语句的最后

​ conn.rollback(); 回滚事务,事务执行失败之后,手动进行回滚,一般放在catch子句当中(事务执行失败,跳转到catch子句捕捉异常,此时回滚事务)

4、JDBC工具类的封装

4.1 代码演示#

Copy
package jdbc.tool; import java.sql.*; import java.util.ResourceBundle; /** * JDBC工具类的封装,简化JDBC编程 */ public class DBTool { static{ //注册驱动 try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } private DBTool() {} /** * 获取连接对象 * @return 返回连接对象 * @throws SQLException 异常上抛 */ public static Connection getConnection() throws SQLException { ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String url = bundle.getString("url"); String user = bundle.getString("user"); String password = bundle.getString("password"); //获取连接 return DriverManager.getConnection(url,user,password); } /** * 释放资源 * @param conn 连接对象 * @param stmt 数据库操作对象 * @param 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(); } } } }

4.2 测试工具类#

Copy
package jdbc; import jdbc.tool.DBTool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 1、测试封装的JDBC工具类好不好用 * 2、实现模糊查询 */ public class JdbcTest10 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { //获取连接 conn = DBTool.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()){ System.out.println(rs.getString(1)); } } catch (SQLException e) { e.printStackTrace(); }finally { DBTool.close(conn,ps,rs); } } }

5、悲观锁(行级锁)和乐观锁机制

image-20221120190541586

5.1 演示行级锁机制#

以下代码进行行级锁,称为“事务一”

Copy
package jdbc; import jdbc.tool.DBTool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 演示悲观锁(行级锁) * 这里进行行级锁 */ public class JdbcTest11 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBTool.getConnection(); 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()){ System.out.println(rs.getString(1) + "," + rs.getString(2) + "," + rs.getString(3)); } conn.commit(); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { e.printStackTrace(); } } e.printStackTrace(); }finally { DBTool.close(conn,ps,rs); } } }

以下代码对锁定的行进行修改,称为“事务二”

Copy
package jdbc; import jdbc.tool.DBTool; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; /** * 这里对锁定的行进行修改 */ public class JdbcTest12 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { conn = DBTool.getConnection(); 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 (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { e.printStackTrace(); } } e.printStackTrace(); }finally { DBTool.close(conn,ps,null); } } }

测试结果:

​ 在事务一没有提交事务之前,事务二的程序只能卡着无法往下执行;直到事务一提交事务之后,事务二立马输出3

posted @   钟望曦  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
目录