栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JDBC三层架构

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDBC三层架构

1.Dao层抽取

Dao层:数据访问层,用于做与数据库交互的事情
        如果不将dao层抽取,例如登录功能,所有代码都放到测试类中,使得测试类代码特别臃肿
抽取后,代码分离设计:
        main方法中做业务逻辑操作; dao层只做jdbc操作

==============案例: Person数据表的增删改查操作=============


//在测试类中,进行业务逻辑操作
//操作步骤:
//1.创建数据表
//2.新建实体类
//3.编写工具类
//4.将jdbc抽象到dao层
//5.在测试类中编写业务逻辑

------------创建实体类------------
//实体类中的属性要与表字段一致--ORM
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private int id;
    private String name;
    private  int age;
    private Date bornDate;
    private  String email;
    private  String address;
}



------------创建测试类------------
public class Test1 {
    public static void main(String[] args) {
        System.out.println("请输入你要进行的操作:1.添加 2.修改 3 删除  4 查所有  5.根据id查");
        PersonDaoImpl personDao = new PersonDaoImpl();
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        switch (num){
            case 1:  //添加传对象,一般主键自增长,id不用管
                int result = personDao.insert(new Person(0,"ls",20,new Date(),"ls@163.com","湖北"));
                System.out.println("插入:"+result);
                break;
            case 2:  //修改传对象,要有修改的主键id,才能修改
                result = personDao.update(new Person(2,"ww",44,new Date(),"ww@163.com","江西"));
                System.out.println("修改:"+result);
                break;
            case 3:
                result = personDao.delete(1);  //删除id
                System.out.println("删除:"+result);
                break;
            case 4:   //查询所有,返回List集合
                List list = personDao.selectAll();
                System.out.println("查所有:"+list);
                break;
            case 5:  //查询单个,往往根据id查,返回实体对象
                Person person = personDao.selectById(2);
                System.out.println("查对象:"+person);
                break;
            default:
                System.out.println("您的输入有误~");
                break;
        }
    }
}


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

    public int insert(Person person) {
        Connection conn = null;
        PreparedStatement prst = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("insert into person(name,age,bornDate,email,address) values(?,?,?,?,?)");
            prst.setString(1,person.getName());
            prst.setInt(2,person.getAge());
            //sql包下的new Date(参数)可以long类型的毫秒值参数
            prst.setDate(3,new Date(person.getBornDate().getTime()));
            prst.setString(4,person.getEmail());
            prst.setString(5,person.getAddress());
            return prst.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(prst,conn);
        }
        return 0;
    }

    public int update(Person person) {
        Connection conn = null;
        PreparedStatement prst = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("update person set name=?,age=? where id=?");
            prst.setString(1,person.getName());
            prst.setInt(2,person.getAge());
            prst.setInt(3,person.getId());
            return prst.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(prst,conn);
        }
        return 0;
    }

    public int delete(int id) {
        Connection conn = null;
        PreparedStatement prst = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("delete from person where id=?");
            prst.setInt(1,id);
            return prst.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(prst,conn);
        }
        return 0;
    }

    public Person selectById(int id) {
        Connection conn = null;
        PreparedStatement prst = null;
        ResultSet rs = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("select * from person where id=?");
            prst.setInt(1,id);
            rs = prst.executeQuery();
            if(rs.next()){
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Date bornDate = rs.getDate("bornDate");
                String email = rs.getString("email");
                String address = rs.getString("address");
                return new Person(id,name,age,bornDate,email,address);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(rs,prst,conn);
        }
        return null;
    }

    public List selectAll() {
        Connection conn = null;
        PreparedStatement prst = null;
        ResultSet rs = null;
        List list = new ArrayList<>();
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("select * from person");
            rs = prst.executeQuery();
            while(rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                Date bornDate = rs.getDate("bornDate");
                String email = rs.getString("email");
                String address = rs.getString("address");
                list.add(new Person(id,name,age,bornDate,email,address));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(rs,prst,conn);
        }
        return list;
    }
}
2.Service业务层

