> 文档中心 > 高效掌握JDBC技术(三)| 三层架构理念 | 书写符合事务特性的工具类 | JUnit测试框架 | JDBC项目开发步骤

高效掌握JDBC技术(三)| 三层架构理念 | 书写符合事务特性的工具类 | JUnit测试框架 | JDBC项目开发步骤


✅作者简介:热爱后端语言的大学生,CSDN内容合伙人
✨精品专栏:C++面向对象
🔥系列专栏:JDBC快速入门

文章目录

  • 1、三层架构
    • 1.1、数据访问层
    • 1.2、业务逻辑层
      • 1.2.1、组成
    • 1.3、表示层
      • 1.3.1、实现
    • 1.4、完整实现步骤
  • 2、事务及JDBCUtils最终版
    • 2.1、事务基本操作与问题解决
      • 2.1.1、存在问题
      • 2.1.2、解决方案:ThreadLocal
    • 2.2、JDBCUtils-最终版
  • 3、JUnit测试框架
    • 3.1、使用步骤
    • 3.2、使用示例
  • 4、JDBC项目开发步骤总结

1、三层架构

  • 一种合理的项目分层理念,好处为可以简化设计、各司其职、更容易扩展内容
  • 三层架构分为:
    • 表示层(UI、WEB):跟用户对接
    • 业务逻辑层(service):书写功能的整体逻辑
    • 数据访问层(dao):对接数据库

1.1、数据访问层

  • DAO:和数据库交接、内存放着对数据库内容增删改查的方法

1.2、业务逻辑层

  • Service:存放着代表主要功能的方法,内部内容主要为调用DAO+逻辑控制代码

1.2.1、组成

Service接口:

  1. 一张表对应一个Service
  2. Service中存放着与该表相关的所有功能方法
  3. 命名与表名相关:PersonService
  4. 包:须存放在service包下 com.xxx.service

Service实现类:

  1. 一个实现类实现一个service接口
  2. 命名为接口名+Impl:PersonServiceImpl
  3. 包:须存放在service.impl下 com.xxx.service.impl

1.3、表示层

  • view:负责跟用户对接

1.3.1、实现

  1. 一个功能一个视图类
  2. 命名:功能+View
  3. 包:须放在view包下 com.xxx.view
  4. 内容:调用service+Scanner

