Day05—JDBC操作事务与数据库连接池

一:JDBC操作事务

1.1:事务异常回滚

事务控制

 连接.setAutoCommit(false); 开启事务

 连接.rollback();

 连接.commit();

connection.setAutoCommit(false);

connection.setAutoCommit(true);

 @Test
    public void test01() throws SQLException, IOException, ClassNotFoundException {
        Connection connection = null;
        PreparedStatement pst = null;
        try {
            connection = JDBCUtils.getConnection();
            connection.setAutoCommit(false);
            String sql = "update account set balance = balance + ? where id = ?";

            pst = connection.prepareStatement(sql);
            //填充数据
            pst.setObject(1, -1000);
            pst.setObject(2, 1);


            //执行操作
            int i = pst.executeUpdate();
            System.out.println("i = " + i);
//        System.out.println("操作成功");
            int i1 = Integer.valueOf("ABCD");
            pst.setObject(1,1000);
            pst.setObject(2,2);
            int i2 = pst.executeUpdate();
            System.out.println("i2 = " + i2);



        } catch (Exception e) {
//            e.printStackTrace();

            System.out.println("出现异常回滚");
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }  finally {
            try {
                connection.setAutoCommit(true);
                JDBCUtils.closeResource(null,connection,pst);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

}

1.2:数据库存储图片

blob 64kb (blob数据类型可以存64kb) 
longblob 4gb  可以存储 字节数据
但是配置文件中my.ini 默认 max_allowed_packet=4M 单次上传的数据最大是4兆
一般是不使用mysql 直接存储图片(一张表的大小才n个kb,有的照片会很大影响查询速率)
  在实际开发中 中单独的图片服务器  在mysql中只是存储 图片路径 

 注意:在插入图片的时候一定要查看图片大小,否则插不进去

@Test
    public void test01() throws SQLException, IOException, ClassNotFoundException {
        Connection connection = JDBCUtils.getConnection();

        String sql = "insert into account values(?,?,?,?)";
        PreparedStatement pst = connection.prepareStatement(sql);

        FileInputStream fileInputStream = new FileInputStream("G:\\img\\03.jpeg");
       /* byte[] bytes = new byte[304];

        int read = fileInputStream.read(bytes);*/



        pst.setObject(1,5);
        pst.setObject(2,"MM3");
        pst.setObject(3,3000);
        pst.setObject(4,fileInputStream);

        pst.executeUpdate();

        JDBCUtils.closeResource(null,connection,pst);


    }

二:数据库连接池

2.1:概念

连接池是connection对象的缓冲区,它里面会存放一些connection,当我们Java程序需要使用connection的时候,如果连接池中有则直接从连接池获取,不需要去新创建connection了。连接池让Java程序能够复用连接、管理连接

2.2:为什么要使用连接池

  • 1.因为每次创建和销毁连接都会带来较大的系统开销

  • 2.每次创建和销毁连接都要消耗大概0.05~1s的时间。

  • 3.可以防止大量用户并发访问数据库服务器。

2.3:连接池的作用

1:资源重用

       由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)

2:更快的系统响应速度

        数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3:统一的连接管理,避免数据库连接泄漏

        在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

举例:特别是在银行的App中,如果登录账户之后,一段时间没有进行任何操作,当再次回到这个页面的时候会,需要重新登录,这就是连接被收回了,防止数据泄露。

2.4:连接池的原理

1>连接池维护着两个容器空闲池活动池

2>空闲池用于存放未使用的连接,活动池用于存放正在使用的连接,活动池中的连接使用完之后要归还回空闲池

3>当Java程序需要连接时,先判断空闲池中是否有连接,如果空闲池中有连接则取出一个连接放置到活动池供Java程序使用

4>Java程序需要连接时,如果空闲池中没有连接了,则先判断活动池的连接数是否已经达到了最大连接数,如果未达到最大连接数,则会新创建一个连接放置到活动池,供Java程序使用

5>如果空闲池中没有连接了,活动池中的连接也已经达到了最大连接数,则不能新创建连接了,那么此时会判断是否等待超时,如果没有等待超时则需要等待活动池中的连接归还回空闲池

6>如果等待超时了,则可以采取多种处理方式,例如:直接抛出超时异常,或者将活动池中使用最久的连接移除掉归还回空闲池以供Java程序使用

2.5:连接池的实现

DataSource接口:

                JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口(通常被称为数据源),所有的Java数据库连接池都需要实现该接口。该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现

2.6:Druid连接池的使用

(1)加入jar包

(2)代码步骤

第一步:创建druid连接池的配置文件druid.properties文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
initialSize=5
maxActive=10
maxWait=1000

2.6.1:DruidDataSourceFactory作用

DataSource是一个接口  其中只有一个抽象方法  用来返回一个连接

Connection getConnection(String username, String password)
    throws SQLException;

DruidDataSourceFactory用来创建Druid连接池对象

DruidDataSourceFactory中的方法createDataSource,会间接返回一个datasource对象

public static DataSource createDataSource(Properties properties) throws Exception {
        return createDataSource((Map)properties);
    }


public static DataSource createDataSource(Map properties) throws Exception {
        DruidDataSource dataSource = new DruidDataSource();
        config(dataSource, properties);
        return dataSource;
    }
 @Test
    public void test01() throws Exception {
        //1. 创建一个Properties对象,让其去读取
        Properties properties = new Properties();
        properties.load(DataSourceTest.class.getClassLoader().getResourceAsStream("druid.properties"));
        //2. 使用DruidDataSourceFactory创建Druid连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //第三步:使用连接池对象获取连接
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);


    }