用于进行业务逻辑分析,一个业务功能可以包含一个或多个dao操作

例如:转账业务-->细化分析
      一个业务-->多个dao,判断发送方账户,接收方账户正确性,钱是否足够
      最后才能做转账功能--一个账户加钱,另一个用户减钱
      以上都是业务分析范畴 

===============案例:转账业务功能分解=================

//操作步骤:
//1.创建数据表,插入两条数据
//2.创建实体类Account
//3.创建DBUtils数据库工具类
//4.创建业务层,进行转账业务分析
//5.具体与数据库交互,交给dao层
//6.在测试类中进行测试


---------创建实体类---------
//创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private int id;
    private  String card;
    private  String password;
    private  double money;
}


----------业务逻辑分析-----------
//转账业务类
public class AccountServiceImpl {
    private  AccountDaoImpl accountDao = new AccountDaoImpl();
    //转账的业务功能
    public String tansfer(String sendCard,double money,String recvCard,String password){
        //1.根据发送方账户,获取对象,如果没有则报异常
        Account sendAccount = accountDao.selectCard(sendCard);
        if(sendAccount==null){
            throw new RuntimeException("发送方账户不存在!");
        }
        //2.如果有,拿到了对象,看看密码是否正确,金额够不够
        if(!password.equals(sendAccount.getPassword())){
            throw  new RuntimeException("密码错误");
        }
        if(sendAccount.getMoney()发送方减钱(update),接收方加钱(update)
        sendAccount.setMoney(sendAccount.getMoney()-money);
        int result = accountDao.update(sendAccount);
        System.out.println("发送方修改:"+result);

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

        return "转账成功~~!";
    }
}


----------数据访问层----------
public class AccountDaoImpl {
    public Account selectCard(String card) {
        Connection conn = null;
        PreparedStatement prst = null;
        ResultSet rs = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("select * from account where card=?");
            prst.setString(1,card);
            rs = prst.executeQuery();
            if(rs.next()){
                int id = rs.getInt("id");
                String password = rs.getString("password");
                double money = rs.getDouble("money");
                return new Account(id,card,password,money);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(rs,prst,conn);
        }
        return null;
    }

    public int update(Account recvAccount) {
        Connection conn = null;
        PreparedStatement prst = null;
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement("update account set money=? where id=?");
            prst.setDouble(1,recvAccount.getMoney());
            prst.setInt(2,recvAccount.getId());
            return prst.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(prst,conn);
        }
        return 0;
    }
}


-------------测试类-------------
public class Test1 {
    public static void main(String[] args) {
        AccountServiceImpl accountService = new AccountServiceImpl();
        String res = accountService.tansfer("10010",1000,"10086","123");
        System.out.println(res);
    }
}
3.事务

案例: 转账功能

发送方减钱
        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 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 "转账成功~~!";
4.三层架构

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

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

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

完整的三层架构的操作步骤:

  • 创建实体类
  • DBUtils的工具类
  • dao层的接口与实现类
  • service层的接口与实现类
  • 通过测试类进行测试,具体结果反馈,返回到测试类中

注:此处加了接口与实现类,方便后续的扩展与维护

//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  List commonQuery(String sql,Class clazz,Object...objs){
        Connection conn = null;
        PreparedStatement prst = null;
        ResultSet rs = null;
        List list = new ArrayList<>();
        try {
            conn = DBUtils.getConnection();
            prst = conn.prepareStatement(sql);
            for(int i=0;i已知属性名,只要属性名和字段名匹配,则可以注入值
                //获取属性名的数组:
                //循环遍历取出属性名,并通过属性名充当字段,得到字段内容
                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 list = DaoUtils.commonQuery(sql,Person.class,id);
        if(list.size()>0){
            return list.get(0);
        }
        return null;
    }

    public List selectAll() {
        String sql = "select * from person";
        return DaoUtils.commonQuery(sql,Person.class);
    }
}
public class UserTest {
    public static void main(String[] args) {
        //面向接口编程:
        UserService userService = new UserServiceImpl();
        List list = userService.selectAll(); //查询所有
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/658748.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号