当前位置:网站首页>JDBC入门学习(三)之事务回滚功能的实现

JDBC入门学习(三)之事务回滚功能的实现

2022-06-23 03:55:00 默主归沙

  1. 事务(重点)
    案例: 转账功能
   //发送方减钱
int result = accountDao.update(sendAccount);

//完成一个功能后,后面的接收方操作无法完成,数据出现问题,如何处理

//接收方加钱
result = accountDao.update(recvAccount);

事务:将转账功能的多条SQL语句放入事务中,要么都成功,则提交;要么都失败,则回滚

事务如何加?在哪个层次加,为什么?
service层,如果只完成一个SQL语句,则回滚;都完成了则提交;例如

   try{
    
      //开启事务
   //发送方减钱
int result = accountDao.update(sendAccount);

//异常

//接收方加钱
result = accountDao.update(recvAccount);
//提交事务
}catch(Exception e){
    
   //回滚
}

问题1:出现异常后,无法进行回滚
原因:处理事务的连接对象和SQL操作的连接对象不是同一个
解决方案:
1.将事务处理的连接对象传入到dao层,共SQL操作使用–不推荐,容易造成接口污染
2.通过ThreadLocal来确保service和dao层操作同一对象–推荐

ThreadLocal的用法类似Map集合,通过键值对存储;
可以在service和dao层中操作同一个共享值,这个共享值就是连接对象

问题2:执行完一个SQL,关闭了连接对象;再次执行SQL会报错
解决方案:统一在提交或回滚事务时,才关闭连接对象

//==============DBUtils工具类==============
public class DBUtils {
    
    private static Properties p = new Properties();
    private static  ThreadLocal<Connection> th = new ThreadLocal<>();
    //静态代码块:只加载一次
    static{
    
        //反射对象调用getResourceAsStream
        //从src目录下获取到db.properties的资源
        try {
    
            InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
            p.load(is);
            Class.forName(p.getProperty("driver"));
        } catch (Exception e) {
    
            e.printStackTrace();
        }
    }

    public static Connection getConnection(){
    
        Connection conn = null;
        try {
    
            conn = th.get();  //从ThreadLocal中获取连接对象
            if(conn==null){
    
                conn = DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password"));
                th.set(conn); //将连接对象存储到ThreadLocal
            }
        } catch (SQLException e) {
    
            e.printStackTrace();
        }
        return conn;
    }

    public static void begin() throws SQLException {
    
        Connection conn = getConnection();
        conn.setAutoCommit(false);  //开启事务
    }

    public static void commit() throws SQLException {
    
        Connection conn = getConnection();
        conn.commit();
        closeAll(conn);  //关闭资源
    }

    public static void rollback(){
    
        Connection conn = getConnection();
        try {
    
            conn.rollback();
            closeAll(conn);  //关闭资源
        } catch (SQLException e) {
    
            e.printStackTrace();
        }
    }

    public static void closeAll(AutoCloseable...cs){
    
        for(AutoCloseable c : cs){
    
            if(c!=null){
    
                try {
    
                    c.close();
                } catch (Exception e) {
    
                    e.printStackTrace();
                }
            }
        }
        th.remove();  //移除th对象
    }
}
//==========转账业务层代码=========
try {
    
    //开启事务
    DBUtils.begin();  //开启事务
    sendAccount.setMoney(sendAccount.getMoney()-money);
    int result = accountDao.update(sendAccount);
    System.out.println("发送方修改:"+result);

    //int i=1/0; //模拟异常

    recvAccount.setMoney(recvAccount.getMoney()+money);
    result = accountDao.update(recvAccount);
    System.out.println("发送方修改:"+result);

    DBUtils.commit();  //提交事务
}catch (Exception e){
    
    System.out.println("事务回滚了");
    DBUtils.rollback();  //事务回滚
    return "转账失败~~!";
}

return "转账成功~~!";
  1. 三层架构
    什么是三层架构?
    与数据库操作的功能,划分了三个层次,分别是:表示层(main),业务层,数据访问层
    表示层:
    将准备的参数传给业务层,并接收业务层反馈(main)