2.6.2DruidDataSource

    @Test
    public void test02() throws SQLException {

        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/myexer?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        DruidPooledConnection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
    }

2.6.3DruidDataSource 连接池实现复用效果

 @Test
    public void test02() throws SQLException {

        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/myexer?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setMaxActive(10);
        dataSource.setInitialSize(5);
        dataSource.setMaxWait(1000);
        for (int i = 0; i < 10; i++) {

            DruidPooledConnection connection = dataSource.getConnection();
            System.out.println("connection = " + connection +"  " +i);
            connection.close();
        }
    }

 原因是:连接使用完之后 就立马放回到了连接池中,因此可以进行复用。

2.6.4DruidDataSource 的封装工具类

public class JDBCDruidUtils {

    static DataSource dataSource;

    static {
        Properties properties = new Properties();

        try {
            properties.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));

            dataSource = DruidDataSourceFactory.createDataSource(properties);


        } catch (Exception e) {
            e.printStackTrace();
        }


    }
    //获取一个连接的时候  主要得到connection  此时 获取dataSource本身就是一个连接池  
    public static DataSource getDataSource() {
        return dataSource;
    }

    public static Connection getConnection() throws SQLException {

        Connection connection = dataSource.getConnection();

        return connection;
    }




    public static void closeResources(ResultSet resultSet , Connection connection , Statement statement){

        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }



    }

}

三:Apache的DBUtils

3.1:DBUtils的概述

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装

使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

QueryRunner类封装了SQL的执行,是线程安全的

(1)可以实现增、删、改、查、批处理

(2)考虑了事务处理需要共用Connection

(3)该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。

3.2:主要的类

QueryRunner() ,创建QueryRunner对象,用于执行SQL语句

QueryRunner的update(Connection conn, String sql, Object... params)方法,用于执行增删改的SQL语句

3.3:QueryRunner()的两个构造器 空参和参数是连接池

public QueryRunner() {
    }
public QueryRunner(DataSource ds) {
        super(ds);
    }

 注意:当传入连接池的时候,在进行执行sql的时候就不需要传入连接了,例如

test04。