1.4、完整实现步骤

  1. 书写实体类

    package com.bz.entity;import java.io.Serializable;public class User implements Serializable {    private Integer id;    private String username;    private String pwd;    @Override    public String toString() { return "User{" +  "id=" + id +  ", username='" + username + '\'' +  ", pwd='" + pwd + '\'' +  '}';    }    public User(Integer id, String username, String pwd) { this.id = id; this.username = username; this.pwd = pwd;    }}
  2. 书写DAO

    package com.bz.dao;import com.bz.entity.User;/** * 跟数据库对接:从数据库中查找user信息 */public interface UserDao {    /**     * 查询用户信息是否存在     * @param username  用户名     * @param pwd 密码     * @return  用户对象     */    User selectUser(String username,String pwd) throws Exception;}
  3. 书写DaoImpl

    package com.bz.dao.impl;import com.bz.dao.UserDao;import com.bz.entity.User;import com.bz.util.JDBCUtils;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;public class UserDaoImpl implements UserDao {    @Override    public User selectUser(String username, String pwd) throws Exception { User user=null;//用来返回 Connection conn= JDBCUtils.getConnection(); String sql = "select * from t_user where u_username=? and u_pwd=?"; PreparedStatement ps=conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2, pwd); ResultSet rs=ps.executeQuery(); if (rs.next()) {     Integer id = rs.getInt("u_id");     String name = rs.getString("u_username");     String password = rs.getString("u_pwd");     user = new User(id, name, password); } JDBCUtils.close(rs,ps,conn); return user;    }}
  4. 书写Service

    package com.bz.service;public interface UserService {    /**     * 用户登录     * @param username  用户输入的账号名     * @param pwd 用户输入的密码     * @return 是否登录成功     */    boolean login(String username,String pwd) throws Exception;}
  5. 书写ServiceImpl

    package com.bz.service.impl;import com.bz.dao.UserDao;import com.bz.dao.impl.UserDaoImpl;import com.bz.service.UserService;public class UserServiceImpl implements UserService {    //创建Dao对象    private UserDao ud=new UserDaoImpl();    @Override    public boolean login(String username, String pwd)throws Exception { if (ud.selectUser(username, pwd)!=null) {     return true; }else{     return false; }    }}
  6. 书写view

    package com.bz.view;import com.bz.service.UserService;import com.bz.service.impl.UserServiceImpl;import java.util.Scanner;/** * 用户登录 */public class UserloginTest {    public static void main(String[] args) throws Exception{ UserService us=new UserServiceImpl(); Scanner sc = new Scanner(System.in); System.out.println("请输入用户名:"); String username=sc.next(); System.out.println("请输入密码:"); String pwd=sc.next(); //调用Service方法判断登录是否成功 if (us.login(username,pwd)){     System.out.println("登录成功!"); }else {     System.out.println("登录失败"); }    }}

2、事务及JDBCUtils最终版

  • 回顾事务概念:将多个操作步骤归为同一个原子操作,要么同时成功,要么同时失败

    开启事务

    执行操作

    结束事务:commit rollback

  • 通常需要添加在Service层,Service层的所有功能方法都应该配套事务

2.1、事务基本操作与问题解决

  1. 开启事务:Connection对象.setAutoCommit(false)
  2. 结束事务:
    • 提交:Connection对象.commit();
    • 回滚:Connection对象.rollback();

2.1.1、存在问题

操作事务和操作数据库数据的数据库连接不是同一个,或导致事务回滚不会影响数据库内容

2.1.2、解决方案:ThreadLocal

  • 思路: 放入线程的存储空间中,ServiceDAO不再自行创建conn,如有需要,直接从线程存储空间中取出

  • 实现:

    1. 确保工具类只会创建一个conn对象

    2. 使用ThreadLocal将工具类创建的conn对象放入存储空间

      ThreadLocal:可以操作线程存储空间的工具,可以对空间的数据进行添加、获取、删除

      添加:ThreadLocal对象.set(数据)

      获取:ThreadLocal对象.get()

      删除:ThreadLocal对象.remove()

  • 使用:

    • 由于DAO和Service共用同一个conn,并且Service一定晚于DAO执行结束,所以为了确保Service的执行,DAO中不能关闭conn,该操作应由Service完成

2.2、JDBCUtils-最终版

package com.bz.util;import java.io.InputStream;import java.sql.*;import java.util.Properties;/** * 工具类:方便方法调用,所有方法都应为静态方法 */public class JDBCUtils {    //提升集合的作用范围,确保getConnection方法中也能使用    private static Properties p=null;    //创建操作线程存储空间的工具对象    private static ThreadLocal<Connection> tl=new ThreadLocal<>();    //把流对象的创建放入静态初始代码块,确保在工具类类加载时执行    static{ try(  //通过类对象.getResourseAsStream()获取一个字节输入流对象  //当前配置文件在src之下  InputStream is=JDBCUtils.class.getResourceAsStream("/jdbc.properties"); ){     //创建用来接收的Properties集合     p=new Properties();     //调用方法加载配置文件的内容至集合中     p.load(is);     //1. 加载驱动     Class.forName(p.getProperty("driverClassName")); }catch (ClassNotFoundException e) {     System.out.println("驱动路径不正确"); } catch (Exception e){     e.printStackTrace(); }    }    /**     * 获取Connection连接     * @return     */    public static Connection getConnection(){ Connection conn =tl.get(); try {     if (conn==null) {//这里如果线程存储空间里没有conn就创建conn并存入线程空间  //2. 获取连接  //连接的url  String url = p.getProperty("url");  //用户名  String username = p.getProperty("username");  //密码  String pwd = p.getProperty("password");  conn = DriverManager.getConnection(url, username, pwd);  //将新创建的conn放入线程的存储空间  tl.set(conn);     } } catch (SQLException e) {     System.out.println("获取连接失败"); } catch (Exception e) {     System.out.println("未知异常");     e.printStackTrace(); } return conn;    }    /**     * 关闭资源连接  非空判断:防止空指针     * @param rs     * @param ps     * @param conn     */    public static void close(ResultSet rs, PreparedStatement ps,Connection conn){ if (rs!=null){     try {  rs.close();     } catch (SQLException e) {  System.out.println("关闭rs失败");     } } if (ps!=null){     try {  ps.close();     } catch (SQLException e) {  System.out.println("关闭ps失败");     } } if (conn!=null){     try {  conn.close();     } catch (SQLException e) {  System.out.println("关闭conn失败");     } }    }}

3、JUnit测试框架

  • 作用:DAO层和Service层中的方法通常需要经过测试JUnit可以通过@Test注解完成执行测试,大大减少测试成本

3.1、使用步骤

  1. 导入jar包:

    • hamcrest-core-1.3.jar

    • junit-4.12.jar

  2. 创建测试类

    • 命名:被测试的类/接口+Test
    • 包:须放在test包下
  3. 使用

    • @Test注解必须写在方法上方
    • 方法必须为公开、非静态、无参、无返回值的最普通的普通方法
    • 一个测试方法中只能测试一个方法
    • 通常情况下,测试方法名应与被测试方法一致,目的更为清晰

3.2、使用示例

package com.bz.test;import com.bz.dao.AccountDao;import com.bz.dao.impl.AccountDaoImpl;import com.bz.entity.Account;import org.junit.Test;public class AccountDaoImplTest {    //创建被测试的对象    AccountDao ad=new AccountDaoImpl();    @Test    public void selectAccountByName(){ Account a = ad.selectAccountByName("张三"); System.out.println(a);    }    @Test    public void updateAccountByName(){ int n = ad.updateAccountByName("张三", 100); System.out.println(n);    }}

4、JDBC项目开发步骤总结

首先要根据要求来建库建表(数据库的内部操作)

  1. 导入jar包:

    hamcrest-core-1.3.jar
    junit-4.12.jar
    mysql-connector-java-8.0.23.jar

  2. 添加工具类(JDBCUtils最终版)

  3. 在src下添加工具类所需的jdbc.properties

  4. 书写实体类

  5. 搭建DAO

    • 必须测试
  6. 搭建Service

    • 最好也进行测试
  7. 书写view(不需要过多关注,实际开发中该层对接的应该是浏览器页面

项目结构图示:

高效掌握JDBC技术(三)| 三层架构理念 | 书写符合事务特性的工具类 | JUnit测试框架 | JDBC项目开发步骤


高效掌握JDBC的分享到此结束,希望对大家有所帮助,如有疑问欢迎大家交流指正。