JDBC详解


JDBC

1.数据库驱动

程序不直接与数据库进行交互,而是通过数据库驱动和数据库打交道。

2.JDBC

没有什么事是加一层解决不了的。

SUN公司为了简化开发人员的(对数据库统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC,这些规范的实现由具体的厂商去做。

对于开发人员来说,只需要掌握JDBC的接口即可。

 


3.第一个JDBC程序

  1. 首先创建一个普通的java程序。

  2. 创建lib目录,导入数据库驱动。

    • 右键 add

  1. 编写测试代码

package com.li.demo01;
 ?
 import java.sql.*;
 ?
 // 我的第一个jdbc程序
 public class JdbcFirstDemo {
     public static void main(String[] args) throws ClassNotFoundException, SQLException {
         // 1.加载驱动
         Class.forName("com.mysql.jdbc.Driver");     //固定写法,加载驱动
         // 2.用户信息和url
         String url = "jdbc:mysql://localhost:3306/ks?useUnicode=true&characterEncoding=utf8&useSSL=true";
         String username = "root";
         String password = "123456";
         // 3.连接成功,获得数据库连接对象 Connection 代表数据库
         Connection connection = DriverManager.getConnection(url, username, password);
         // 4.执行sql语句的对象 Statement 执行sql的对象
         Statement statement = connection.createStatement();
         // 5.执行sql语句的对象去执行sql,获得结果集
         String sql = "SELECT * FROM info";
         ResultSet resultSet = statement.executeQuery(sql);
         // 6.得到返回的结果集,取出展示
         while (resultSet.next()){
             System.out.println("info_id=" + resultSet.getObject("info_id"));
             System.out.println("info_title=" + resultSet.getObject("info_title"));
             System.out.println("info_content=" + resultSet.getObject("info_content"));
             System.out.println("root_id=" + resultSet.getObject("root_id"));
             System.out.println("info_date=" + resultSet.getObject("info_date"));
             System.out.println("===========================================");
         }
         // 7.释放连接
         resultSet.close();
         statement.close();;
         connection.close();
     }
 }

步骤总结:

  1. 加载驱动。

  2. 填写数据库连接需要的相应信息。

  3. 连接数据库,获得数据库连接对象。

  4. 获取执行sql语句的对象。

  5. 执行sql得到结果集。

  6. 遍历结果集,打印出来。

  7. 释放连接。

 


4.JDBC中对象的解释

数据库驱动:

 Class.forName("com.mysql.jdbc.Driver");     --这是固定加载mysql驱动的写法
 执行Driver里面的静态代码块,new一个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!");
         }
     }
 }
 ?
 用DriverManager来连接数据库,得到数据库连接对象。
 Connection connection = DriverManager.getConnection(url, username, password);
 connection 代表数据库,即可以操作数据库事务
 connection.commit();
 connection.setAutocommit();
 connection.rollback();

url:

 Srting url = "jdbc:mysql://localhost:3306/ks?useUnicode=true&charecterEncoding=utf8&useSSL=true";
 ?
 -- jdbc:mysql   这是协议    就像  http:www:
 -- localhost    这是地址
 -- ks           数据库名称
 -- useUnicode=true&charecterEncoding=utf8&useSSL=true   参数  使用中文、字符编码、安全协议
 ?
 即:
 String url = " 协议 : 主机地址:端口号/数据库名称?相关参数"

Statement 执行sql的对象:

用于向数据库发送sql语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。

 statement.executeQuery();   --查询操作,返回resultSet
 statement.execute();        --执行任何的sql
 statement.executeUpdate();  --更新、插入、删除、都是用这个,这个不返回resultSet,返回一个受影响的行数

resultSet查询的结果集:封装了所有的查询结果

 resultSet.getObject();      --在不知道列类型的情况下使用
 ?
 如果知道列类型的就使用固定的类型
 resultSet.getString();  
 resultSet.getInt(); 
 resultSet.getFloat();   
 resultSet.getDate();    
 resultSet.get...(); 
 ?
 遍历,指针
 resultSet.next();   --移动到下一个数据
 resultSet.beforeFirst();    --移动到最前面
 resultSet.afterLast();      --移动到最后面

 


5.提取工具类

db.properties:

 driver=com.mysql.jdbc.Driver
 url=jdbc:mysql://localhost:3306/ks?useUnicode=true&characterEncoding=utf8&useSSL=true
 username=root
 password=123456

