JDBC
概念
Java DataBase Connectivity Java 数据库连接, Java语言操作数据库。JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。JDBC 规范定义接口,具体的实现由各大数据库厂商来实现。JDBC 是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。所以我们只需要会调用 JDBC 接口中的方法即可,数据库驱动由数据库厂商提供。
- 程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
- 使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库
使用 JDBC 开发使用到的包:
JDBC 的核心 API
JDBC 访问数据库的步骤
1:注册和加载驱动
2:获取连接使用DriverManager类中的getConnection方法,获取连接对象
3:Connection连接对象 获取 Statement/getConnection 执行SQL 对象
4:使用Statement/getConnection 执行SQL 对象执行 SQL 语句
5:返回结果集
6: 释放资源
详解JDBC核心API
DriverManager类:驱动管理对象
- 管理和注册驱动: 告诉程序该使用哪一个数据库驱动jar
详解:
我们通过下面语句注册驱动
查看com.mysql.jdbc.Driver 类 源代码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.mysql.jdbc; import java.sql.DriverManager; import java.sql.SQLException; // Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类 public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
结论:
我们写代码使用: Class.forName("com.mysql.jdbc.Driver");注册驱动。底层是通过静态代码块调用 DriverManager 类中的static void registerDriver(Driver driver) 方法加载驱动。从 JDBC3 开始,目前已经普遍使用的版本。可以不用注册驱动而直接使用。Class.forName 这句话可以省略。不建议省略,因为不会向下兼容。
- 创建数据库的连接
- 协议名:子协议://服务器名或 IP 地址:端口号/数据库名?参数=参数值
小技巧:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
示例:
package jdbc; import java.sql.Connection; import java.sql.DriverManager; public class Demo2 { public static void main(String[] args) throws Exception { String url = "jdbc:mysql://localhost:3306/day24"; //1) 使用用户名、密码、URL 得到连接对象 Connection connection = DriverManager.getConnection(url, "root", "root"); //com.mysql.jdbc.JDBC4Connection@68de145 System.out.println(connection); } }
Connection接口:数据库连接对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
管理事务:
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
- 提交事务:commit()
- 回滚事务:rollback()
Statement接口:执行sql的对象
- boolean execute(String sql) :可以执行任意的sql
常用方法
需求
执行DDL语句,创建一个student表
代码实现
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * 执行DDL语句,创建一个student表 */ public class Demo1 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.获取连接对象 conn = DriverManager.getConnection("jdbc:mysql:///one", "root", "root"); //3.定义sql String sql = "create table student (id int , name varchar(20))"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql); //6.处理结果 System.out.println(count); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //7.释放资源 if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
student表 添加一条记录 insert 语句
package jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * student 表 添加一条记录 insert 语句 */ public class Demo2 { public static void main(String[] args) { Statement stmt = null; Connection conn = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 定义sql String sql = "insert into student values(null,'王五')"; //3.获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql:///one", "root", "root"); //4.获取执行sql的对象 Statement stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql);//影响的行数 //6.处理结果 System.out.println(count);//1 if (count > 0) { System.out.println("添加成功!"); } else { System.out.println("添加失败!");//添加成功! } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //stmt.close(); //7. 释放资源 //避免空指针异常 if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
ResultSet:结果集对象
常用数据类型转换表
java.sql.Date、Time、Timestamp(时间戳),三个共同父类是:java.util.Date
- 如果光标在第一行之前,使用 rs.getXX()获取列值,报错:Before start of result set
- 如果光标在最后一行之后,使用 rs.getXX()获取列值,报错:After end of result set
- 使用完毕以后要关闭结果集 ResultSet,再关闭 Statement,再关闭 Connection
需求
查询student类中的所有数据,并且对结果集遍历
package jdbc; import java.sql.*; /** * 执行DDL语句 */ public class Demo3 { 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:///one", "root", "root"); //3.定义sql String sql = "select * from student"; //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql rs = stmt.executeQuery(sql); //6.处理结果 //循环判断游标是否是最后一行末尾。 while (rs.next()) { //获取数据 //6.2 获取数据 int id = rs.getInt("id"); String name = rs.getString("name"); System.out.println(id + "---" + name);//0---王五 } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { //7.释放资源 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(); } } } } }
通过上述代码,我们发现冗余的代码太多怎么办?解决办法:定义工具类
数据库工具类 JdbcUtils
创建类 JdbcUtil 包含 3 个方法:
- 可以把几个字符串定义成常量:用户名,密码,URL,驱动类(写成配置文件)
- 得到数据库的连接:getConnection() (通过读取配置文件,与不同的数据库连接)
- 关闭所有打开的资源:
步骤1:
在src文件下 新建配置文件,输入配置信息如下:
url=jdbc:mysql:///数据库名称 user=root password=root driver=com.mysql.jdbc.Driver
步骤2:
新建一个util包,存放我们写的配置JDBCUtils类,代码如下:
package util; import java.io.FileReader; import java.io.IOException; import java.net.URL; import java.sql.*; import java.util.Properties; /** * JDBC工具类 */ public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 */ static { //读取资源文件,获取值。 try { //1. 创建Properties集合类。 Properties pro = new Properties(); //获取src路径下的文件的方式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); // System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties //2. 加载文件 // pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties")); pro.load(new FileReader(path)); //3. 获取数据,赋值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注册驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连接 * * @return 连接对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 释放资源 * * @param stmt * @param conn */ public static void close(Statement stmt, Connection conn) { if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 释放资源 * * @param stmt * @param conn */ public static void close(ResultSet rs, Statement stmt, Connection conn) { 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(); } } } }
需求
1. 通过键盘录入用户名和密码,判断用户是否登录成功
MySQL数据库中准备数据
2.编写登录类
package jdbc; import util.JDBCUtils; import java.sql.*; import java.util.Scanner; public class Demo4 { /** * 练习: * * 需求: * 1. 通过键盘录入用户名和密码 * 2. 判断用户是否登录成功 */ public static void main(String[] args) { //1.键盘录入,接受用户名和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.nextLine(); System.out.println("请输入密码:"); String password = sc.nextLine(); //2.调用方法 boolean flag = new Demo4().login(username, password); //3.判断结果,输出不同语句 if (flag) { //登录成功 System.out.println("登录成功!"); } else { System.out.println("用户名或密码错误!"); } } /** * 登录方法 */ public boolean login(String username, String password) { if (username == null || password == null) { return false; } //连接数据库判断是否登录成功 Connection conn = null; Statement stmt = null; ResultSet rs = null; //1.获取连接 try { try { conn = JDBCUtils.getConnection(); } catch (SQLException e) { e.printStackTrace(); } //2.定义sql String sql = "select * from USER where name = '" + username + "' and password = '" + password + "' "; //3.获取执行sql的对象 stmt = conn.createStatement(); //4.执行查询 rs = stmt.executeQuery(sql); //5.判断 return rs.next();//如果有下一行,则返回true } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, stmt, conn); } return false; } }
SQL 注入问题
- 使用PreparedStatement对象来解决
PreparedStatement 接口
Connection 创建 PreparedStatement 对象
PreparedStatement 接口中的方法:
- prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
- 安全性更高,没有 SQL 注入的隐患。
- 提高了程序的可读性
使用 PreparedStatement 的步骤:
- 编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?";
- 获得 PreparedStatement 对象
- 设置实际参数:setXxx(占位符的位置, 真实的值)
- 执行参数化 SQL 语句
- 关闭资源
package jdbc; import util.JDBCUtils; import java.sql.*; import java.util.Scanner; public class Demo4 { /** * 练习: * * 需求: * 1. 通过键盘录入用户名和密码 * 2. 判断用户是否登录成功 */ public static void main(String[] args) { //1.键盘录入,接受用户名和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username = sc.nextLine(); System.out.println("请输入密码:"); String password = sc.nextLine(); //2.调用方法 boolean flag = new Demo4().login2(username, password); //3.判断结果,输出不同语句 if (flag) { //登录成功 System.out.println("登录成功!"); } else { System.out.println("用户名或密码错误!"); } } /** * 登录方法,使用PreparedStatement实现 */ public boolean login2(String username, String password) { if (username == null || password == null) { return false; } //连接数据库判断是否登录成功 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; //1.获取连接 try { conn = JDBCUtils.getConnection(); //2.定义sql String sql = "select * from user where name = ? and password = ?"; //3.获取执行sql的对象 pstmt = conn.prepareStatement(sql); //给?赋值 pstmt.setString(1, username); pstmt.setString(2, password); //4.执行查询,不需要传递sql rs = pstmt.executeQuery(); //5.判断 return rs.next();//如果有下一行,则返回true } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, pstmt, conn); } return false; } }
JDBC控制事务:
事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作:
- 开启事务
- 提交事务
- 回滚事务
使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务我们一般在执行sql之前开启事务。
- 提交事务:commit() 我们一般当所有sql都执行完提交事务
- 回滚事务:rollback() 我们一般在catch中回滚事务
来源:https://www.cnblogs.com/wurengen/p/12305072.html