事物

事物的基本介绍

image.png

总结数据库:

执行不会提交

  • 当自动提交关闭时,执行成功SQL语句,不会把数据提交到数据库
  • 没有提交才有回滚的机会,提交之后就不能回滚了

    默认执行就提交

    image.png

    课堂练习

    image.png

    没有事务

    1. //操作转账的业务
    2. //1.获得连接
    3. Connection connection = null;
    4. //2.组织SQ语句
    5. String sql = "update account set balance = balance + ? where name = ?";
    6. //3.创建PreparedStatement对象
    7. PreparedStatement preparedStatement = null;
    8. try {
    9. connection = JDBCUtils.getConnection();//默认情况下,connection是默认自动提交
    10. preparedStatement = connection.prepareStatement(sql);
    11. //给?赋值
    12. preparedStatement.setDouble(1,-100);
    13. preparedStatement.setString(2,"马云");
    14. //执行
    15. preparedStatement.executeUpdate();//执行第一条
    16. int i = 1 / 0;//抛出异常
    17. //马化腾
    18. //给?赋值
    19. preparedStatement.setDouble(1,100);
    20. preparedStatement.setString(2,"马化腾");
    21. //执行
    22. preparedStatement.executeUpdate();//执行第二条
    23. } catch (SQLException e) {
    24. e.printStackTrace();
    25. }
    26. //关闭资源

    有事务

    1. try {
    2. connection = JDBCUtils.getConnection();//默认情况下,connection是默认自动提交
    3. //将connection 设置为手动提交
    4. connection.setAutoCommit(false);//相当于开启了事务
    5. preparedStatement = connection.prepareStatement(sql);
    6. //给?赋值,//第一条
    7. preparedStatement.setDouble(1,-100);
    8. preparedStatement.setString(2,"马云");
    9. //执行
    10. preparedStatement.executeUpdate();
    11. //给?赋值, //第二条
    12. preparedStatement.setDouble(1,100);
    13. preparedStatement.setString(2,"马化腾");
    14. //执行
    15. preparedStatement.executeUpdate();
    16. connection.commit(); //提交事物
    17. } catch (SQLException e) {
    18. //事务回滚,即撤销执行的SQL
    19. //默认回滚事务开始的状态
    20. try {
    21. connection.rollback();
    22. } catch (SQLException throwables) {
    23. throwables.printStackTrace();
    24. }
    25. e.printStackTrace();
    26. }
    27. //关闭资源

    总结事务

  • try抛出异常之后,catch捕获异常,因此这里进行回滚rollback()

  • 事务回滚,即撤销执行的SQL;默认回滚事务开始的状态
  • commit() 写在java执行sql后面;即catch之前,excuteUpdate之后

    2 批处理

    2.1基本介绍

    image.png

    2.2批处理总结:

    优点:

  • 减少编译次数,减少运行次数,效率大大提升

    注意:

  • jdbc配置文件加url后加参数 ?rewriteBatchedStatements=true

  • addBatch()装好之后,然后executeBacth()再执行,最后clearBatch()清空;

发车顺序:(装数据,执行,清空)
就像发车一样,来回拉不同的客人,preparedStatement来回执行不同的数据

image.png

2.3 课堂练习

演示向admin表中添加5000条数据
注意:jdbc配置文件加url后加参数 ?rewriteBatchedStatements=true

CREATE TABLE admin3( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL, PASSWORD VARCHAR(32) NOT NULL);

a. 传统方式

  1. //连接
  2. String sql = "insert into admin2 values(null,?,?)";
  3. PreparedStatement preparedStatement = connection.prepareStatement(sql);
  4. long start = System.currentTimeMillis();//开始时间
  5. for (int i = 0; i < 5000; i++) {
  6. preparedStatement.setString(1,"jack" + i);
  7. preparedStatement.setString(2,"666");
  8. preparedStatement.executeUpdate();
  9. }
  10. long end = System.currentTimeMillis();
  11. System.out.println("传统方式耗时=" + (end - start));//传统方式耗时=17939
  12. //关闭连接

b. 批处理方式

  1. //连接
  2. String sql = "insert into admin3 values(null,?,?)";
  3. PreparedStatement preparedStatement = connection.prepareStatement(sql);
  4. long start = System.currentTimeMillis();//开始时间
  5. for (int i = 0; i < 5000; i++) {
  6. preparedStatement.setString(1,"Andy" + i);
  7. preparedStatement.setString(2,"123456");
  8. //1.将sql语句加入到批处理包中
  9. preparedStatement.addBatch();
  10. //当有1000条记录时,在批量执行
  11. if((i + 1) % 1000 == 0){//满1000条sql
  12. //2.批量执行
  13. preparedStatement.executeBatch();
  14. //3.清空1000条sql
  15. preparedStatement.clearBatch();
  16. }
  17. }
  18. long end = System.currentTimeMillis();
  19. System.out.println("批量处理方式耗时=" + (end -start));//批量处理方式耗时=178
  20. //关闭连接