public class DbUtilTest {
    @Test
    public void test05() throws SQLException {
        //简化
        int update = new QueryRunner(JDBCDruidUtils.getDataSource()).update("insert into users values(null,?,?,?)", "lbb", "123456", "李冰冰");
        System.out.println("update = " + update);
    }
    @Test
    public void test04() throws SQLException {
        //1:创建一个带有连接池的QueryRunner
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2:创建sql
        String sql = "insert into users values(null,?,?,?)";
        //3:执行sql
        int update = queryRunner.update(sql, "wbb", "123456", "王冰冰");
        System.out.println("update = " + update);

    }
    @Test
    public void test03() throws SQLException {

        //1:创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //2:获取连接
        Connection connection = JDBCDruidUtils.getConnection();
        //3:创建sql
        String sql = "update users set name = ? ,nickName = ? where id = ?";
        int update = queryRunner.update(connection, sql, "fbb","范冰冰", 1);
        System.out.println("update = " + update);


    }


    @Test
    public void test02() throws SQLException {
    //1:创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //2:创建连接
        Connection connection = JDBCDruidUtils.getConnection();
        //3:创建sql
        String sql = "delete from users where id = ?";
        int i = queryRunner.update(connection, sql, 2);
        System.out.println("i = " + i);


    }

    @Test
    public void test01() throws SQLException {
        //1:创建QueryRunner
        QueryRunner queryRunner = new QueryRunner();
        //2:创建sql
        String sql = "insert into users values(null,?,?,?)";
        //获取一个连接
        Connection connection = JDBCDruidUtils.getConnection();
        //3:填充数据  并执行sql
        int i = queryRunner.update(connection,sql, "lbb","123456","李冰冰");
        System.out.println("i = " + i);
        //关闭资源
        JDBCDruidUtils.closeResources(null,connection,null);

    }
}

四:DBUtils执行批处理

4.1:API介绍

public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: 支持批处理INSERT, UPDATE, or DELETE语句

public <T> T insertBatch(Connection conn,String sql,ResultSetHandler<T> rsh,Object[][] params)throws SQLException:只支持INSERT语句

4.2:批处理顺序

1. 创建QueryRunner对象

2. 编写SQL语句

3. 设置二维数组参数:第一维表示我们需要处理多少条数据,第二维表示sql语句有多少个参数

4:填充数据

5:执行批处理    方法是        queryRunner.batch(sql,params);

4.2:代码实现

public class DBatchTest {
    @Test
    public void test03() throws SQLException {
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());

        String sql = "insert into users values(null,?,?,?)";

        String[][] params = new String[5000][3];
        for (int i = 0; i < params.length; i++) {
            params[i][0] = "fbb" + i;
            params[i][1] = "123456";
            params[i][2] = "范冰冰" + i;

        }
        queryRunner.batch(sql, params);
    }
    @Test
    public void test02() throws SQLException {
        //1:创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2: 创建sql
        String sql = "insert into users values(null,?,?,?)";
        //3:创建数组
        Object[][] params = new Object[5000][3];
        for (int i = 0; i < params.length; i++) {
           /* params[i][0] = "fbb" + i;
            params[i][1] = "123456";
            params[i][2] = "范冰冰" + i;
*/
        }
        queryRunner.batch(sql,params);


    }
    @Test
    public void test01() throws SQLException {

        //使用DBUtils执行批处理
        //二维数组:数组内的元素还是数组{{1,2},{3,4},{5,6}}
        //批量往user表中添加五千条数据
        //1:创建QueryRunner对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2. 编写SQL语句
        String sql = "insert into users values(null,?,?,?)";
        //3. 设置二维数组参数:第一维表示我们需要处理多少条数据,第二维表示sql语句有多少个参数
        Object[][] params = new Object[5000][3];
        for (int i = 0; i < params.length; i++) {
            //params[i]表示插入的第i条数据
            //params[i][1]表示插入的第i条数据的第一个问号处的参数
            //注意数据库中下标从1开始  这里java从0开始
           /* params[i][0] = "fbb" + i;
            params[i][1] = "123456";
            params[i][2] = "范冰冰" + i;
            */
        }
        //queryRunner执行批处理
        queryRunner.batch(sql,params);


    }

