JDBC学习笔记
文章目录
- 1.前言
- 2. JDBC编程六步骤
-
- 2.1示例
- 3.进阶写法
- 4.处理查询结果集
- 5.关于SQL注入
-
- 5.1代码展示
- 5.2关于Statement和PreparedStatement的区别
- 6.JDBC事务自动提交机制
-
- 6.1代码演示
- 6.2手动修改事务
- 7.JDBC工具类的封装
-
- 7.1封装代码
- 7.2使用JDBC实现模糊查询
- 8.悲观锁和乐观锁的概念
-
- 8.1悲观锁(行级锁)
- 8.2乐观锁
1.前言
(1)JDBC概念:Java DataBase Connectivity(Java语言连接数据库)
(2)本质:是SUN公司制定的一套接口
2. JDBC编程六步骤
(1)注册驱动(告诉Java程序,即将要连接的是哪个品牌的数据库)
(2)获取连接(表示JVM的进程和数据库进程之间的通道打开了,这是属于进程之间的通信,重量级的,使用完之后一定要关闭通道)
(3)获取数据库操作对象(专门执行sql语句的对象)
(4)执行SQL语句(DQL,DML)
(5)处理查询结果集(只有当第四步执行的是select语句的时候才有这第五步处理查询结果集)
(6)释放资源(使用完资源以后一定要关闭资源。Java和数据库属于进程间的通信,使用完一定要关闭)
2.1示例
import java.sql.*;public class JDBCTest { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{ //1.注册驱动 Driver driver = com.mysql.jdbc.Driver(); DriverManager.registerDriver(driver); //2.获取连接 /** * url:统一资源定位符 * 比如一个网址就是url * URL由协议、IP、Port、资源名字 组成 * * http://182.61.200.7:80/index.html * http://:通信协议 * 182.61.200.7:80:是IP地址 * 80:是端口号 * index.html:是服务器上的某个资源名 * * jdbc:mysql://127.0.0.1:3306/bjpowernode * jdbc:mysql://:是协议 * 127.0.0.1是本机IP地址 * 3306是MySQL服务端口号 * bjpowernode是需要访问的资源 * * 所谓通信协议就是组织好的数据传输格式 * * */ String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode"; String user = "root"; String password = "1017"; conn = DriverManager.getConnection(url,user,password); //3.获取数据库操作对象,Statement专门执行sql语句。 stmt = conn.createStatement(); //4.执行sql语句 String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')"; /** * 专门执行DML语句(update,delete,insert) * 返回值是影响数据库中的数据条数(比如插入三条就返回3) * */ int count = stmt.executeUpdate(sql); System.out.println(count == 1?"保存成功":"保存失败"); //5.处理查询结果集 }catch (SQLException e){ e.printStackTrace(); }finally { //6.释放资源 /** * 为了保证资源一定释放,在finally中释放资源 * 释放资源要保证从小到大依次释放。比如statement是由connection对象产生的,我们要先关闭statement再关闭connection对象 * 释放资源要写在try...catch块里 * */ try { if (stmt!=null) stmt.close(); }catch (SQLException e){ e.printStackTrace(); } try { if (conn!=null) conn.close(); }catch (SQLException e){ e.printStackTrace(); } } }}
3.进阶写法
import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;import java.util.ResourceBundle;public class JDBCTest02 { /** * 注册驱动最常用的一种方法 * * 在实际开发中,不建议把连接数据库的信息写死到程序里面去 * */ 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; try { //1.注册驱动 /** * 这种方式常用是因为参数是字符串,字符串可以写入xxx.properties文件中 * */ Class.forName(driver);//使用反射机制将驱动加载到内存当中。 //2.获取连接 conn = DriverManager.getConnection(url,user,password); //3.获取数据库操作对象,Statement专门执行sql语句。 stmt = conn.createStatement(); //4.执行sql语句 String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')"; int count = stmt.executeUpdate(sql); System.out.println(count == 1?"保存成功":"保存失败"); //5.处理查询结果集 }catch (SQLException e){ e.printStackTrace(); }catch (ClassNotFoundException 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(); } } }}
4.处理查询结果集
import java.sql.*;public class JDBCTest03 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","1017"); //3.获取数据库操作对象,Statement专门执行sql语句。 stmt = conn.createStatement(); //4.执行sql语句 String sql = "select empno,ename,sal from emp"; //int count = stmt.executeUpdate(sql);<---专门处理DML语句的方法 rs = stmt.executeQuery(sql); //5.处理查询结果集 boolean flag1 = rs.next(); /** * getString()方法的特点是不管数据库中的数据类型是什么,都是以String的形式取出. * 并且其中要求的参数是列的下标(getString(1))或者是列名(getString("ename"),如果列名被重写必须要使用被重写的列名) * * 除了可以以String类型取出之外,还可以以特定的类型取出 * 比如:int empno = rs.getInt("empno") * * 不过要注意的是,jdbc中的下标从1开始不是从0开始 * */ while (rs.next()){ String empno = rs.getString(1); String ename = rs.getString(2); String sal = rs.getNString(3); System.out.println(empno+","+ename+","+sal); } }catch (SQLException e){ e.printStackTrace(); }catch (ClassNotFoundException 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(); } } }}
5.关于SQL注入
5.1代码展示
import java.sql.*;import java.util.HashMap;import java.util.Map;import java.util.Scanner;/** * 如何解决SQL注入的问题: * * 使用java.sql.PreparedStatement * 该类继承了statement,其属于预编译的数据库操作对象。 * 其原理是:预先对SQL语句的框架进行编译,然后再给sql语句传值。 * * */public class JDBCTest05 { public static void main(String[] args) { Map<String, String> userLoginInfo = init(); boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess?"登录成功":"登录失败"); } private static boolean login(Map<String,String> userLoginInfo) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; String userName = userLoginInfo.get("userName"); String passWord = userLoginInfo.get("passWord"); boolean loginSuccess = false; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","1017"); String sql = "select * from t_user where loginName=? and loginPwd=?"; ps = conn.prepareStatement(sql); ps.setString(1,userName); ps.setString(2,passWord); rs = ps.executeQuery(); if (rs.next()){ loginSuccess = true; } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if (rs!=null){ try { rs.close(); }catch (Exception e){ e.printStackTrace(); } } if (ps!=null){ try { ps.close(); }catch (Exception e){ e.printStackTrace(); } } if (conn!=null){ try { conn.close(); }catch (Exception e){ e.printStackTrace(); } } } return loginSuccess; } private static Map<String, String> init() { Scanner in = new Scanner(System.in); System.out.println("用户名:"); String userName = in.nextLine(); System.out.println("密码:"); String passWord = in.nextLine(); Map<String, String> userLoginInfo = new HashMap<>(); userLoginInfo.put("userName",userName); userLoginInfo.put("passWord",passWord); return userLoginInfo; }}
5.2关于Statement和PreparedStatement的区别
Statement存在注入问题;而PreparedStatement存在注入问题。
Statement是编译一次执行一次;而PreparedStatement是编译一次执行N次。
PreparedStatement会在编译阶段做类型的安全检查。
大多数情况下使用PreparedStatement,只有在业务要求SQL注入的时候才会使用Statement。
6.JDBC事务自动提交机制
6.1代码演示
import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;/** * JDBC事务机制: * 1.JDBC中的事务是自动提交,什么是自动提交? * 只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。 * 但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须 * 保证他们这些DML语句在同一个事务当中同时成功或者同时失败。 * */public class JDBCTest06 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","1017"); String sql = "update dept set dname = ? where deptno=?"; ps = conn.prepareStatement(sql);//操作1 ps.setString(1,"X部门"); ps.setInt(2,30); int count = ps.executeUpdate(); System.out.println(count);//操作2 ps.setString(1,"Y部门"); ps.setInt(2,30); int count2 = ps.executeUpdate(); System.out.println(count2); } catch (Exception e) { e.printStackTrace(); }finally{ if (ps!=null){ try { ps.close(); }catch (Exception e){ e.printStackTrace(); } } if (conn!=null){ try { conn.close(); }catch (Exception e){ e.printStackTrace(); } } } }}
我们可以对以上代码进行Debug,不难发现,当操作1代码执行完毕以后我们的数据库就已经发生了改变。
6.2手动修改事务
//将Connection对象禁用自动提交机制。conn.setAutoCommit(false);//在操作完成以后我们使用commit()方法手动提交。conn.commit();//为了保证数据安全,最后还要在catch块中手动调用事务回滚 catch (Exception e) { conn.rollback(); e.printStackTrace(); }
7.JDBC工具类的封装
7.1封装代码
public class DBUtil { private DBUtil(){} static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","1017"); } public static void close(Connection conn, Statement ps, ResultSet rs){ if (rs != null){ try { rs.close(); }catch (Exception e){ e.printStackTrace(); } } if (ps != null){ try { ps.close(); }catch (Exception e){ e.printStackTrace(); } } if (conn != null){ try { conn.close(); }catch (Exception e){ e.printStackTrace(); } } }}
7.2使用JDBC实现模糊查询
public class DBUtil { private DBUtil(){} static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","1017"); } public static void close(Connection conn, Statement ps, ResultSet rs){ if (rs != null){ try { rs.close(); }catch (Exception e){ e.printStackTrace(); } } if (ps != null){ try { ps.close(); }catch (Exception e){ e.printStackTrace(); } } if (conn != null){ try { conn.close(); }catch (Exception e){ e.printStackTrace(); } } }}
8.悲观锁和乐观锁的概念
8.1悲观锁(行级锁)
select ename,job,sal from emp where job="MANAGER" for update;
在sql语句末尾加上for update表示悲观锁,在事务结束之前,其他线程无法对悲观锁锁上的数据进行操作,所有事务都必须排队执行,不允许并发。
8.2乐观锁
支持并发,事务也不需要排队,只不过需要一个版本号
比如说一条记录具有版本号1.1。有两个事务分别是:A、B其中事务A先进行修改,完成以后版本号变为1.2,事务B是后进行修改的,修改以后准备提交发现版本号变为1.2,和他最初读的版本号不一样,所以它选择回滚。