企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
# JDBC入门 [TOC] ## 导学 JDBC的全称为:Java DataBase Connectivity(java数据库连接),这是一门Java连接数据库的技术,可对多种数据库进行统一操作,不需要对单个数据库的驱动进行详细了解。 ![](https://img.kancloud.cn/a8/b3/a8b30e66ee4ad65324c040c09eab31e7_817x354.png) 【仅需要了解 JDBC 的规范即可】 - 具体驱动会具体实现相应功能 ## 开发步骤 1. 选择数据库:加载数据库驱动 2. 连接数据库 3. 创建数据库查询 4. 获取查询结果 5. 关闭查询和连接 在使用 JDBC 的时候,需要关注的几个问题 * 查询分为操作类(增加、删除、修改)和查询类。 * 要避免重复的创建连接,增加数据库的连接数。 * 注意异常的处理逻辑,保证没有未关闭的无效连接存在。 示例: ~~~ import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class Demo { public static void main(String[] args) { getDataFromDataBase(); } public static void getDataFromDataBase() { Connection conn = null;//Connection是与特定数据库连接回话的接口 PreparedStatement ps = null; try { //1.注册驱动(静态方法)(包名+类名),告知JVM使用的是哪一个数据库的驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //设置连接地址,时间格式和编码格式(因为版本升级,所以需要加上时间格式) String url = "jdbc:mysql://localhost:3306/j121study?serverTimezone=UTC&characterEncoding=UTF-8"; String user = "root"; String password = "123456"; //2.连接数据库 conn=DriverManager.getConnection(url,user,password); /** * DriverManager类用来管理数据库中的所有驱动程序。 * 是JDBC的管理层,作用于用户和驱动程序之间,跟踪可用的驱动程序,并在数据库的驱动程序之间建立连接。 * 此外,DriverManager类中的方法都是静态方法,所以在程序中无须对它进行实例化,直接通过类名就可以调用。 * DriverManager类的常用方法有getConnection(String url,String user,String password)方法 */ //3.获取语句执行平台,并创建数据库查询 //Statement s = conn.createStatement();Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。然而查询略显复杂。 //与 Statement一样,PreparedStatement也是用来执行sql语句的,与创建Statement不同的是,需要根据sql语句创建PreparedStatement。 //除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。 ps = conn.prepareStatement("select * from school where school_name=?"); //该语句为参数保留一个问号作为占位符 ps.setString(1, "渡课教育跃龙路主校区"); //4.执行查询语句,并获取查询结果 ResultSet rs = ps.executeQuery(); while(rs.next()) { System.out.println(rs.getInt("id")); System.out.println(rs.getString("address")); }//结果集同样可以释放,以便于下次更方便的填充数据 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } finally { if(null != conn) { try { conn.close();conn = null; } catch (SQLException e) { e.printStackTrace(); } } if(null != ps) { try { ps.close(); ps = null; } catch (SQLException e) { e.printStackTrace(); } } } } } ~~~ PrepareStatement中方法 ~~~ ResultSet executeQuery(String sql) 执行sql中select语句 int executeUpdate(String sql) 执行sql中的update,insert,delete语句,返回影响条数 ~~~ 把资源关闭放到finally语句中,最后要把每个关闭了的资源对象设置一个null值。因为这些资源是很宝贵的就算被关闭了也不会马上被回收,设置为null可以让垃圾回收机制更早的回收 ## JDBC工具类的抽取 在使用JDBC时,我们发现会有很多代码是重复的,那么其实我们可以将这些代码抽取到工具类中。而且也可以和属性文件搭配使用!避免修改源代码。 jdbc.properties: ~~~ driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8 username=root password=abc ~~~ JDBC工具类: ~~~ public class JDBCUtils { private static final String driverClass; private static final String url; private static final String username; private static final String password; static{ // 加载属性文件解析对象: Properties props = new Properties(); // 如何获得属性文件的输入流? // 通常情况下使用类的加载器的方式进行获取:类加载器加载JDBCUtils类,并去使用文件资源作为输入流 InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); try { props.load(is); } catch (IOException e) { e.printStackTrace(); } driverClass = props.getProperty("driverClass"); url = props.getProperty("url"); username = props.getProperty("username"); password = props.getProperty("password"); } /** * 注册驱动的方法 * @throws ClassNotFoundException */ public static void loadDriver() throws ClassNotFoundException{ Class.forName(driverClass); } /** * 获得连接的方法: * @throws SQLException */ public static Connection getConnection() throws Exception{ loadDriver(); Connection conn = DriverManager.getConnection(url, username, password); return conn; } /** * 资源释放 */ public static void release(Statement stmt,Connection conn){ if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } public static void release(ResultSet rs,Statement stmt,Connection conn){ if(rs!= null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } ~~~ ## SQL注入的漏洞 注入漏洞产生的原因: **在输入的时候输入了sql语句的关键字** ~~~ public class JDBCDemo4 { /** * 测试SQL注入漏洞的方法 */ public void demo1(){ boolean flag = JDBCDemo4.login2("aaa' or '1=1", "1fsdsdfsdf"); if(flag == true){ System.out.println("登录成功!"); }else{ System.out.println("登录失败!"); } } /** * 避免SQL注入漏洞的方法 */ public static boolean login2(String username,String password){ Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; boolean flag = false; try{ // 获得连接: conn = JDBCUtils.getConnection(); // 编写SQL: String sql = "select * from user where username = ? and password = ?"; // 预处理SQL: pstmt = conn.prepareStatement(sql); // 设置参数: pstmt.setString(1, username); pstmt.setString(2, password); // 执行SQL: rs = pstmt.executeQuery(); // 判断结果街 if(rs.next()){ flag = true; }else{ flag = false; } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, pstmt, conn); } return flag; } /** * 产生SQL注入漏洞的方法 * @param username * @param password * @return */ public static boolean login(String username,String password){ Connection conn = null; Statement stmt = null; ResultSet rs = null; boolean flag = false; try{ conn = JDBCUtils.getConnection(); // 创建执行SQL语句的对象: stmt = conn.createStatement(); // 编写SQL: String sql = "select * from user where username = '"+username+"' and password = '"+password+"'"; // 执行SQL: rs = stmt.executeQuery(sql); // 判断结果集中是否有数据。 if(rs.next()){ flag = true; }else{ flag = false; } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, stmt, conn); } return flag; } } ~~~ ## 数据库连接池 连接池: 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。 在实际应用开发中,特别是在WEB应用系统中,如果JSP、Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程,但是,深入了解这项技术是非常必要的。 数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已经建立的数据库连接,可以克服上述缺点,极大地节省系统资源和时间。 ![](https://img.kancloud.cn/fe/b5/feb55c51ba0be3bbd68f7a359d23a4ec_924x463.png) ![](https://img.kancloud.cn/ad/11/ad113c3fa0543d5ef28648bc1fd6722b_934x458.png) 用数据库连接池时,不要再临时创建到数据库的连接,而是直接去使用某个容器或者地址中创建好的connection >[info]常用的有 : DPCP、C3P0 C3P0 jar包下载地址:[https://sourceforge.net/projects/c3p0/?source=navbar](https://sourceforge.net/projects/c3p0/?source=navbar) 示例: c3p0配置文件-放在src目录下面 ~~~ <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&amp;characterEncoding=UTF-8</property> <property name="user">root</property> <property name="password">123456</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">20</property> </default-config> </c3p0-config> ~~~ ~~~ public class JDBCUtils2 { private static final ComboPooledDataSource dataSource = new ComboPooledDataSource(); /** * 获得连接的方法: * @throws SQLException */ public static Connection getConnection() throws Exception{ Connection conn = dataSource.getConnection(); return conn; } /** * 资源释放 */ public static void release(Statement stmt,Connection conn){ if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } public static void release(ResultSet rs,Statement stmt,Connection conn){ if(rs!= null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } } ~~~ ~~~ public class DataSourceDemo1 { /** * 使用配置文件的方式 */ public void demo2(){ Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try{ /*// 获得连接: ComboPooledDataSource dataSource = new ComboPooledDataSource();*/ // 获得连接: // conn = dataSource.getConnection(); conn = JDBCUtils2.getConnection(); // 编写Sql: String sql = "select * from user"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数 // 执行SQL: rs = pstmt.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils2.release(rs, pstmt, conn); } } @Test /** * 手动设置连接池 */ public void demo1(){ // 获得连接: Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try{ // 创建连接池: ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 设置连接池的参数: dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8"); dataSource.setUser("root"); dataSource.setPassword("abc"); dataSource.setMaxPoolSize(20);//设置最大连接数 dataSource.setInitialPoolSize(3);//设置初始化时连接池中有多少连接 // 获得连接:从连接池获得连接 conn = dataSource.getConnection(); // 编写Sql: String sql = "select * from user"; // 预编译SQL: pstmt = conn.prepareStatement(sql); // 设置参数 // 执行SQL: rs = pstmt.executeQuery(); while(rs.next()){ System.out.println(rs.getInt("uid")+" "+rs.getString("username")+" "+rs.getString("password")+" "+rs.getString("name")); } }catch(Exception e){ e.printStackTrace(); }finally{ JDBCUtils.release(rs, pstmt, conn); } } } ~~~