4.3:使用QueryRunner类实现查询

4.3.1:API介绍

1> query(String sql, ResultSetHandler<T> rsh, Object... params) ,执行查询 select

2>ResultSetHandler结果集处理类

4.3.2:Handler类型(处理结果集的工具类)

 4.3.2:查寻的大体步骤

1. 创建QueryRunner,并且传入连接池对象

2:创建sql

3:调用查询额方法(sql,结果集处理器)  根据查询的不同场景选择结果集处理器

  • BeanHandler: 封装单个对象

  • BeanListHandler: 将多个对象封装到 List集合内

  • ScalarHandler  返回单个值   如果 结果集是多个值 可以采用下标或者是列的名称 进行灵活选择。

  • MapListHandler 存储的是 List<Map<String,Object>>

 @Test
    public void test03() throws SQLException {
        //使用DBUtils执行查询的SQL语句,将查询到的一条数据封装到User对象中
        //1. 创建QueryRunner,并且传入连接池对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2:创建SQL语句
        String sql = "select * from users where id = ?";
        //3:执行
        User user = queryRunner.query(sql, new BeanHandler<>(User.class),1);
        System.out.println("user = " + user);


    }




    @Test
    public void test02() throws SQLException {
        //查询多条数据,封装到List<JavaBean>
        //1. 创建QueryRunner,并且传入连接池对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2:执行sql
        String sql = "select * from users";
        //BeanListHandler结果集处理器可以将结果封装称为一个个对象
        List<User> list = queryRunner.query(sql, new BeanListHandler<>(User.class));
        System.out.println("list = " + list);
    }

    @Test
    public void test01() throws SQLException {

        //查询数据条数
        //1. 创建QueryRunner,并且传入连接池对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());

        //2:创建sql语句
        String sql = "select count(id) from users";
        //ScalarHandler 结果集处理器只能处理一条数据
        Object query = queryRunner.query(sql, new ScalarHandler<>());

        System.out.println("query = " + query);
    }

五:分组结果集存储并查询

avg()

max()

等等

例如:select `job_id`,avg(salary) from t_employee group by did;   查询的字段不固定

此时需要使用  map 和 list ,map存储一条数据,字段名为key值为数据,多条数据放到list中

即需要使用maplistHandler()

 

@Test
    public void test04() throws SQLException {
        //1:创建queryrunner对象
        QueryRunner queryRunner = new QueryRunner(JDBCDruidUtils.getDataSource());
        //2:创建sql
        String sql = "select `job_id`,avg(salary) from t_employee group by did";
        //3:执行sql
        List<Map<String, Object>> query = queryRunner.query(sql, new MapListHandler());

        //lambda表达式  方式一
        /*query.forEach(new Consumer<Map<String, Object>>() {
            @Override
            public void accept(Map<String, Object> stringObjectMap) {
                stringObjectMap.forEach((k,v)-> System.out.println(k + "==>" + v));
            }
        });*/
        //lambda表达式  方式二   首先遍历list   因为list中的元素是map  因此进行第二次遍历  map
        query.forEach((e) -> {
            e.forEach((k,v) -> {
                System.out.print(k + "<===>" + v + "  ");
            });
            System.out.println();
        } );

六:Idea自动生成实体类

时区不正确需要加上

?useUnicode=true&characterEncoding=UTF8&serverTimezone=UTC"

 自动生成实体类

 注意:

在生成实体类的时候如果字段的名字中如果有下划线的话,要给sql语句的字段名起个别名,小驼峰的方式进行命名,否则将不能赋上值。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值