导学

JDBC的全称为:Java DataBase Connectivity(java数据库连接),这是一门Java连接数据库的技术,可对多种数据库进行统一操作,不需要对单个数据库的驱动进行详细了解。
5.JDBC入门 - 图1
【仅需要了解 JDBC 的规范即可】 - 具体驱动会具体实现相应功能
image.png

开发步骤

  1. 选择数据库:加载数据库驱动
  2. 连接数据库
  3. 创建数据库查询
  4. 获取查询结果
  5. 关闭查询和连接

在使用 JDBC 的时候,需要关注的几个问题

  • 查询分为操作类(增加、删除、修改)和查询类。
  • 要避免重复的创建连接,增加数据库的连接数。
  • 注意异常的处理逻辑,保证没有未关闭的无效连接存在。

示例:

  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. public class Demo {
  7. public static void main(String[] args) {
  8. getDataFromDataBase();
  9. }
  10. public static void getDataFromDataBase() {
  11. Connection conn = null;//Connection是与特定数据库连接回话的接口
  12. PreparedStatement ps = null;
  13. ResultSet rs = null;
  14. try {
  15. //Driver driver = new Driver();通过构建对象的方法去注册驱动,会使驱动注册两次
  16. //DriverManager.registerDriver(driver);
  17. //所以只能通过反射机制去加载该类,使静态代码块中的代码自动执行,可以保证之注册一次
  18. //选择数据库,加载数据库驱动
  19. //1.注册驱动(静态方法)(包名+类名),告知JVM使用的是哪一个数据库的驱动
  20. Class.forName("com.mysql.cj.jdbc.Driver");
  21. //设置连接地址,时间格式和编码格式(因为版本升级,所以需要加上时间格式)
  22. String url = "jdbc:mysql://localhost:3306/j121study?serverTimezone=UTC&characterEncoding=UTF-8";
  23. String user = "root";
  24. String password = "123456";
  25. //2.连接数据库
  26. conn = DriverManager.getConnection(url,user,password);
  27. /**
  28. * DriverManager类用来管理数据库中的所有驱动程序。
  29. * 是JDBC的管理层,作用于用户和驱动程序之间,跟踪可用的驱动程序,并在数据库的驱动程序之间建立连接。
  30. * 此外,DriverManager类中的方法都是静态方法,所以在程序中无须对它进行实例化,直接通过类名就可以调用。
  31. * DriverManager类的常用方法有getConnection(String url,String user,String password)方法
  32. */
  33. //3.获取语句执行平台,并创建数据库查询
  34. //Statement s = conn.createStatement();
  35. //该语句使用字符串拼接参数存在较大的漏洞,极易被SQL注入攻击
  36. //Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。然而查询略显复杂。
  37. //与 Statement一样,PreparedStatement也是用来执行sql语句的,
  38. //与创建Statement不同的是,需要根据sql语句创建PreparedStatement。
  39. //除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。
  40. ps = conn.prepareStatement("select * from school where school_name=?");
  41. //该语句为参数保留一个问号作为占位符
  42. //传递参数,参数什么Xxx类型,就使用setXxx()方法
  43. ps.setString(1, "渡课教育跃龙路主校区");//1表示第1个占位符
  44. //4.执行查询语句,并获取查询结果
  45. rs = ps.executeQuery();
  46. //当要执行查询时,需要调用executeQuery()方法
  47. //如果要执行增删改操作时,调用的是executeUpdate()方法
  48. while(rs.next()) {
  49. System.out.println(rs.getInt("id"));
  50. System.out.println(rs.getString("address"));
  51. }//结果集同样可以释放,以便于下次更方便的填充数据
  52. } catch (ClassNotFoundException e) {
  53. e.printStackTrace();
  54. } catch (SQLException e) {
  55. e.printStackTrace();
  56. } finally {
  57. //关闭查询与连接和结果集
  58. if(null != conn) {
  59. try {
  60. conn.close();
  61. conn = null;
  62. //数据库连接资源非常宝贵,要让连接资源及时回收。
  63. //因为哪怕将连接关闭,数据库连接回收机制也不会主动回收连接资源
  64. //将连接资源设置为null,可以使Java的垃圾回收机制更早回收连接资源。
  65. //这样可以影响数据库回收机制,使连接资源更早回收
  66. } catch (SQLException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. if(null != ps) {
  71. try {
  72. ps.close();
  73. ps = null;
  74. } catch (SQLException e) {
  75. e.printStackTrace();
  76. }
  77. }r
  78. if(null != rs) {
  79. try {
  80. rs.close();
  81. rs = null;
  82. } catch (SQLException e) {
  83. e.printStackTrace();
  84. }
  85. }
  86. }
  87. }
  88. }

PrepareStatement中方法

  1. ResultSet executeQuery(String sql) 执行sqlselect语句
  2. int executeUpdate(String sql) 执行sql中的updateinsertdelete语句,返回影响条数

把资源关闭放到finally语句中,最后要把每个关闭了的资源对象设置一个null值。因为这些资源是很宝贵的就算被关闭了也不会马上被回收,设置为null可以让垃圾回收机制更早的回收

JDBC工具类的抽取

在使用JDBC时,我们发现会有很多代码是重复的,那么其实我们可以将这些代码抽取到工具类中。而且也可以和属性文件搭配使用!避免修改源代码。
jdbc.properties:

  1. driverClass=com.mysql.jdbc.Driver
  2. url=jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8
  3. username=root
  4. password=abc

JDBC工具类:

  1. public class JDBCUtils {
  2. private static final String driverClass;
  3. private static final String url;
  4. private static final String username;
  5. private static final String password;
  6. static{
  7. // 加载属性文件解析对象:
  8. Properties props = new Properties();
  9. // 如何获得属性文件的输入流?
  10. // 通常情况下使用类的加载器的方式进行获取:类加载器加载JDBCUtils类,并去使用文件资源作为输入流
  11. //在JVM运行JDBCUtils时,通过class属性,得到该类的字节码,并通过字节码得到类加载器,
  12. //指明类加载器在加载JDBCUtils类时,将某一个文件作为输入流并到该类的字节码中
  13. InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("com/dodoke/jdbc_test/jdbc.properties");
  14. try {
  15. props.load(is);
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. driverClass = props.getProperty("driverClass");
  20. url = props.getProperty("url");
  21. username = props.getProperty("username");
  22. password = props.getProperty("password");
  23. }
  24. /**
  25. * 注册驱动的方法
  26. * @throws ClassNotFoundException
  27. */
  28. public static void loadDriver() throws ClassNotFoundException{
  29. Class.forName(driverClass);
  30. }
  31. /**
  32. * 获得连接的方法:
  33. * @throws SQLException
  34. */
  35. public static Connection getConnection() throws Exception{
  36. loadDriver();
  37. Connection conn = DriverManager.getConnection(url, username, password);
  38. return conn;
  39. }
  40. /**
  41. * 资源释放
  42. */
  43. public static void release(Statement stmt,Connection conn){
  44. if(stmt != null){
  45. try {
  46. stmt.close();
  47. } catch (SQLException e) {
  48. e.printStackTrace();
  49. }
  50. stmt = null;
  51. }
  52. if(conn != null){
  53. try {
  54. conn.close();
  55. } catch (SQLException e) {
  56. e.printStackTrace();
  57. }
  58. conn = null;
  59. }
  60. }
  61. public static void release(ResultSet rs,Statement stmt,Connection conn){
  62. if(rs!= null){
  63. try {
  64. rs.close();
  65. } catch (SQLException e) {
  66. e.printStackTrace();
  67. }
  68. rs = null;
  69. }
  70. if(stmt != null){
  71. try {
  72. stmt.close();
  73. } catch (SQLException e) {
  74. e.printStackTrace();
  75. }
  76. stmt = null;
  77. }
  78. if(conn != null){
  79. try {
  80. conn.close();
  81. } catch (SQLException e) {
  82. e.printStackTrace();
  83. }
  84. conn = null;
  85. }
  86. }
  87. }

SQL注入的漏洞

注入漏洞产生的原因:
在输入的时候输入了sql语句的关键字

  1. public class JDBCDemo4 {
  2. /**
  3. * 测试SQL注入漏洞的方法
  4. */
  5. public void demo1(){
  6. boolean flag = JDBCDemo4.login2("aaa' or '1=1", "1fsdsdfsdf");
  7. if(flag == true){
  8. System.out.println("登录成功!");
  9. }else{
  10. System.out.println("登录失败!");
  11. }
  12. }
  13. /**
  14. * 避免SQL注入漏洞的方法
  15. */
  16. public static boolean login2(String username,String password){
  17. Connection conn = null;
  18. PreparedStatement pstmt = null;
  19. ResultSet rs = null;
  20. boolean flag = false;
  21. try{
  22. // 获得连接:
  23. conn = JDBCUtils.getConnection();
  24. // 编写SQL:
  25. String sql = "select * from user where username = ? and password = ?";
  26. // 预处理SQL:
  27. pstmt = conn.prepareStatement(sql);
  28. // 设置参数:
  29. pstmt.setString(1, username);
  30. pstmt.setString(2, password);
  31. // 执行SQL:
  32. rs = pstmt.executeQuery();
  33. // 判断结果街
  34. if(rs.next()){
  35. flag = true;
  36. }else{
  37. flag = false;
  38. }
  39. }catch(Exception e){
  40. e.printStackTrace();
  41. }finally{
  42. JDBCUtils.release(rs, pstmt, conn);
  43. }
  44. return flag;
  45. }
  46. /**
  47. * 产生SQL注入漏洞的方法
  48. * @param username
  49. * @param password
  50. * @return
  51. */
  52. public static boolean login(String username,String password){
  53. Connection conn = null;
  54. Statement stmt = null;
  55. ResultSet rs = null;
  56. boolean flag = false;
  57. try{
  58. conn = JDBCUtils.getConnection();
  59. // 创建执行SQL语句的对象:
  60. stmt = conn.createStatement();
  61. // 编写SQL:
  62. String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
  63. // 执行SQL:
  64. rs = stmt.executeQuery(sql);
  65. // 判断结果集中是否有数据。
  66. if(rs.next()){
  67. flag = true;
  68. }else{
  69. flag = false;
  70. }
  71. }catch(Exception e){
  72. e.printStackTrace();
  73. }finally{
  74. JDBCUtils.release(rs, stmt, conn);
  75. }
  76. return flag;
  77. }
  78. }

数据库连接池

连接池:
连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
在实际应用开发中,特别是在WEB应用系统中,如果JSP、Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程,但是,深入了解这项技术是非常必要的。
数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已经建立的数据库连接,可以克服上述缺点,极大地节省系统资源和时间。
5.JDBC入门 - 图3
5.JDBC入门 - 图4
用数据库连接池时,不要再临时创建到数据库的连接,而是直接去使用某个容器或者地址中创建好的connection

常用的有 : DPCP、C3P0

C3P0 jar包下载地址:https://sourceforge.net/projects/c3p0/?source=navbar
示例:
c3p0配置文件-放在src目录下面

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <c3p0-config>
  3. <default-config>
  4. <property name="driverClass">com.mysql.jdbc.Driver</property>
  5. <property name="jdbcUrl">jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&amp;characterEncoding=UTF-8</property>
  6. <property name="user">root</property>
  7. <property name="password">123456</property>
  8. <property name="initialPoolSize">5</property>
  9. <property name="maxPoolSize">20</property>
  10. </default-config>
  11. </c3p0-config>
  1. public class JDBCUtils2 {
  2. private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();
  3. /**
  4. * 获得连接的方法:
  5. * @throws SQLException
  6. */
  7. public static Connection getConnection() throws Exception{
  8. Connection conn = dataSource.getConnection();
  9. return conn;
  10. }
  11. /**
  12. * 资源释放
  13. */
  14. public static void release(Statement stmt,Connection conn){
  15. if(stmt != null){
  16. try {
  17. stmt.close();
  18. } catch (SQLException e) {
  19. e.printStackTrace();
  20. }
  21. stmt = null;
  22. }
  23. if(conn != null){
  24. try {
  25. conn.close();
  26. } catch (SQLException e) {
  27. e.printStackTrace();
  28. }
  29. conn = null;
  30. }
  31. }
  32. public static void release(ResultSet rs,Statement stmt,Connection conn){
  33. if(rs!= null){
  34. try {
  35. rs.close();
  36. } catch (SQLException e) {
  37. e.printStackTrace();
  38. }
  39. rs = null;
  40. }
  41. if(stmt != null){
  42. try {
  43. stmt.close();
  44. } catch (SQLException e) {
  45. e.printStackTrace();
  46. }
  47. stmt = null;
  48. }
  49. if(conn != null){
  50. try {
  51. conn.close();
  52. } catch (SQLException e) {
  53. e.printStackTrace();
  54. }
  55. conn = null;
  56. }
  57. }
  58. }
  1. public class DataSourceDemo1 {
  2. /**
  3. * 使用配置文件的方式
  4. */
  5. public void demo2(){
  6. Connection conn = null;
  7. PreparedStatement pstmt = null;
  8. ResultSet rs = null;
  9. try{
  10. /*// 获得连接:
  11. ComboPooledDataSource dataSource = new ComboPooledDataSource();*/
  12. // 获得连接:
  13. // conn = dataSource.getConnection();
  14. conn = JDBCUtils2.getConnection();
  15. // 编写Sql:
  16. String sql = "select * from user";
  17. // 预编译SQL:
  18. pstmt = conn.prepareStatement(sql);
  19. // 设置参数
  20. // 执行SQL:
  21. rs = pstmt.executeQuery();
  22. while(rs.next()){
  23. System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name"));
  24. }
  25. }catch(Exception e){
  26. e.printStackTrace();
  27. }finally{
  28. JDBCUtils2.release(rs, pstmt, conn);
  29. }
  30. }
  31. @Test
  32. /**
  33. * 手动设置连接池
  34. */
  35. public void demo1(){
  36. // 获得连接:
  37. Connection conn = null;
  38. PreparedStatement pstmt = null;
  39. ResultSet rs = null;
  40. try{
  41. // 创建连接池:
  42. ComboPooledDataSource dataSource = new ComboPooledDataSource();
  43. // 设置连接池的参数:
  44. dataSource.setDriverClass("com.mysql.jdbc.Driver");
  45. dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8");
  46. dataSource.setUser("root");
  47. dataSource.setPassword("abc");
  48. dataSource.setMaxPoolSize(20);//设置最大连接数
  49. dataSource.setInitialPoolSize(3);//设置初始化时连接池中有多少连接,数字越大,启动越慢
  50. // 获得连接:从连接池获得连接
  51. conn = dataSource.getConnection();//返回数据库连接池中空闲的连接
  52. // 编写Sql:
  53. String sql = "select * from user";
  54. // 预编译SQL:
  55. pstmt = conn.prepareStatement(sql);
  56. // 设置参数
  57. // 执行SQL:
  58. rs = pstmt.executeQuery();
  59. while(rs.next()){
  60. System.out.println(
  61. rs.getInt("uid")+" "+
  62. rs.getString("username")+" "+
  63. rs.getString("password")+" "+
  64. rs.getString("name"));
  65. }
  66. }catch(Exception e){
  67. e.printStackTrace();
  68. }finally{
  69. JDBCUtils.release(rs, pstmt, conn);
  70. }
  71. }
  72. }