JDBC及其数据库连接池知识点

JDBC

  • 概念:Java DataBase Connectivity ( Java 数据库连接 ) --> Java语言操作数据库
  • 本质:是官方(SUN公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动 jar 包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动 jar 包中的实现类

快速入门

  • 步骤
    1. 导入驱动 jar 包
      1. 复制 mysql-connector-java.jar 到项目的 libs 目录下
      2. 右键 libs --> add as Library
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义 sql
    5. 获取执行 sql 语句的对象 Statement
    6. 执行 sql,接收返回结果
    7. 处理结果
    8. 释放资源
//1.导入jar包
Statement stmt = null;
Connection conn = null;
try {
    //2.注册驱动
    Class.forName("com.mysql.jdbc.Driver");

    //3.获取数据库对象
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");

    //4.定义 sql 语句
    String sql = "update student set name = "阿旺" where id = 1";

    //5.获取执行 sql 的对象 Statement
    Statement stmt = conn.createStatement();

    //6.执行 sql
    int count = stmt.executeUpdate(sql);//影响的行数

    //7.处理结果
    System.out.println(count);//输出影响成功的行数
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (SQLException e){
    e.printStackTrace();
} finall {
    //8.释放资源
    //避免空指针异常
    if (stmt != null){
        try {
            stmt.close();
        } catch (SQLException e){
            e.printStackTrace();
        }
    }
    if (conn != null){
        try {
            conn.close();
        } catch (SQLException e){
            e.printStackTrace();
        }
    }
}

各个对象详解

DriverManager(驱动管理对象)

  • 注册驱动:告诉程序该使用哪一个数据库驱动 jar

    • 注册与给定的驱动程序 DriverManager:static void registerDriver(Driver driver)
    • 使用方法:Class.forName("com.mysql.jdbc.Driver");
    • 注意:mysql 5 之后的 jar 包可以省略注册驱动的步骤
    • com.mysql.jdbc.Driver 类中存在静态代码块
    static {
        try {
            java.sql.DriverManager.registerDrvier(new Driver());
        } catch (SQLException e){
            throw new RuntimeException("Can't register driver!");
        }
    }
    
  • 获取数据库连接

    • 方法:static Connection getConnection(String url,String user,String password);
    • 参数
    • Url:指定连接的路径
      • 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
      • 例子:jdbc:mysql://localhost:3306:db1
      • 细节:如果连接的是本机的 mysql 服务器,并且 mysql 服务端默认端口是 3306,则 url 可以简写为 jdbc:mysql:///数据库名称
    • User:用户名
    • Password:密码
  • 实例:Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");

Connection(数据库连接对象)

  • 获取执行 sql 的对象

    • Statement createStatement()
    • PreparedStatement prepareStatement(String sql)
  • 管理事务

    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为 false,即开启事务
    • 提交事务:commit()
    • 回滚事务:rollback()

Statement(执行 sql 的对象)

  • boolean execute(String sql):可以执行任意的 sql (了解即可)

  • int executeUpdate(String sql):执行 DML(insert、update、delete)语句、DDL(create、alter、drop)语句

    • 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功( 返回值>0 则执行成功)
  • ResultSet executeQuery(String sql):执行 DQL(select)语句

ResultSet(结果集对象,封装查询结果)

  • next():游标向下移动一行(mysql数据库的下一行),判断当前行是否是最后一行末尾(如果是返回false,如果不是返回true)
  • getXxx(参数):获取数据
    • Xxx:代表数据类型
      • 如:int getInt()String getString()
    • 参数
      • Int:代表列的编号,从1开始。如:getString(1);
      • String:代表列的名称。如:getDouble("salary");
//1.导入jar包
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
try {
    //2.注册驱动
    Class.forName("com.mysql.jdbc.Driver");

    //3.获取数据库对象
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");

    //4.定义 sql 语句
    String sql = "select * from student";

    //5.获取执行 sql 的对象 Statement
    Statement stmt = conn.createStatement();

    //6.执行 sql
    rs = stmt.executeQuery(sql);

    //7.处理结果
    rs.next();//默认在0上,让光标向下移动一行(以后用while循环,这里只做演示)
    int id = rs.getInt(1);
    String name = rs.getString("name");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (SQLException e){
    e.printStackTrace();
} finall {
    //8.释放资源
    //避免空指针异常
    if (stmt != null){
        try {
            stmt.close();
        } catch (SQLException e){
            e.printStackTrace();
        }
    }
    if (conn != null){
        try {
            conn.close();
        } catch (SQLException e){
            e.printStackTrace();
        }
    }
}

PrepareStatement(执行 sql 的对象)

  • SQL注入问题:在拼接 sql 时,有一些 sql 的特殊关键字参与字符串的拼接。(会造成安全性问题)

  • 解决 sql 注入问题:使用 PrepareStatement 对象来解决

  • 预编译的 SQL:参数使用 ? 作为占位符

  • 步骤

    1. 导入驱动 jar 包
    2. 注册驱动
    3. 获取数据库连接对象 Connection
    4. 定义 sql
      • 注意:sql的参数使用 ? 作为占位符。如:select * from user where username = ? and password = ?;
    5. 获取执行 sql 语句的对象 PrepareStatement Connection.prepareStatement(String sql)
    6. ? 赋值
      • 方法:setXxx(参数1,参数2)
      • 参数1:? 的位置,编号从1开始
      • 参数2:? 的值
    7. 执行 sql,接收返回结果
    8. 处理结果
    9. 释放资源
  • 注意:后期都会使用 PrepareStatement 来完成增删改查的所有操作

    • 可以防止 SQL 注入
    • 效率更高

抽取JDBC工具类:JDBCUtils

  • 先配置 jdbc.properties 文件
url = jdbc:mysql://localhost:3306:db1
user = root
password = password
driver = com.mysql.jdbc.Driver
//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();
            //2.加载文件
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            pro.load(is);
            //3.获取数据,赋值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
            //4.注册驱动
            Class.forName(driver);
        } catch () {
            e.printStackTrace();
        }
    }
    //获取连接,连接对象
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(url,user,password);
    }
    
    //释放资源
    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();
            }
        }
    }
    
    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();
            }
        }
    }
}