业务层:(接口与实现类)
业务逻辑处理,调用dao层方法,并接收返回结果

数据访问层:(接口与实现类)
与数据库的交互操作

在这里插入图片描述

//完整的三层架构的操作步骤:
//1.创建实体类
//2.DBUtils的工具类
//3.dao层的接口与实现类
//4.service层的接口与实现类
//5.通过测试类进行测试,具体结果反馈,返回到测试类中
//注意:此处加了接口与实现类,方便后续的扩展与维护
public class UserTest {
    
    public static void main(String[] args) {
    
        //面向接口编程:
        UserService userService = new UserServiceImpl();
        List<User> list = userService.selectAll(); //查询所有
    }
}

  1. DaoUtils(重点)
    dao层的jdbc操作有太多的冗余代码
    DaoUtils就是针对dao层代码进行封装,这样可以进行增删改查的复用
//----------DaoUtils封装---------
//对dao层的增删改查的jdbc进行优化
public class DaoUtils {
    
    //增删改的封装
    //返回值--int(与数据库返回相关)
    //参数:增删改中编号的数据抽取到参数-sql,传入数据
    public static int commonUpdate(String sql,Object... objs) {
    
        Connection conn = null;
        PreparedStatement prst = null;
        try {
    
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement(sql);
            //有多少个参数,就有多少个Object
            for (int i = 0; i < objs.length; i++) {
    
                prst.setObject(i + 1, objs[i]);
            }
            return prst.executeUpdate();
        } catch (SQLException e) {
    
            e.printStackTrace();
        } finally {
    
            DBUtils.closeAll(prst, conn);
        }
        return 0;
    }

    //DQL的封装:
    //返回值:集合的泛型
    //参数:sql, 反射对象,所有参数
    public static <T> List<T> commonQuery(String sql,Class<T> clazz,Object...objs){
    
        Connection conn = null;
        PreparedStatement prst = null;
        ResultSet rs = null;
        List<T> list = new ArrayList<>();
        try {
    
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement(sql);
            for(int i=0;i<objs.length;i++){
    
                prst.setObject(i+1,objs[i]);
            }
            rs = prst.executeQuery();
            Field[] fields = clazz.getDeclaredFields();
            while(rs.next()){
    
                //如何将数据进行ORM
                //rs.getObject传字段名(未知)-->已知属性名,只要属性名和字段名匹配,则可以注入值
                //获取属性名的数组:
                //循环遍历取出属性名,并通过属性名充当字段,得到字段内容
                T t = clazz.newInstance();
                for(Field f:fields){
      //获取field对象
                    Object value = rs.getObject(f.getName()); //字段名一定要与属性名一致
                    f.setAccessible(true);  //开启权限
                    f.set(t,value);        //赋值
                }
                list.add(t);  //将实体对象存入List
            }
        } catch (Exception e) {
    
            e.printStackTrace();
        }finally {
    
            DBUtils.closeAll(rs,prst,conn);
        }
        return list;
    }

}
//----------PersonDaoImpl---------
//dao层--数据访问层(用于做jdbc操作)
public class PersonDaoImpl {
    

    public int insert(Person p) {
    
        String sql = "insert into person(name,age,bornDate,email,address) values(?,?,?,?,?)";
        return DaoUtils.commonUpdate(sql,p.getName(),p.getAge(),p.getBornDate(),p.getEmail(),p.getAddress());
    }

    public int update(Person p) {
    
        String sql = "update person set name=?,age=? where id=?";
        return  DaoUtils.commonUpdate(sql,p.getName(),p.getAge(),p.getId());
    }

    public int delete(int id) {
    
       String sql = "delete from person where id=?";
       return DaoUtils.commonUpdate(sql,id);
    }

    public Person selectById(int id) {
    
        String sql = "select * from person where id=?";
        List<Person> list = DaoUtils.commonQuery(sql,Person.class,id);
        if(list.size()>0){
    
            return list.get(0);
        }
        return null;
    }

    public List<Person> selectAll() {
    
        String sql = "select * from person";
        return DaoUtils.commonQuery(sql,Person.class);
    }
}



原网站

版权声明
本文为[默主归沙]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_45682261/article/details/125356020