JdbcUtils:

 package com.li.demo02.utils;
 ?
 import java.io.IOException;
 import java.io.InputStream;
 import java.sql.*;
 import java.util.Properties;
 ?
 public class JdbcUtils {
     private static String driver = null;
     private static String url = null;
     private static String username = null;
     private static String password = null;
 ?
     static {
         try{
             InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
             Properties properties = new Properties();
             properties.load(in);
             driver = properties.getProperty("driver");
             url = properties.getProperty("url");
             username = properties.getProperty("username");
             password = properties.getProperty("password");
 ?
             //驱动只加载一次
             Class.forName(driver);
             } catch (Exception e) {
                 e.printStackTrace();
         }
     }
 ?
     //获取连接
     public static Connection getConnection() throws SQLException {
         return DriverManager.getConnection(url,username,password);
     }
 ?
     //释放资源
     public static void release(Connection conn, Statement st, ResultSet rs){
         if(rs!=null){
             try {
                 rs.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(st!=null){
             try {
                 st.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(conn!=null){
             try {
                 conn.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
     }
 }

TestUtils:

 -- 插入数据
 package com.li.demo02;
 ?
 import com.li.demo02.utils.JdbcUtils;
 ?
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 ?
 public class TestUtils {
     public static void main(String[] args) {
         Connection conn = null;
         Statement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils.getConnection();
             st = conn.createStatement();
             String sql ="INSERT INTO info(`info_id`,`info_title`,`info_content`,`root_id`,`info_date`) VALUES (7,'某某公司招聘公告','这是某某公司的招聘公告',1,'2020-12-17 14:57:52')";
             int i = st.executeUpdate(sql);
             if(i > 0){
                 System.out.println("数据插入成功");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }
 ?
 -- 修改数据
 package com.li.demo02;
 ?
 import com.li.demo02.utils.JdbcUtils;
 ?
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 ?
 public class TestUtilsUpdate {
     public static void main(String[] args) {
         Connection conn = null;
         Statement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils.getConnection();
             st = conn.createStatement();
             String sql = "UPDATE info SET info_content = '这是某某公司修改过的招聘公告-----' WHERE info_id = 7";
             int i = st.executeUpdate(sql);
             if(i > 0){
                 System.out.println("信息修改成功");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }
 ?
 -- 查询数据
 package com.li.demo02;
 ?
 import com.li.demo02.utils.JdbcUtils;
 ?
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 ?
 public class TestUtilsSelect {
     public static void main(String[] args) {
         Connection conn = null;
         Statement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils.getConnection();
             st = conn.createStatement();
             String sql = "select * from info";
             rs = st.executeQuery(sql);
             while(rs.next()){
                 System.out.println("info_id=" + rs.getObject("info_id"));
                 System.out.println("info_title=" + rs.getObject("info_title"));
                 System.out.println("info_content=" + rs.getObject("info_content"));
                 System.out.println("root_id=" + rs.getObject("root_id"));
                 System.out.println("info_date=" + rs.getObject("info_date"));
                 System.out.println("===========================================");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
 ?
     }
 }
 ?
 -- 删除数据
 package com.li.demo02;
 ?
 import com.li.demo02.utils.JdbcUtils;
 ?
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 ?
 public class TestUtilsDelete {
     public static void main(String[] args) {
         Connection conn = null;
         Statement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils.getConnection();
             st = conn.createStatement();
             String sql = "delete from info where info_id = 7";
             int i = st.executeUpdate(sql);
             if(i > 0){
                 System.out.println("信息删除成功!");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }

6.SQL注入问题

sql存在安全漏洞,会被攻击导致数据歇漏。本质:sql会被拼接。

例如在用户登录的时候,传入相应的参数,恶意拼接sql,修改查询匹配的条件。

 login(username:" 'or '1=1",password:" 'or '1=1");

7.PreparedStatement对象

可以防止sql注入。效率更好。

与Statement的区别在于:sql语句里面不是写完整的数据库查询语句,而是在里面使用 ? 占位符,然后先预编译sql,后面再来给他设置值,最后执行sql。

 -- 插入数据
 package com.li.demo03;
 import com.li.demo02.utils.JdbcUtils;
 import java.util.Date;
 import java.sql.*;
 ?
 public class TestInsert {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils.getConnection();
             // 区别
             //使用 ? 占位符代替参数
             String sql = "INSERT INTO info(`info_id`,`info_title`,`info_content`,`root_id`,`info_date`) VALUES (?,?,?,?,?)";
             st = conn.prepareStatement(sql);   //预编译sql,然后不执行
             //预编译之后手动给参数赋值
             st.setInt(1,7);
             st.setString(2,"这是新的工厂");
             st.setString(3,"这是新的工厂招聘信息");
             st.setInt(4,1);
             // 注意点,这两个date是不一样的
             st.setDate(5,new java.sql.Date(new Date().getTime()));
             //最后执行
             int i = st.executeUpdate();
             if(i > 0){
                 System.out.println("信息插入成功!");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }
 ?
 -- 修改数据
 package com.li.demo03;
 import com.li.demo02.utils.JdbcUtils;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 ?
 public class TestUpdate {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
         try {
             conn = JdbcUtils.getConnection();
             String sql ="update info set info_content = ? where info_id = ?";
             st = conn.prepareStatement(sql);
             st.setString(1,"这是修改过后的信息-----------");
             st.setInt(2,7);
             int i = st.executeUpdate();
             if (i > 0){
                 System.out.println("信息修改成功");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }
 ?
 -- 查询数据
 package com.li.demo03;
 import com.li.demo02.utils.JdbcUtils;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 ?
 public class TestSelect {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
         try {
             conn = JdbcUtils.getConnection();
             String sql = "select * from info where info_id = ?";
             st = conn.prepareStatement(sql);
             st.setInt(1,7);
             rs = st.executeQuery();
             while (rs.next()){
                 System.out.println("info_id=" + rs.getObject("info_id"));
                 System.out.println("info_title=" + rs.getObject("info_title"));
                 System.out.println("info_content=" + rs.getObject("info_content"));
                 System.out.println("root_id=" + rs.getObject("root_id"));
                 System.out.println("info_date=" + rs.getObject("info_date"));
                 System.out.println("===========================================");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }
 ?
 -- 删除数据
 package com.li.demo03;
 import com.li.demo02.utils.JdbcUtils;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 ?
 public class TestDelete {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
         try {
             conn = JdbcUtils.getConnection();
             String sql = "delete from info where info_id = ?";
             st = conn.prepareStatement(sql);
             st.setInt(1,7);
             int i = st.executeUpdate();
             if (i > 0){
                 System.out.println("信息删除成功!");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }

 


8.代码实现JDBC事务

  1. 关闭自动提交事务,自动开启事务。

    •  conn.setAutoCommit(false);
  2. 执行完sql之后,注意需要手动提交事务。

    •  conn.commit();
  3. 失败则回滚。

    •  conn.rollback();

具体代码实现:

 
package com.li.demo03;
 import com.li.demo02.utils.JdbcUtils;
 import java.util.Date;
 import java.sql.*;
 ?
 public class TestInsert {
     public static void main(String[] args) throws SQLException {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
         try {
             conn = JdbcUtils.getConnection();
             //开启事务,关闭数据库的自动提交,自动会开启事务
             conn.setAutoCommit(false);         
             String sql1 = "INSERT INTO info(`info_id`,`info_title`,`info_content`,`root_id`,`info_date`) VALUES (?,?,?,?,?)";
             st = conn.prepareStatement(sql1);
             st.setInt(1,7);
             st.setString(2,"这是新的工厂");
             st.setString(3,"这是新的工厂招聘信息");
             st.setInt(4,1);
             st.setDate(5,new java.sql.Date(new Date().getTime()));
             //最后执行
             int i = st.executeUpdate();
             String sql2 = "update info set info_content = ? where info_id = ?";
             st = conn.prepareStatement(sql2);
             st.setString(1,"1111111111111111111");
             st.setInt(2,7);
             st.executeUpdate();
             //业务完毕,提交事务
             conn.commit();
             System.out.println("事务执行完毕!");
         } catch (SQLException e) {
             conn.rollback();    //如果失败就回滚
             e.printStackTrace();
         }finally {
             JdbcUtils.release(conn,st,rs);
         }
     }
 }

9.数据库连接池

数据库连接------执行sql------释放连接。

这个过程十分浪费资源。

池化技术:准备一些预先的资源,进来就直接用预先准备好的。

最小连接数

最大连接数

等待超时

 

开源数据源实现:

DBCP

需要使用的jar包。

commons-dbcp-1.4   commons-pool-1.6

dbcpconfig.properties:

 driverClassName=com.mysql.jdbc.Driver
 url=jdbc:mysql://localhost:3306/ks?useUnicode=true&characterEncoding=utf8&useSSL=true
 username=root
 password=123456

 -- 注意:这里的driverClassName 是固定的写法
 数据源里面封装的方法,固定用的就是这个名字,不然找不到
 还有就是后面不能带有注释,否则会包加载不了驱动的错误

JdbcUtils_DBCP:

 package com.li.demo04.utils;
 import com.li.demo02.utils.JdbcUtils;
 import org.apache.commons.dbcp.BasicDataSourceFactory;
 import javax.sql.DataSource;
 import java.io.InputStream;
 import java.sql.*;
 import java.util.Properties;
 ?
 public class JdbcUtils_DBCP {
 ?
     private static DataSource dataSource = null;
     static {
         try{
             InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
             Properties properties = new Properties();
             properties.load(in);
             //创建数据源
             dataSource = BasicDataSourceFactory.createDataSource(properties);
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 ?
     //获取连接
     public static Connection getConnection() throws SQLException {
         return dataSource.getConnection();  //从数据源中获取相应的信息
     }
 ?
     //释放资源
     public static void release(Connection conn, Statement st, ResultSet rs){
         if(rs!=null){
             try {
                 rs.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(st!=null){
             try {
                 st.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(conn!=null){
             try {
                 conn.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
     }
 }
 ?
 ?
 -- 与前面不一样的就是,使用了封装的数据源datasource.
 使用工厂模式创建 dataSource = BasicDataSourceFactory.createDataSource(properties);
 获取连接也是使用封装好的方法:
 dataSource.getConnection();

TestDBCP:

 package com.li.demo04;
 import com.li.demo04.utils.JdbcUtils_DBCP;
 import java.sql.*;
 public class TestDBCP {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils_DBCP.getConnection();
             String sql = "select * from info where info_id = ?";
             st = conn.prepareStatement(sql);
             st.setInt(1, 7);
             rs = st.executeQuery();
             while (rs.next()) {
                 System.out.println("info_id=" + rs.getObject("info_id"));
                 System.out.println("info_title=" + rs.getObject("info_title"));
                 System.out.println("info_content=" + rs.getObject("info_content"));
                 System.out.println("root_id=" + rs.getObject("root_id"));
                 System.out.println("info_date=" + rs.getObject("info_date"));
                 System.out.println("===========================================");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             JdbcUtils_DBCP.release(conn, st, rs);
         }
     }
 }?

C3P0

需要使用到的jar包:

c3p0-0.9.5.5        mchange-commons-java-0.2.19

c3p0-config.xml

 <?xml version="1.0" encoding="UTF-8"?>
 ?
 
     <default-config>
         "driverClass">com.mysql.jdbc.Driver
         "jdbcUrl">jdbc:mysql://localhost:3306/ks?useUnicode=true&characterEncoding=utf8&useSSL=true
         "user">root
         "password">123456
 ?
         "initialPoolSize">10
         "maxIdleTime">30
         "maxPoolSize">100
         "minPoolSize">10
     default-config>
 ?
     "mySource">
         "driverClass">com.mysql.jdbc.Driver
         "jdbcUrl">jdbc:mysql://localhost:3306/bookstore
         "user">root
         "password">xxxx
 ?
         "initialPoolSize">10
         "maxIdleTime">30
         "maxPoolSize">100
         "minPoolSize">10
     
 

JdbcUtils_C3P0:

 
package com.li.demo04.utils;
 ?
 import com.li.demo02.utils.JdbcUtils;
 import com.mchange.v2.c3p0.ComboPooledDataSource;
 import org.apache.commons.dbcp.BasicDataSourceFactory;
 import javax.sql.DataSource;
 import java.io.InputStream;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Properties;
 ?
 public class JdbcUtils_C3P0 {
     private static DataSource dataSource = null;
     static {
         try{
             //配置文件写法,里面没有参数则是默认的,加了就匹配对应的
             dataSource = new ComboPooledDataSource();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
 ?
     //获取连接
     public static Connection getConnection() throws SQLException {
         return dataSource.getConnection();  //从数据源中获取相应的信息
     }
 ?
     //释放资源
     public static void release(Connection conn, Statement st, ResultSet rs){
         if(rs!=null){
             try {
                 rs.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(st!=null){
             try {
                 st.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
         if(conn!=null){
             try {
                 conn.close();
             } catch (SQLException throwables) {
                 throwables.printStackTrace();
             }
         }
     }
 }
 ?
 -- 一行代码解决所有连接问题
 dataSource = new ComboPooledDataSource();

TestC3P0:

 package com.li.demo04;
 ?
 import com.li.demo04.utils.JdbcUtils_C3P0;
 ?
 ?
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 ?
 public class TestC3P0 {
     public static void main(String[] args) {
         Connection conn = null;
         PreparedStatement st = null;
         ResultSet rs = null;
 ?
         try {
             conn = JdbcUtils_C3P0.getConnection();
             String sql = "select * from info where info_id = ?";
             st = conn.prepareStatement(sql);
             st.setInt(1, 7);
             rs = st.executeQuery();
             while (rs.next()) {
                 System.out.println("info_id=" + rs.getObject("info_id"));
                 System.out.println("info_title=" + rs.getObject("info_title"));
                 System.out.println("info_content=" + rs.getObject("info_content"));
                 System.out.println("root_id=" + rs.getObject("root_id"));
                 System.out.println("info_date=" + rs.getObject("info_date"));
                 System.out.println("===========================================");
             }
         } catch (SQLException e) {
             e.printStackTrace();
         } finally {
             JdbcUtils_C3P0.release(conn, st, rs);
         }
     }
 }

总结:

  • 无论使用什么封装的数据源,本质都是一样的,都是继承了DataSource接口,方法不会变。