JDBC控制事务

  • 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
  • 使用 Connection 对象来管理事务
    • 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为 false,即开启事务
      • 在执行 sql 之前开启事务
    • 提交事务:commit()
      • 当所有 sql 都执行完提交事务
    • 回滚事务:rollback()
      • 在 catch 中回滚事务
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
    //1.获取连接
    conn = JDBCUtils.getConnection();
    //开启事务
    conn.setAutoCommit(false);
    //2.定义sql
    //2.1 张三 - 500
    String sql1 = "update account set balance = balance - ? where id = ?";
    //2.2 李四 + 500
    String sql2 = "update account set balance = balance + ? where id = ?";
    //3.获取执行sql对象
    pStmt1 = conn.prepareStatement(sql1);
    pStmt2 = conn.prepareStatement(sql2);
    //4. 设置参数
    pStmt1.setDouble(1,500);
    pStmt1.setInt(2,1);
    
    pStmt2.setDouble(1,500);
    pStmt2.setInt(2,2);
    //5.执行sql
    pStmt1.executeUpdate();
    int i = 3/0;// 手动制造异常
    pStmt2.executeUpdate();
    //提交事务
    conn.commit();
} catch (Exception e) {
    //事务回滚
    try {
        if(conn != null) {
            conn.rollback();
        }
    } catch (SQLException e1) {
        e1.printStackTrace();
    }
    e.printStackTrace();
}finally {
    JDBCUtils.close(pstmt1,conn);
    JDBCUtils.close(pstmt2,null);
}

数据库连接池

  • 概念:其实就是一个容器(集合),存放数据库连接的容器
    • 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器
  • 好处
    • 节约资源
    • 用户访问高效
  • 实现
    • 标准接口:DataSource javax.sql包下的
      • 获取连接方法:getConnection()
      • 归还连接:如果连接对象 Connection 是从连接池中获取的,那么调用 Connection.close() 方法,则不会再关闭连接了,而是归还连接
    • 一般我们不去实现它,由数据库厂商实现
      • C3P0:数据库连接池技术
      • Druid:数据库连接池实现技术,由阿里巴巴提供

C3P0:数据库连接池技术

  • 步骤
    1. 导入 jar 包(两个):c3p0.jarmachange-commons-java.jar(不要忘记导入数据库驱动 jar 包)
    2. 定义配置文件:
      • 名称:c3p0.properties 或者 c3p0-config.xml
      • 路径:直接将文件放在 src 目录下即可
    3. 创建核心对象:数据库连接池对象 CombpPooledDataSource
    4. 获取连接:getConnection()
<!-- c3p0-config.xml配置 -->
<c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!-- 连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/db1</property>
        <property name="user">root</property>
        <property name="password">password</property>

        <!-- 连接池参数 -->
        <!-- 初始化申请的连接数量 -->
        <property name="initialPoolSize">5</property>
        <!-- 最大的连接数量 -->
        <property name="maxPoolSize">10</property>
        <!-- 超时时间 -->
        <property name="checkoutTimeout">3000</property>
    </default-config>

    <named-config name="otherc3p0"> 
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">8</property>
        <property name="checkoutTimeout">1000</property>
    </named-config>