2.4 源码分析

image.png
image.png

将sql语句放到批处理包中—>源码分析(preparedStatement.addBatch())

  1. 第一次创建Arraylist - elementDate =>Obejct[]
  2. elementDate =>Obejct[]就会存放我们预处理的sql语句
  3. 当 elementDate满后,就按照1.5扩容
  4. 当添加到指定的值后,就会executeBatch
  5. 批量处理减少发生sql语句的网络开销,且减少了编译次数,因此效率提高

概括总结:addBatch()会数据放到一个集合里面,等到了指定值之后,就执行executeBatch

3 数据库的连接池-解决连接复用问题

3.1 问题引出

image.png

测试

  1. 创建5000个连接未关闭——>程序崩溃
  2. 创建5000个连接,用完就关闭——>消耗太多时间

    image.png总结两个问题:

  3. 如果没有关闭连接,连接太多会报错

  4. 如果关闭连接,频繁的开启连接和关闭连接会消耗大量时间

原因分析连接:

  • 会消耗时间(需要验证ip、用户名和密码),创建连接过多会让数据库崩溃
  • 甚至因为异常数据库未关闭,可能导致数据库内存泄漏

    3.2 基本介绍

    image.png连接池总结:

  1. 连接池放入指定数量的连接,通过网络跟数据库形成了通道
  2. 程序从连接池取出连接
  3. 使用完连接之后,放回连接池
  4. 如果连接用完了,程序请求会进入等待队列

    3.3 连接池原理图:

    image.png

    3.4 数据库连接池种类

    image.png数据库连接池总结image.png

    3.5 C3P0 引用实例

    image.png
    连接池代码体现是DataSource(数据源),连接和管理交给数据源了,以后就是数据源帮连接
    因此数据源需要知道(user、password、driver以及url)
    初始化连接:程序运行创建好的
    最大连接:当程序连接超过初始化连接,会创建新的连接,最多所有连接不能超过最大连接
    等待队列:当程序连接超过最大连接,会进入等待队列,等待其他连接释放
    comboPooledDataSource.setInitialPoolSize(10);//程序运行之后连接池放10个连接

    1. //方式1:相关参数,在程序中指定user,url,password等
    2. public void testC3P0_01() throws Exception {
    3. //1.创建一个数据源对象
    4. ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
    5. //2.通过配置文件获取相关信息
    6. Properties properties = new Properties();
    7. properties.load(new FileInputStream("src\\mysql.properties"));
    8. //读取相关属性值
    9. String user = properties.getProperty("user");
    10. String password = properties.getProperty("password");
    11. String url = properties.getProperty("url");
    12. String driver = properties.getProperty("driver");
    13. //给数据源comboPooledDataSource设置相关参数
    14. //注意:连接管理由comboPooledDataSource来管理的
    15. comboPooledDataSource.setDriverClass(driver);//连接数据库的驱动
    16. comboPooledDataSource.setJdbcUrl(url);
    17. comboPooledDataSource.setUser(user);
    18. comboPooledDataSource.setPassword(password);
    19. //初始化数据源连接数
    20. //初始化连接:程序运行创建好的
    21. //最大连接:当程序连接超过初始化连接,会创建新的连接,最多所有连接不能超过最大连接
    22. //等待队列:当程序连接超过最大连接,会进入等待队列,等待其他连接释放
    23. comboPooledDataSource.setInitialPoolSize(10);//程序运行之后连接池放10个连接
    24. //最大连接数
    25. comboPooledDataSource.setMaxPoolSize(50);
    26. //测试连接池的效率,测试连接mysql5000次
    27. long start = System.currentTimeMillis();
    28. for (int i = 0; i < 5000; i++) {
    29. //这个是实现DataSource接口的方法
    30. Connection connection = comboPooledDataSource.getConnection();
    31. connection.close();
    32. }
    33. long end = System.currentTimeMillis();
    34. System.out.println("C3P0 5000连接,耗时" + (end - start));//C3P0 5000连接,耗时2300
    35. }
    1. ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("hsp_edu");
    2. //测试连接池的效率,测试连接mysql5000次
    3. long start = System.currentTimeMillis();
    4. for (int i = 0; i < 5000; i++) {
    5. Connection connection = comboPooledDataSource.getConnection();
    6. connection.close();
    7. }
    8. long end = System.currentTimeMillis();
    9. System.out.println("C3P0的第二种方式:5000连接,耗时" + (end - start));
    10. //C3P0的第二种方式:5000连接,耗时2157

    修改配置文件c3p0-config.xml

    url、driver、user、password
    最大连接,最小连接,初始化连接、每次增长连接、每个连接最多执行的语句

