导学
JDBC的全称为:Java DataBase Connectivity(java数据库连接),这是一门Java连接数据库的技术,可对多种数据库进行统一操作,不需要对单个数据库的驱动进行详细了解。
【仅需要了解 JDBC 的规范即可】 - 具体驱动会具体实现相应功能
开发步骤
- 选择数据库:加载数据库驱动
- 连接数据库
- 创建数据库查询
- 获取查询结果
- 关闭查询和连接
在使用 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;ResultSet rs = null;try {//Driver driver = new Driver();通过构建对象的方法去注册驱动,会使驱动注册两次//DriverManager.registerDriver(driver);//所以只能通过反射机制去加载该类,使静态代码块中的代码自动执行,可以保证之注册一次//选择数据库,加载数据库驱动//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();//该语句使用字符串拼接参数存在较大的漏洞,极易被SQL注入攻击//Statement接口创建之后,可以执行SQL语句,完成对数据库的增删改查。然而查询略显复杂。//与 Statement一样,PreparedStatement也是用来执行sql语句的,//与创建Statement不同的是,需要根据sql语句创建PreparedStatement。//除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。ps = conn.prepareStatement("select * from school where school_name=?");//该语句为参数保留一个问号作为占位符//传递参数,参数什么Xxx类型,就使用setXxx()方法ps.setString(1, "渡课教育跃龙路主校区");//1表示第1个占位符//4.执行查询语句,并获取查询结果rs = ps.executeQuery();//当要执行查询时,需要调用executeQuery()方法//如果要执行增删改操作时,调用的是executeUpdate()方法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;//数据库连接资源非常宝贵,要让连接资源及时回收。//因为哪怕将连接关闭,数据库连接回收机制也不会主动回收连接资源//将连接资源设置为null,可以使Java的垃圾回收机制更早回收连接资源。//这样可以影响数据库回收机制,使连接资源更早回收} catch (SQLException e) {e.printStackTrace();}}if(null != ps) {try {ps.close();ps = null;} catch (SQLException e) {e.printStackTrace();}}rif(null != rs) {try {rs.close();rs = 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.Driverurl=jdbc:mysql://localhost:3306/j123study?serverTimezone=UTC&characterEncoding=UTF-8username=rootpassword=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类,并去使用文件资源作为输入流//在JVM运行JDBCUtils时,通过class属性,得到该类的字节码,并通过字节码得到类加载器,//指明类加载器在加载JDBCUtils类时,将某一个文件作为输入流并到该类的字节码中InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("com/dodoke/jdbc_test/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直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器中,基本都提供了这项技术,无需自己编程,但是,深入了解这项技术是非常必要的。
数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已经建立的数据库连接,可以克服上述缺点,极大地节省系统资源和时间。

用数据库连接池时,不要再临时创建到数据库的连接,而是直接去使用某个容器或者地址中创建好的connection
常用的有 : DPCP、C3P0
C3P0 jar包下载地址: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&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);}}}