</c3p0-config>
//1.创建数据库连接池对象(使用默认配置)
DataSource ds = new ComboPooledDataSource();
//1.1创建数据库连接池对象(使用指定配置)
DataSource ds1 = new ComboPooledDataSource("otherc3p0");
//2.获取连接对象
Connection conn = ds.getConnection();
//3.归还连接到连接池,下次取的时候是同一地址值的连接对象
conn.close();

Druid:数据库连接池实现技术

  • 步骤
    1. 导入 jar 包:druid.jar
    2. 定义配置文件
      • 是 properties 形式的
      • 可以叫任意名称,可以放在任意目录下
    3. 获取数据库连接池对象:通过工厂来获取 DruidDataSourceFactory
#druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1
username=root
password=password
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
filters=stat

timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
  • 定义工具类
    • 定义一个类 JDBCUtils
    • 提供静态代码块加载配置文件,初始化连接池对象
    • 提供方法
      • 获取连接方法:通过数据库连接池获取连接
      • 释放资源
      • 获取连接池的方法
public class JDBCUtils {
    //1.定义成员变量 DataSource
    private static DataSource ds;
    static {
        try{
            //1.加载配置文件
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            Properties pro = new Properties();
            pro.load(is);
            //2.获取 DataSource
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e){
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }

    //释放资源
    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();
            }
        }
    }

    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();
            }
        }
    }

    //获取连接池的方法
    public static DataSource getDataSource(){
        return ds;
    }
}

Spring JDBC

  • Spring 框架对 JDBC 的简单封装。提供了一个 JDBCTemplate 对象简化 JDBC 的开发
  • 步骤
    1. 导入 jar 包
    2. 创建 JdbcTemplate 对象。依赖于数据源 DataSource
      • JdbcTemplate template = new JdbcTemplate(ds);
    3. 调用 JdbcTemplate 的方法来完成 CRUD 操作
      • update():执行DML(增、删、改)语句
      • queryForMap():查询结果将结果集封装为 map 集合(将列名作为key,将值作为value,只能将一条记录封装为一个map集合)
        • 注意:这个方法查询的结果集长度只能是1
      • queryForList():查询结果将结果集封装为 list 集合
        • 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
      • query():查询结果,将结果封装为 JavaBean 对象
        • query的参数:RowMapper
          • 一般我们使用 BeanPropertyRowMapper 实现类。可以完成数据到 JavaBean 的自动封装
          • new BeanPropertyRowMapper<类型>(类型.class)
      • queryForObject():查询结果,将结果封装为对象
        • 一般用于聚合函数的查询
public class JdbcTemplateDemo{
	public static void main(String[] args){
        //1.导入jar包
        //2.创建JdbcTemplate对象
        JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
        //3.调用方法
        String sql = "update student set age = 20 where id =?";
        template.update(sql,3);	//'3'对应的是第一个?
        //用完了会自动归还到数据库连接池中
    }
}

简易代码

public class JdbcTemplateTest{
	private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    @Test
    public void testUpdate(){
        //让id为3的学生年龄为20
        String sql = "update student set age = 20 where id =?";
        template.update(sql,3);
    }
    
    @Test
    public void testAdd(){
        //添加一条记录
        String sql = "insert into student(id,name,age) values(?,?,?)";
        template.update(sql,6,"阿旺",20);
    }
    
    @Test
    public void testDel(){
        //删除id为6的记录
        String sql = "delete from student where id = ?";
        template.update(sql,6);
    }
    
    @Test
    public void testSelOne(){
        //查询id为3的学生
        String sql = "select * from student where id =?";
        Map<String, Object> map = template.queryForMap(sql,3);
        //这个方法查询的结果集长度只能是 1
    }
    
    @Test
    public void testSelAll(){
        //查询所有记录
        String sql = "select * from student";
        List<Map<String, Object>> list = template.queryForList(sql);
        for (Map<String, Object> map : list){
            //...
        }
    }
    
    @Test
    public void testSelAll2(){
        //查询所有记录,将其封装为student对象的list集合
        String sql = "select * from student";
        /*
            List<Student> list = template.query(sql, new RowMapper<Student>(){
                @Override
                public Student mapRow(ResultSet rs, int i) throws SQLException {
                    int id = rs.getInt("id");
                    String name = rs.getName("name");
                    int age = rs.getInt("age");
                    return new Student(id,name,age);
                }
            });
        */
        
        //简化书写
        List<Student> list = template.query(sql, new RowMapper<Student>(Student.class));
    }
    
    @Test
    public void testSelAll(){
        //查询总记录数
        String sql = "select count(id) from student";
        Long total = template.queryForObject(sql, long.class);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值