3.5 DRUID 引用实例

数据源是帮忙管理的数据库的连接,因此需要驱动,仅仅在上面做了包装做了缓冲池而已

配置文件

  1. #key=value
  2. driverClassName=com.mysql.jdbc.Driver
  3. url=jdbc:mysql://localhost:3306/hsp_db02?rewriteBatchedStatements=true
  4. username=root
  5. password=root
  6. #initial connection Size
  7. initialSize=10
  8. #min idle connecton size
  9. minIdle=5
  10. #max active connection size
  11. maxActive=50
  12. #max wait time (5000 mil seconds)
  13. maxWait=5000

maxWait=5000
等待队列:超过5秒还没有取到连接,放弃等待
解决方案:调整initialSize和MaxActive

  1. //1.加入Druid jar包
  2. //2.加入配置文件,将文件拷贝项目的src目录
  3. //3.创建properties对象,读取配置文件
  4. Properties properties = new Properties();
  5. properties.load(new FileInputStream("src\\druid.properties"));
  6. //4.创建一个指定参数的数据库连接池,Druid的连接池
  7. //连接池创建根据配置文件properties的参数作为依据
  8. DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
  9. long start = System.currentTimeMillis();
  10. for (int i = 0; i < 500000; i++) {
  11. Connection connection = dataSource.getConnection();
  12. connection.close();
  13. }
  14. long end = System.currentTimeMillis();
  15. System.out.println("druid连接池 操作500000次,耗时=" + (end - start));
  16. //druid连接池 操作500000次,耗时=3517

image.png
image.png

