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

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

1、三层架构


一种合理的项目分层理念,好处为可以简化设计、各司其职、更容易扩展内容

三层架构分为:


表示层(UI、WEB):跟用户对接

业务逻辑层(service):书写功能的整体逻辑

数据访问层(dao):对接数据库


1.1、数据访问层


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

1.2、业务逻辑层


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


1.2.1、组成


Service接口:


一张表对应一个Service

Service中存放着与该表相关的所有功能方法

命名与表名相关:PersonService

包:须存放在service包下 com.xxx.service

Service实现类:


一个实现类实现一个service接口

命名为接口名+Impl:PersonServiceImpl

包:须存放在service.impl下 com.xxx.service.impl


1.3、表示层


view:负责跟用户对接

1.3.1、实现


一个功能一个视图类

命名:功能+View

包:须放在view包下 com.xxx.view

内容:调用service+Scanner

1.4、完整实现步骤


书写实体类


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;
    }
}


书写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;
}


书写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;
    }
}


书写Service


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


书写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;
        }
    }
}


书写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、事务基本操作与问题解决


开启事务:Connection对象.setAutoCommit(false)

结束事务:

提交:Connection对象.commit();

回滚:Connection对象.rollback();


2.1.1、存在问题


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


2.1.2、解决方案:ThreadLocal


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


实现:


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


使用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、使用步骤


导入jar包:


hamcrest-core-1.3.jar


junit-4.12.jar


创建测试类


命名:被测试的类/接口+Test

包:须放在test包下

使用


@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项目开发步骤总结


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


导入jar包:


hamcrest-core-1.3.jar

junit-4.12.jar

mysql-connector-java-8.0.23.jar


添加工具类(JDBCUtils最终版)


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


书写实体类


搭建DAO


必须测试

搭建Service


最好也进行测试

书写view(不需要过多关注,实际开发中该层对接的应该是浏览器页面)


项目结构图示:


相关文章
|
6月前
|
Java 数据库连接
JDBC跨平台封装工具类
JDBC跨平台封装工具类
|
21天前
|
SQL druid Java
JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO
JDBC和数据库连接池-两个工具类-JDBCUtilsByDruid和BasicDAO
19 0
|
3月前
|
SQL Java 关系型数据库
探索Java数据库连接的奥秘:JDBC技术全攻略
探索Java数据库连接的奥秘:JDBC技术全攻略
53 8
|
5月前
|
Java 数据库连接 API
后端开发之用Mybatis简化JDBC的开发快速入门2024及数据库连接池技术和lombok工具详解
后端开发之用Mybatis简化JDBC的开发快速入门2024及数据库连接池技术和lombok工具详解
61 3
|
5月前
|
存储 设计模式 搜索推荐
早期javeweb技术 JSP JDBC JSTJ Servlet BooStrap(下)
早期javeweb技术 JSP JDBC JSTJ Servlet BooStrap(下)
31 1
|
5月前
|
Java 测试技术
Java一分钟之-JUnit测试框架:断言与测试套件
【6月更文挑战第3天】本文介绍了JUnit在Java单元测试中的应用,包括断言基础如`assertEquals`、`assertTrue`等,用于验证代码预期结果;利用`@Suite`创建测试套件以组合多个测试;并讨论了常见问题及解决方法,如忽略测试、错误断言、异常处理和保持测试简洁。理解并熟练运用这些概念能提升测试代码的质量和效率。
87 2
|
5月前
|
SQL Java 数据库连接
老程序员分享:JDBC工具类封装、封装DAO方法与数据库表实体类封装的使用案列详情
老程序员分享:JDBC工具类封装、封装DAO方法与数据库表实体类封装的使用案列详情
|
5月前
|
测试技术
技术心得记录:复利计算器4.0之再遇JUnit
技术心得记录:复利计算器4.0之再遇JUnit
20 0
|
6月前
|
IDE Java 测试技术
Java中JUnit等测试框架的使用
Java中JUnit等测试框架的使用
|
5月前
|
XML 前端开发 Java
早期javeweb技术 JSP JDBC JSTJ Servlet BooStrap(上)
早期javeweb技术 JSP JDBC JSTJ Servlet BooStrap(上)
29 0