• JDBC学习笔记(三)高级篇


    一、JDBC 优化及工具类封装

    1.1 现有问题

    1.2 JDBC 工具类封装 V1.0

    resources/db.properties配置文件:

    1. driverClassName=com.mysql.cj.jdbc.Driver
    2. url=jdbc:mysql:///atguigu
    3. username=root
    4. password=123456
    5. initialSize=10
    6. maxActive=20

    工具类代码:

    1. package com.atguigu.advanced.senior.util;
    2. import com.alibaba.druid.pool.DruidDataSourceFactory;
    3. import javax.sql.DataSource;
    4. import java.io.IOException;
    5. import java.io.InputStream;
    6. import java.sql.Connection;
    7. import java.sql.SQLException;
    8. import java.util.Properties;
    9. /**
    10. * JDBC工具类(V1.0)
    11. * 1.维护一个连接池对象
    12. * 2.对外提供在连接池中获取连接的方法
    13. * 3.对外提供回收连接的方法
    14. * 【注意】:工具类仅对外提供共性的功能代码,所以方法均为静态方法!
    15. */
    16. public class JDBCUtil {
    17. //创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的
    18. private static DataSource dataSource;
    19. //在项目启动时,即创建连接池对象,赋值给 dataSource
    20. static {
    21. try {
    22. Properties properties = new Properties();
    23. InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
    24. properties.load(inputStream);
    25. dataSource = DruidDataSourceFactory.createDataSource(properties);
    26. } catch (Exception e) {
    27. throw new RuntimeException(e);
    28. }
    29. }
    30. //对外提供在连接池中获取连接的方法
    31. public static Connection getConnection(){
    32. try {
    33. return dataSource.getConnection();
    34. } catch (SQLException e) {
    35. throw new RuntimeException(e);
    36. }
    37. }
    38. //对外提供回收连接的方法
    39. public static void release(Connection connection){
    40. try {
    41. connection.close();
    42. } catch (SQLException e) {
    43. throw new RuntimeException(e);
    44. }
    45. }
    46. }
    测试代码:
    1. package com.atguigu.advanced.senior;
    2. import com.atguigu.advanced.senior.util.JDBCUtil;
    3. import org.junit.Test;
    4. import java.sql.Connection;
    5. public class JDBCUtilTest {
    6. @Test
    7. public void testGetConnection() {
    8. Connection connection = JDBCUtil.getConnection();
    9. System.out.println(connection);
    10. //CRUD
    11. JDBCUtil.release(connection);
    12. }
    13. }

    1.3 ThreadLocal (本地线程)

    问题:

    同一用户线程多次操作获取了多个连接,造成连接资源的浪费

    怎么取?

    怎么存?

    怎么移除?

    • 在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束
    • 线程间数据隔离
    • 进行事务操作,用于存储线程事务信息
    • 数据库连接, Session 会话管理

    1. ThreadLocal对象.get:获取ThreadLocal中当前线程共享变量的值
    2. ThreadLocal对象.set:设置ThreadLocal中当前线程共享变量的值
    3. ThreadLocal对象.remove:移除ThreadLocal中当前线程共享变量的值。

    1.4 JDBC 工具类封装 V2.0

    1. package com.atguigu.advanced.senior.util;
    2. import com.alibaba.druid.pool.DruidDataSourceFactory;
    3. import javax.sql.DataSource;
    4. import java.io.InputStream;
    5. import java.sql.Connection;
    6. import java.sql.SQLException;
    7. import java.util.Properties;
    8. /**
    9. * JDBC工具类(V2.0)
    10. * 1.维护一个连接池对象,维护了一个线程绑定变量的ThreadLocal对象
    11. * 2.对外提供在ThreadLocal中获取连接的方法
    12. * 3.对外提供回收连接的方法,回收过程中,将要回收的连接从ThreadLocal中移除!
    13. * 【注意】:工具类仅对外提供共性的功能代码,所以方法均为静态方法!
    14. * 【注意】:使用ThreadLocal就是为了一个线程在多次数据库操作过程中,使用的是同一个连接!
    15. */
    16. public class JDBCUtilV2 {
    17. //创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的
    18. private static DataSource dataSource;
    19. private static ThreadLocal threadLocal = new ThreadLocal<>();
    20. //在项目启动时,即创建连接池对象,赋值给 dataSource
    21. static {
    22. try {
    23. Properties properties = new Properties();
    24. InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
    25. properties.load(inputStream);
    26. dataSource = DruidDataSourceFactory.createDataSource(properties);
    27. } catch (Exception e) {
    28. throw new RuntimeException(e);
    29. }
    30. }
    31. //对外提供在连接池中获取连接的方法
    32. public static Connection getConnection(){
    33. try {
    34. //在ThreadLocal中获取Connection
    35. Connection connection = threadLocal.get();
    36. //threadLoacl里没有存储Connection,也就是第一次获取
    37. if(connection == null){
    38. connection = dataSource.getConnection();
    39. threadLocal.set(connection);
    40. }
    41. return connection;
    42. } catch (SQLException e) {
    43. throw new RuntimeException(e);
    44. }
    45. }
    46. //对外提供回收连接的方法
    47. public static void release(){
    48. try {
    49. Connection connection = threadLocal.get();
    50. if (connection != null){
    51. //从threadLocal中移除当前已经存储的Connection对象
    52. threadLocal.remove();
    53. //将Connection对象归还给连接池
    54. connection.close();
    55. }
    56. connection.close();
    57. } catch (SQLException e) {
    58. throw new RuntimeException(e);
    59. }
    60. }
    61. }

    测试代码

    1. @Test
    2. public void testJDBCV2(){
    3. /*
    4. JDBCUtil 获取连接
    5. */
    6. Connection connection1 = JDBCUtil.getConnection();
    7. Connection connection2 = JDBCUtil.getConnection();
    8. Connection connection3 = JDBCUtil.getConnection();
    9. System.out.println(connection1);
    10. System.out.println(connection1);
    11. System.out.println(connection1);
    12. System.out.println("================================");
    13. /*
    14. JDBCUtilV2 获取连接
    15. */
    16. Connection connectionA = JDBCUtilV2.getConnection();
    17. Connection connectionB = JDBCUtilV2.getConnection();
    18. Connection connectionC = JDBCUtilV2.getConnection();
    19. System.out.println(connectionA);
    20. System.out.println(connectionB);
    21. System.out.println(connectionC);
    22. }

    二、DAO封装及BaseDAO 工具类

    2.1 DAO 概念

    1、DAO:Data Access Object,数据访问对象。

    2、Java 是面向对象语言,数据在 Java 中通常以对象的形式存在。一张表对应一个实体类,一张表的操作对应一个 DAO 对象。

    3、在 Java 操作数据库时,我们会将对同一张表的增删改查操作统一维护起来,维护的这个类就是 DAO 层。

    4、DAO 层只关注对数据库的操作,供业务层 Service 调用,将职责划分清楚!

    EmployeeDao.java

    1. package com.atguigu.advanced.senior.dao;
    2. import com.atguigu.advanced.senior.pojo.Employee;
    3. import java.util.List;
    4. /**
    5. * EmployeeDao 这个类对应的是 t_emp 这张表的增删改查的操作
    6. */
    7. public interface EmployeeDao {
    8. /**
    9. * 数据库对应的查询所有的操作
    10. * @return 表中所有的数据
    11. */
    12. List selectAll();
    13. /**
    14. * 数据库对应的根据empId查询单个员工数据操作
    15. * @param empId
    16. * @return 一个员工对象(一行数据)
    17. */
    18. Employee selectByEmpId(Integer empId);
    19. /**
    20. * 数据库对应的新增一条员工数据
    21. * @param employee ORM思想中的一个员工对象
    22. * @return 受影响的行数
    23. */
    24. int insert(Employee employee);
    25. /**
    26. * 数据库对应的修改一条员工数据
    27. * @param employee ORM思想中的一个员工对象
    28. * @return 受影响的行数
    29. */
    30. int update(Employee employee);
    31. /**
    32. * 数据库对应的根据empId删除一条员工数据
    33. * @param empId 主键列
    34. * @return 受影响的行数
    35. */
    36. int delete(Integer empId);
    37. }

    EmployeeDaoImpl.java

    1. package com.atguigu.advanced.senior.dao.impl;
    2. import com.atguigu.advanced.senior.dao.EmployeeDao;
    3. import com.atguigu.advanced.senior.pojo.Employee;
    4. import java.util.List;
    5. public class EmployeeDaoImpl implements EmployeeDao {
    6. @Override
    7. public List selectAll() {
    8. //1.注册驱动
    9. //2.获取连接
    10. //3.预编译SQL语句
    11. //4.为占位符赋值,执行SQL,接受返回结果
    12. //5.处理结果
    13. //6.释放资源
    14. return null;
    15. }
    16. @Override
    17. public Employee selectByEmpId(Integer empId) {
    18. return null;
    19. }
    20. @Override
    21. public int insert(Employee employee) {
    22. return 0;
    23. }
    24. @Override
    25. public int update(Employee employee) {
    26. return 0;
    27. }
    28. @Override
    29. public int delete(Integer empId) {
    30. return 0;
    31. }
    32. }

    2.2 BaseDAO 概念

    基本上每一个数据表都应该有一个对应的 DAO 接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些 DAO 的实现类可以抽取一个公共的父类,复用增删改查的基本操作,我们称为 BaseDAO。

    2.3 BaseDAO 搭建

    1. package com.atguigu.advanced.senior.dao;
    2. import com.atguigu.advanced.senior.util.JDBCUtilV2;
    3. import java.sql.Connection;
    4. import java.sql.PreparedStatement;
    5. /**
    6. * 将共性的数据库的操作代码封装在BaseDAO里
    7. */
    8. public class BaseDAO {
    9. /**
    10. * 通用的增删改的方法
    11. * @param sql 调用者要执行的SQL语句
    12. * @param params SQL语句中的占位符要赋值的参数
    13. * @return 受影响的行数
    14. */
    15. public int executeUpdate(String sql,Object... params) throws Exception {
    16. //1.通过JDBCUtilV2获取数据库连接
    17. Connection connection = JDBCUtilV2.getConnection();
    18. //3.预编译SQL语句
    19. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    20. //4.为占位符赋值,执行SQL,接受返回结果
    21. if (params != null && params.length > 0){
    22. for(int i = 0; i < params.length; i++){
    23. //占位符是从1开始的,参数的数组是从0开始的
    24. preparedStatement.setObject(i+1,params[i]);
    25. }
    26. }
    27. int row = preparedStatement.executeUpdate();
    28. //5.释放资源
    29. preparedStatement.close();
    30. JDBCUtilV2.release();
    31. //6.返回结果
    32. return row;
    33. }
    34. }

    2.4 BaseDAO 的应用

    1、BaseDAO 搭建通用查询方法思路

     

    1. package com.atguigu.advanced.senior.dao;
    2. import com.atguigu.advanced.senior.util.JDBCUtilV2;
    3. import java.lang.reflect.Field;
    4. import java.sql.Connection;
    5. import java.sql.PreparedStatement;
    6. import java.sql.ResultSet;
    7. import java.sql.ResultSetMetaData;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. /**
    11. * 将共性的数据库的操作代码封装在BaseDAO里
    12. */
    13. public class BaseDAO {
    14. /**
    15. * 通用的增删改的方法
    16. * @param sql 调用者要执行的SQL语句
    17. * @param params SQL语句中的占位符要赋值的参数
    18. * @return 受影响的行数
    19. */
    20. public int executeUpdate(String sql,Object... params) throws Exception {
    21. //1.通过JDBCUtilV2获取数据库连接
    22. Connection connection = JDBCUtilV2.getConnection();
    23. //3.预编译SQL语句
    24. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    25. //4.为占位符赋值,执行SQL,接受返回结果
    26. if (params != null && params.length > 0){
    27. for(int i = 0; i < params.length; i++){
    28. //占位符是从1开始的,参数的数组是从0开始的
    29. preparedStatement.setObject(i+1,params[i]);
    30. }
    31. }
    32. int row = preparedStatement.executeUpdate();
    33. //5.释放资源
    34. preparedStatement.close();
    35. JDBCUtilV2.release();
    36. //6.返回结果
    37. return row;
    38. }
    39. /**
    40. * 通用的查询:多行多列、单行多列、单行单列
    41. * 多行多列:List
    42. * 单行多列:Employee
    43. * 单行单列:封装的是一个结果。Double、Integer......
    44. *
    45. * 封装的过程:
    46. * 1.返回的类型:泛型:类型不确定,调用者知道,调用时,将此次查询的结果类型告知BaseDAO就可以了
    47. * 2.返回的结果:通用,List 可以存储多个结果,也可也存储一个结果 get(0)
    48. * 3.结果的封装:反射,要求调用者告知BaseDAO要封装对象的类对象。Class
    49. */
    50. public List executeQuery(Class clazz, String sql,Object... params) throws Exception {
    51. //获取连接
    52. Connection connection = JDBCUtilV2.getConnection();
    53. //预编译SQL语句
    54. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    55. //设置占位符的值
    56. if(params != null && params.length > 0){
    57. for(int i = 0; i < params.length; i++){
    58. preparedStatement.setObject(i+1,params[i]);
    59. }
    60. }
    61. //执行SQL,并接受返回的结果集
    62. ResultSet resultSet = preparedStatement.executeQuery();
    63. //获取结果集中的元数据对象
    64. //包含了:列的数量、每个列的名称
    65. ResultSetMetaData metaData = resultSet.getMetaData();
    66. //获取列的数量
    67. int columnCount = metaData.getColumnCount();
    68. List list = new ArrayList();
    69. //处理结果
    70. while (resultSet.next()){
    71. //循环一次,代表有一行数据,通过反射创建一个对象
    72. T t = clazz.newInstance();
    73. //循环遍历当前行的列,循环几次,看有多少列
    74. for (int i = 1; i <= columnCount ; i++) {
    75. //通过下标获取列的值
    76. Object value = resultSet.getObject(i);
    77. //获取到的列的value值,这个值就是 t这个对象中的某一个属性
    78. //获取当前拿到的列的名字 = 对象的属性名
    79. String fieldName = metaData.getColumnLabel(i);
    80. //通过类对象和fieldName(字段名)获取要封装的对象的属性
    81. Field field = clazz.getDeclaredField(fieldName);
    82. //突破封装的private
    83. field.setAccessible(true);
    84. field.set(t, value);
    85. }
    86. list.add(t);
    87. }
    88. //资源的关闭
    89. resultSet.close();
    90. preparedStatement.close();
    91. JDBCUtilV2.release();
    92. return list;
    93. }
    94. }

    2、BaseDAO 搭建查询单个结果方法

    1. package com.atguigu.advanced.senior.dao;
    2. import com.atguigu.advanced.senior.util.JDBCUtilV2;
    3. import java.lang.reflect.Field;
    4. import java.sql.Connection;
    5. import java.sql.PreparedStatement;
    6. import java.sql.ResultSet;
    7. import java.sql.ResultSetMetaData;
    8. import java.util.ArrayList;
    9. import java.util.List;
    10. /**
    11. * 将共性的数据库的操作代码封装在BaseDAO里
    12. */
    13. public class BaseDAO {
    14. /**
    15. * 通用的增删改的方法
    16. * @param sql 调用者要执行的SQL语句
    17. * @param params SQL语句中的占位符要赋值的参数
    18. * @return 受影响的行数
    19. */
    20. public int executeUpdate(String sql,Object... params) throws Exception {
    21. //1.通过JDBCUtilV2获取数据库连接
    22. Connection connection = JDBCUtilV2.getConnection();
    23. //3.预编译SQL语句
    24. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    25. //4.为占位符赋值,执行SQL,接受返回结果
    26. if (params != null && params.length > 0){
    27. for(int i = 0; i < params.length; i++){
    28. //占位符是从1开始的,参数的数组是从0开始的
    29. preparedStatement.setObject(i+1,params[i]);
    30. }
    31. }
    32. int row = preparedStatement.executeUpdate();
    33. //5.释放资源
    34. preparedStatement.close();
    35. JDBCUtilV2.release();
    36. //6.返回结果
    37. return row;
    38. }
    39. /**
    40. * 通用的查询:多行多列、单行多列、单行单列
    41. * 多行多列:List
    42. * 单行多列:Employee
    43. * 单行单列:封装的是一个结果。Double、Integer......
    44. *
    45. * 封装的过程:
    46. * 1.返回的类型:泛型:类型不确定,调用者知道,调用时,将此次查询的结果类型告知BaseDAO就可以了
    47. * 2.返回的结果:通用,List 可以存储多个结果,也可也存储一个结果 get(0)
    48. * 3.结果的封装:反射,要求调用者告知BaseDAO要封装对象的类对象。Class
    49. */
    50. public List executeQuery(Class clazz, String sql,Object... params) throws Exception {
    51. //获取连接
    52. Connection connection = JDBCUtilV2.getConnection();
    53. //预编译SQL语句
    54. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    55. //设置占位符的值
    56. if(params != null && params.length > 0){
    57. for(int i = 0; i < params.length; i++){
    58. preparedStatement.setObject(i+1,params[i]);
    59. }
    60. }
    61. //执行SQL,并接受返回的结果集
    62. ResultSet resultSet = preparedStatement.executeQuery();
    63. //获取结果集中的元数据对象
    64. //包含了:列的数量、每个列的名称
    65. ResultSetMetaData metaData = resultSet.getMetaData();
    66. //获取列的数量
    67. int columnCount = metaData.getColumnCount();
    68. List list = new ArrayList();
    69. //处理结果
    70. while (resultSet.next()){
    71. //循环一次,代表有一行数据,通过反射创建一个对象
    72. T t = clazz.newInstance();
    73. //循环遍历当前行的列,循环几次,看有多少列
    74. for (int i = 1; i <= columnCount ; i++) {
    75. //通过下标获取列的值
    76. Object value = resultSet.getObject(i);
    77. //获取到的列的value值,这个值就是 t这个对象中的某一个属性
    78. //获取当前拿到的列的名字 = 对象的属性名
    79. String fieldName = metaData.getColumnLabel(i);
    80. //通过类对象和fieldName(字段名)获取要封装的对象的属性
    81. Field field = clazz.getDeclaredField(fieldName);
    82. //突破封装的private
    83. field.setAccessible(true);
    84. field.set(t, value);
    85. }
    86. list.add(t);
    87. }
    88. //资源的关闭
    89. resultSet.close();
    90. preparedStatement.close();
    91. JDBCUtilV2.release();
    92. return list;
    93. }
    94. /**
    95. * 通用查询:在上面查询的集合结果中获取第一个结果,简化了获取单行单列的获取、单行多列的获取
    96. */
    97. public T executeQueryBean(Class clazz, String sql, Object... params) throws Exception{
    98. List list = this.executeQuery(clazz, sql, params);
    99. if (list == null || list.size() == 0){
    100. return null;
    101. }
    102. return list.get(0);
    103. }
    104. }

    3、DAO 结合 BaseDAO 完成 CRUD

    工具类:
    1. package com.atguigu.advanced.senior.dao.impl;
    2. import com.atguigu.advanced.senior.dao.BaseDAO;
    3. import com.atguigu.advanced.senior.dao.EmployeeDao;
    4. import com.atguigu.advanced.senior.pojo.Employee;
    5. import java.util.List;
    6. public class EmployeeDaoImpl extends BaseDAO implements EmployeeDao {
    7. @Override
    8. public List selectAll() {
    9. //1.注册驱动
    10. //2.获取连接
    11. //3.预编译SQL语句
    12. //4.为占位符赋值,执行SQL,接受返回结果
    13. //5.处理结果
    14. //6.释放资源
    15. try {
    16. String sql = "select emp_id empId, emp_name empName, emp_salary empSalary, emp_age empAge from t_emp";
    17. return executeQuery(Employee.class,sql,null);
    18. } catch (Exception e) {
    19. throw new RuntimeException(e);
    20. }
    21. }
    22. @Override
    23. public Employee selectByEmpId(Integer empId) {
    24. try {
    25. String sql = "select emp_id empId,emp_name empName,emp_salary empSalary,emp_age empAge from t_emp where emp_id = ?";
    26. return executeQueryBean(Employee.class,sql,empId);
    27. } catch (Exception e) {
    28. throw new RuntimeException(e);
    29. }
    30. }
    31. @Override
    32. public int insert(Employee employee) {
    33. try {
    34. String sql = "insert into t_emp(emp_name,emp_salary,emp_age) values(?,?,?)";
    35. return executeUpdate(sql,employee.getEmpName(),employee.getEmpSalary(),employee.getEmpAge());
    36. } catch (Exception e) {
    37. throw new RuntimeException(e);
    38. }
    39. }
    40. @Override
    41. public int update(Employee employee) {
    42. try {
    43. String sql = "update t_emp set emp_salary = ? where emp_id = ?";
    44. return executeUpdate(sql,employee.getEmpSalary(),employee.getEmpId());
    45. } catch (Exception e) {
    46. throw new RuntimeException(e);
    47. }
    48. }
    49. @Override
    50. public int delete(Integer empId) {
    51. try {
    52. String sql = "delete from t_emp where emp_id = ?";
    53. return executeUpdate(sql,empId);
    54. } catch (Exception e) {
    55. throw new RuntimeException(e);
    56. }
    57. }
    58. }
    测试代码:
    1. package com.atguigu.advanced.senior;
    2. import com.atguigu.advanced.senior.dao.impl.EmployeeDaoImpl;
    3. import com.atguigu.advanced.senior.pojo.Employee;
    4. import com.atguigu.advanced.senior.util.JDBCUtil;
    5. import com.atguigu.advanced.senior.util.JDBCUtilV2;
    6. import org.junit.Test;
    7. import java.sql.Connection;
    8. import java.util.List;
    9. public class JDBCUtilTest {
    10. @Test
    11. public void testGetConnection() {
    12. Connection connection = JDBCUtil.getConnection();
    13. System.out.println(connection);
    14. //CRUD
    15. JDBCUtil.release(connection);
    16. }
    17. @Test
    18. public void testJDBCV2(){
    19. /*
    20. JDBCUtil 获取连接
    21. */
    22. Connection connection1 = JDBCUtil.getConnection();
    23. Connection connection2 = JDBCUtil.getConnection();
    24. Connection connection3 = JDBCUtil.getConnection();
    25. System.out.println(connection1);
    26. System.out.println(connection1);
    27. System.out.println(connection1);
    28. System.out.println("================================");
    29. /*
    30. JDBCUtilV2 获取连接
    31. */
    32. Connection connectionA = JDBCUtilV2.getConnection();
    33. Connection connectionB = JDBCUtilV2.getConnection();
    34. Connection connectionC = JDBCUtilV2.getConnection();
    35. System.out.println(connectionA);
    36. System.out.println(connectionB);
    37. System.out.println(connectionC);
    38. }
    39. @Test
    40. public void testEmployeeDao() {
    41. //1.创建DAO实现类
    42. EmployeeDaoImpl employeeDao = new EmployeeDaoImpl();
    43. /*
    44. //2.调用查询所有方法
    45. List employeeList = employeeDao.selectAll();
    46. //3.处理结果
    47. for (Employee employee : employeeList) {
    48. System.out.println("employee = " + employee);
    49. }
    50. */
    51. /*
    52. //调用根据id查询单个员工方法
    53. Employee employee = employeeDao.selectByEmpId(1);
    54. System.out.println("employee = " + employee);
    55. */
    56. /*
    57. //调用添加员工的方法
    58. Employee employee = new Employee(null,"tom",300.65,38);
    59. int insert = employeeDao.insert(employee);
    60. System.out.println("insert = " + insert);
    61. */
    62. /*
    63. //调用更新员工信息的方法
    64. Employee employee = new Employee(20009,"tom",656.65,38);
    65. int update = employeeDao.update(employee);
    66. System.out.println("update = " + update);
    67. */
    68. //调用删除的方法
    69. int delete = employeeDao.delete(20009);
    70. System.out.println("delete = " + delete);
    71. }
    72. }

    三、事务

    3.1 事务回顾

    • 数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定!一个事务内所有语句都成功及事务成功,我们可以触发 commit 提交事务来结束事务,更新数据!一个事务内任意一条语句失败,即为事务失败,我们可以触发 rollback 回滚结束事务,数据回到事务之前状态!
    • 一个业务涉及多条修改数据库语句!例如:
      • 经典的转账案例,转账业务(A账户减钱和B账户加钱,要一起成功)
      • 批量删除(涉及多个删除)
      • 批量添加(涉及多个插入)
    • 事务的特性:
      • 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
      • 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
      • 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
      • 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
    • 事务的提交方式:
      • 自动提交:每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚!
      • 手动提交:手动开启事务,添加语句,手动提交或者手动回滚即可!

    3.2 JDBC 中事务实现

    关键代码:

    1. @Test
    2. public void testTransaction(){
    3. BankDaoImpl bankDao = new BankDaoImpl();
    4. Connection connection = null;
    5. //1.获取连接,将连接的事务提交改为手动提交
    6. try {
    7. connection = JDBCUtilV2.getConnection();
    8. connection.setAutoCommit(false);//开启事务,将当前连接的自动提交关闭,改为手动提交!
    9. //2.操作减钱
    10. bankDao.subMoney(1,100);
    11. int i = 10 / 0;
    12. //3.操作加钱
    13. bankDao.addMoney(2,100);
    14. //4.前置的多次dao操作,没有异常,提交事务
    15. connection.commit();
    16. } catch (Exception e) {
    17. try {
    18. connection.rollback();
    19. } catch (Exception ex) {
    20. throw new RuntimeException(ex);
    21. }
    22. } finally {
    23. JDBCUtilV2.release();
    24. }
    25. }

    3.3 JDBC 事务代码实现

    • 准备数据库表
        1. -- 继续在atguigu 的库中创建银行表
        2. CREATE TABLE t_bank(
        3. id int primary key auto_increment comment '账号主键',
        4. account varchar(20) not null unique comment '账号',
        5. money int unsigned comment '金额,不能为负值'
        6. );
        7. insert into t_bank(account, money) VALUES ('zhangsan',1000),('lisi',1000);

    • DAO 接口代码:
    1. package com.atguigu.advanced.senior.dao;
    2. public interface BankDao {
    3. public int addMoney(Integer id, Integer money);
    4. public int subMoney(Integer id, Integer money);
    5. }

    接口实现代码:

    1. package com.atguigu.advanced.senior.dao.impl;
    2. import com.atguigu.advanced.senior.dao.BankDao;
    3. import com.atguigu.advanced.senior.dao.BaseDAO;
    4. public class BankDaoImpl extends BaseDAO implements BankDao {
    5. @Override
    6. public int addMoney(Integer id, Integer money) {
    7. try {
    8. String sql = "update t_bank set money = money + ? where id = ?";
    9. return executeUpdate(sql,money,id);
    10. } catch (Exception e) {
    11. throw new RuntimeException(e);
    12. }
    13. }
    14. @Override
    15. public int subMoney(Integer id, Integer money) {
    16. try {
    17. String sql = "update t_bank set money = money - ? where id = ?";
    18. return executeUpdate(sql,money,id);
    19. } catch (Exception e) {
    20. throw new RuntimeException(e);
    21. }
    22. }
    23. }

    【注意】当开启事务后,切记一定要根据代码执行结果来决定是否提交或回滚!否则数据库看不到数据的操作结果!

  • 相关阅读:
    Leetcode.714 买卖股票的最佳时机含手续费
    字节二面,差点没答好
    7. 吴恩达机器学习-PCA
    Pandas进阶修炼120题-第五期(一些补充,101-120题)
    【SA8295P 源码分析 (一)】02 - SA8295P 的 LUN 及 分区表 配置详解
    解决Oracle死锁问题
    JavaSE运算符
    MySQL中索引的基本知识
    1. 获取数据-requests.get()
    redis中怎么用分布式token
  • 原文地址:https://blog.csdn.net/qq_43470538/article/details/139426545