mysql数据库厂商原生和Druid是Alibaba第三方实现

  1. 前后者实现java接口的方式不同,因此两者实现类是不同的(动态绑定)
  2. 前者的connection是创建连接,而后者是取到连接
  3. 前者close是真正关闭连接,后者是把连接引用断掉,连接放到数据源中

    把Druid的配置文件和连接、关闭改为工具类

    工具类

    1. public class JDBCUtilsByDruid {
    2. private static DataSource ds;
    3. //在静态代码块完成ds初始化
    4. static {
    5. Properties properties = new Properties();
    6. try {
    7. properties.load(new FileInputStream("src\\druid.properties"));
    8. ds = DruidDataSourceFactory.createDataSource(properties);
    9. } catch (Exception e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. //编写getConnection方法
    14. public static Connection getConnection(){
    15. try {
    16. return ds.getConnection();
    17. } catch (SQLException e) {
    18. throw new RuntimeException(e);
    19. }
    20. }
    21. //关闭连接,强调:在数据库连接池技术中,close不是真正的断掉连接
    22. //而是把使用的Connection对象放回连接池
    23. public static void close(ResultSet resultSet, Statement statement,Connection connection){
    24. //判断是否为null
    25. try {
    26. if(resultSet != null){
    27. resultSet.close();
    28. }
    29. if(statement != null){
    30. statement.close();
    31. }
    32. if(connection != null){
    33. connection.close();
    34. }
    35. } catch (SQLException e) {
    36. //将编译异常转成运行异常抛出
    37. throw new RuntimeException(e);
    38. }
    39. }
    40. }

    image.png

    工具类总结:

    工具类必要项——静态代码块:配置文件读取和创建数据源
    多处使用的部分——静态属性:数据源(传入配置文件参数到druid的数据工厂获得一个数据源)
    分离的部分——静态方法:connection( )和close( )

    使用工具类

    1. //1.得到连接
    2. Connection connection = null;
    3. //2.组织sql
    4. String sql = "select * from actor where id >= ?";
    5. //3.创建PreparedStatement对象
    6. PreparedStatement preparedStatement = null;
    7. ResultSet resultSet = null;
    8. try {
    9. connection = JDBCUtilsByDruid.getConnection();
    10. preparedStatement = connection.prepareStatement(sql);
    11. preparedStatement.setInt(1,4);
    12. //执行,得到结果集
    13. resultSet = preparedStatement.executeQuery();
    14. //遍历结果集
    15. while(resultSet.next()){
    16. int id = resultSet.getInt("id");
    17. String name = resultSet.getString("name");
    18. String gender = resultSet.getString("gender");
    19. Date birthday = resultSet.getDate("birthday");
    20. String cellphone = resultSet.getString("cellphone");
    21. System.out.println(id + "\t" + name + "\t" + gender + "\t" + birthday + "\t" + cellphone);
    22. }
    23. } catch (SQLException e) {
    24. e.printStackTrace();
    25. }finally {
    26. JDBCUtilsByDruid.close(resultSet,preparedStatement,connection);
    27. }

    4.Apache-DBUtils-解决结果集复用和增删改查问题

    4.1 分析一个问题

    image.png

    分析问题:

  • resultSet要connectin一直处于连接状态
  • 其他处不能调用ressultSet

    4.2 原理图

    image.png


    Actor类把mysql的字段封装到属性中 ```java //核心代码 //创建ArrayList对象,存放Actor ArrayList list = new ArrayList<>();

//把得到的resultSet的记录,封装到Actor对象,放入到list集合 list.add(new Actor(id,name,gender,birthday,cellphone));

//调用者直接调用方法得mysql集合复用 public ArrayList testSelectToArrayList(){ //因为Arraylist和connection没有任何关联,所以该集合可以复用 return list;
}

  1. <a name="DQZUF"></a>
  2. ### 封装到集合优势:
  3. 1. 可复用:把字段存放=>Actor类中,然后把Actor对象=>存放到集合中 可复用
  4. 1. 方法返回集合:可以把方法void改为返回集合,这样调用者可直接得到数据集合
  5. 1. 依赖性变得更差:断掉connection,resultSet不影响,已经把数据封装到集合中了,集合跟连接是没有关系的
  6. <a name="wKPzd"></a>
  7. ## 4.3 Apache-DBUtils
  8. <a name="I0S2d"></a>
  9. ## ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1621769438099-52a373ce-6b37-4ab5-acd4-7f772fe1fab1.png#clientId=u3c30609c-2470-4&from=paste&height=727&id=u1769c121&name=image.png&originHeight=727&originWidth=1341&originalType=binary&size=1510990&status=done&style=none&taskId=u153ee74d-f31e-4000-82d8-02c85b21b1d&width=1341)
  10. <a name="NrRx6"></a>
  11. #### jar包[commons-dbutils-1.3.jar](https://www.yuque.com/attachments/yuque/0/2021/jar/12555714/1621837842778-2fd7d256-83c1-458f-8576-f42880595b11.jar?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2021%2Fjar%2F12555714%2F1621837842778-2fd7d256-83c1-458f-8576-f42880595b11.jar%22%2C%22name%22%3A%22commons-dbutils-1.3.jar%22%2C%22size%22%3A41376%2C%22type%22%3A%22%22%2C%22ext%22%3A%22jar%22%2C%22status%22%3A%22done%22%2C%22taskId%22%3A%22ud339c9ce-515a-4680-92e2-6d19946b9c8%22%2C%22taskType%22%3A%22upload%22%2C%22id%22%3A%22u7f42bd5c%22%2C%22card%22%3A%22file%22%7D)
  12. <a name="ktiD3"></a>
  13. ## 4.4 课堂练习
  14. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12555714/1621769832580-f1af10e4-b1bc-40aa-a5d3-5a71d261f173.png#clientId=u3c30609c-2470-4&from=paste&height=193&id=ue95e934c&name=image.png&originHeight=193&originWidth=522&originalType=binary&size=135435&status=done&style=none&taskId=uc5aa3fe5-251b-4c77-b7c3-57f43652bd5&width=522)
  15. <a name="YXUCq"></a>
  16. ### 代码实现:
  17. <a name="fDvNv"></a>
  18. #### 多行实现
  19. ```java
  20. //演示apache-dbutils + druid完成返回结果是多行记录
  21. public void testQueryMany() throws SQLException {//返回结果是多行的情况
  22. //1.得到连接(druid)
  23. Connection connection = JDBCUtilsByDruid.getConnection();
  24. //2.使用DBUtils类和接口,先引入DBUtils相关jar
  25. //3.创建QueryRunner
  26. QueryRunner queryRunner = new QueryRunner();
  27. //4.可以执行相关的方法,返回Arraylist结果集
  28. String sql = "select * from actor where id >= ?";
  29. //说明:query()
  30. List<Actor> list =
  31. queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
  32. for(Actor actor : list){
  33. System.out.println(actor);
  34. }
  35. JDBCUtilsByDruid.close(null,null,connection);
  36. }

单行实现

  1. //演示apache-dbutils + druid完成返回结果是单行记录(单个记录)
  2. 核心代码
  3. //因为返回单个记录--->单个记录,使用Handler是BeanHandler
  4. Actor actor = queryRunner.query(connection, sql, new BeanHandler<>(Actor.class),4);
  5. //释放连接

单行单列

  1. //演示 apache-dbutils + druid 完成查询结果是单行单列-返回就是object
  2. //核心代码
  3. //因为返回的是一个对象,使用handler就是ScalarHandler
  4. Object obj = queryRunner.query(connection, sql, new ScalarHandler(), 4);

dml

  1. //演示 apache-dbutils + druid 完成dml
  2. //执行相关方法,返回单个对象
  3. String sql = "update actor set name = ? where id = ?";
  4. //1.执行dml操作是queryRunner.update()
  5. //2.返回值是受影响的行数
  6. int affectRow = queryRunner.update(connection, sql, "刘德华", 8);
  7. System.out.println(affectRow > 0 ? "执行成功" : "执行没有影响到表");
  8. String sql = "insert into actor values(null,?,?,?,?)";
  9. int affectRow = queryRunner.update(connection, sql, "赵敏","女","2000-10-10","17984559847");
  10. String sql = "delete from actor where id = ?";
  11. int affectRow = queryRunner.update(connection, sql, 4);

4.5 总结说明:

  1. query() 执行sql语句,得到resultSet—-封装到—->ArrayList集合中
  2. 返回集合
  3. connection:连接
  4. sql:执行sql语句
  5. new BeanListHandler<>(Actor.class):在resultSet—>Actor对象—>封装到ArrayList

底层使用反射机制去获取Actor类的属性,然后进行封装

  1. 1就是给sql语句中的?赋值,可以有多个值,因为是可变餐宿Object…params
  2. 底层得到resultSet,会在query() 关闭,还会关闭PreparedStatement

image.png

4.6 源码分析:

分析queryRunner.query方法:
image.png
image.png

4.7 表和JavaBean的类型映射关系

image.png

mysql列类型和java数据类型总结:

  1. 数值型:基本类型用包装类,因为msql的所有类型都有可能是null
  2. 字符串:mysql中的char,java要用String
  3. 日期型:mysql中的date,java可以用date或String

    5.Dao-解决灵活使用sql

    5.1 先分析一个问题

    image.png5.2 原理图

    image.png

    5.3 基本说明

    image.png

    5.4 应用实例

    image.png

    代码实现

    utils====>JDBCUtilsByDruid apach-dbutils
    javabean====>Actor
    DAO====>ActorDAO继承BasicDAO,BasicDAO是其他DAO的父类,ActorDAO根据业务需求写特有的功能(演示为空)
    test====>创建ActorDAO对象,因为ActorDAO继承BasicDAO,所以可以直接使用BasicDAO的增删改查

    BasicDAO

    1. // apach-dbutils
    2. public class BasicDAO<T> {//泛型指定具体类型
    3. private QueryRunner qr = new QueryRunner();
    4. //开发通用dml方法,针对任意的表
    5. public int update(String sql,Object... params) throws Exception {
    6. Connection connection = JDBCUtilsByDruid.getConnection();
    7. int rows = qr.update(connection, sql, params);
    8. return rows;
    9. JDBCUtilsByDruid.close(null, null, connection);
    10. }
    11. //查询多行记录
    12. public List<T> queryMultiple(String sql,Class<T> clazz,Object... params){
    13. Connection connection = JDBCUtilsByDruid.getConnection();
    14. return qr.query(connection, sql, new BeanListHandler<T>(clazz), params);
    15. JDBCUtilsByDruid.close(null, null, connection);
    16. }
    17. //查询单行结果的通用方法
    18. public T querySingle(String sql,Class<T> clazz,Object... params) {
    19. Connection connection = JDBCUtilsByDruid.getConnection();
    20. return qr.query(connection, sql, new BeanHandler<T>(clazz), params);
    21. JDBCUtilsByDruid.close(null, null, connection);
    22. }
    23. //查询单行单列结果的通用方法
    24. public Object queryScalar(String sql,Object... params){
    25. Connection connection = JDBCUtilsByDruid.getConnection();
    26. return qr.query(connection, sql,new ScalarHandler(), params);
    27. JDBCUtilsByDruid.close(null, null, connection);
    28. }
    29. }

    5.4 课后练习

    image.png