第六章 三层结构解释及案例

本文详细介绍了三层架构的概念,包括表示层、业务逻辑层和数据访问层的作用与实现方式,并通过一个具体的学生信息管理系统案例,展示了如何在Java环境中构建三层架构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

6.1 什么是三层

  在实际的开发中,为了更好的解耦合、使开发人员之间的分工明确、提高代码的可重用性等,通常会采用“三层架构”的模式来组织代码。

1) 三层定义:

  所谓“三层”,是指表示层(User Show Layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data Access Layer)。

三层中使用的数据,是通过实体类(即封装数据的JavaBean)来传递的。实体类一般放在entity包下。

2) 各层的简介:

①数据访问层(Data Access Layer):

  也称为持久层,位于三层中的最下层,用于对数据进行处理。该层中的方法一般都是“原子性”的,即每一个方法都不可再分。比如,可以在DAL层中实现数据的增删改查操作,而增、删、改、查四个操作是非常基本的功能,都是不能再拆分的。

  在程序中,DAL一般写在dao包中,包里面的类名也是以”Dao”结尾,如StudentDao.java、DepartmentDao.java等;换句话说,在程序中,DAL是由dao包中的多个“类名Dao.java”组成。每一个“类名Dao.java”类,就包含着对该“类名”的所有对象的数据操作,如StudentDao.java中包含对Student对象的增、删、改、查等数据操作,DepartmentDao.java中包含对Department对象的增、删、改、查等数据操作。

②业务逻辑层(Business Login Layer):

  位于三层中的中间层(表示层与数据访问层中间),起到了数据交换中承上启下的作用,用于对业务逻辑的封装。业务逻辑层的设计对于一个支持可扩展的架构尤为关键,因为它扮演了两个不同的角色。业务逻辑层沟通了表示层和数据访问层。

  使用上,就是对数据访问层中的方法进行“组装”。比如,该层也可以实现对Student的增删改查,但与数据访问层不同的是,业务逻辑层中的增、删、改、查不再是 “原子性”的功能,而是包含了一定的业务逻辑。比如该层中的“删”不再像数据访问层中那样仅仅实现“删”,而是在“删”之前要进行业务逻辑的判断:先查找该学生是否存在(即先执行业务逻辑层的“查”),如果存在才会真正的“删”(再执行数据访问层的“删”),如果不存在则应该提示错误信息。即业务逻辑层中的“删”,应该是“带逻辑的删”(即先“查”后“删”),也就是对数据访问层中的“查”和“删”两个方法进行了“组装”。

  在程序中,业务逻辑层一般写在service包中,包里面的类名也是以”Service”结尾,如StudentService.java、DepartmentService.java等。换句话说,在程序中,业务逻辑层是由service包中的多个“类名Service.java”组成。每一个“类名Service.java”类,就包含着对该“类名”的对象的业务操作,如StudenService.java中包含对Student对象的“带逻辑的删”、“带逻辑的增”等业务逻辑操作,DepartmentService.java中包含对所有Department对象的“带逻辑的删”、“带逻辑的增”等业务逻辑操作。

③表示层(User Show Layer):

  位于三层中的最上层,用于显示数据和接收用户输入的数据,为用户提供一种交互式操作的界面。表示层又分为“表示层前台代码”和“表示层后台代码”,其中“表示层前台代码”是指用户能直接访问到的界面,一般是程序的外观(如html文件、JSP文件等),类似于MVC模式中的“视图”;“表示层后台代码”是指用来调用业务逻辑层的JAVA代码(如Servlet),类似于MVC模式中的“控制器”。表示层前台代码一般放在WebContent目录下,而表示层后台代码目前放在servlet包下。

MVC和三层架构,是分别从两个不同的角度去设计的,但目的都是为了“解耦,分层,代码复用等”。

6.2 三层结构案例

项目的目录结构如下:
项目的目录结构

第一步:写操作数据库的工具类

①工具类建在com.lee.util下:

DBUtil.java

package com.lee.util;
public class DBUtil {
	private static String DRIVER="";
	private static String URL="";
	private static  String USER="";
	private static  String PASSWORD="";
	private static Connection connection = null;
	private static PreparedStatement pstmt=null;
	private static ResultSet resultSet=null;
	/**
	 * 获取连接
	 * @return
	 */
	public static Connection getConnection() {
		//加载数据库配置文件 
		try {
			String path = DBUtil.class.getClassLoader().getResource("db.properties").toString().substring(5);
			Properties properties = new Properties();
			FileInputStream inputStream = new FileInputStream(path);
			properties.load(inputStream);
			DRIVER=properties.getProperty("driver");
			URL=properties.getProperty("url");
			USER=properties.getProperty("user");
			PASSWORD=properties.getProperty("password");
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		//记载驱动类
		try {
			Class.forName(DRIVER);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//获取连接
		try {
			connection = DriverManager.getConnection(URL,USER,PASSWORD);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}
	
	/**
	 * 执行insert语句
	 * @param sql:增加的SQL语句
	 * @param os:传递实体中的属性
	 * @return
	 */
	public static boolean executeInsert(String sql,Object[] os) {
		boolean flag = false;
		try {
			pstmt = getConnection().prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (os!=null) {
			for(int i=0;i<os.length;i++) {
				try {
					pstmt.setObject(i+1,os[i]);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					flag=false;
				}
			}
			try {
				pstmt.executeUpdate();
				flag=true;
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				flag=false;
			}finally {
				releaseResource();
			}
		}
		return flag;
	}
	
	/**
	 * 执行delete语句
	 * @param sql
	 * @param os
	 * @return
	 */
	public static boolean executeDelete(String sql ,Object[] os) {
		boolean flag = false;
		//获取PreparedStatement对象
		try {
			pstmt = getConnection().prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (os!=null) {
			//赋值
			for (int i = 0; i < os.length; i++) {
				try {
					pstmt.setObject(i + 1, os[i]);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} 
		}
		//执行
		try {
			pstmt.executeUpdate();
			flag = true;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			flag=false;
		}finally {
			releaseResource();
		}
		return flag;
	}
	/**
	 * 执行update语句
	 * @param sql
	 * @param os
	 * @return
	 */
	public static boolean executeUpdate(String sql ,Object[] os) {
		boolean flag = false;
		try {
			pstmt = getConnection().prepareStatement(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		for(int i=0;i<os.length;i++) {
			try {
				pstmt.setObject(i+1,os[i]);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			pstmt.executeUpdate();
			flag=true;
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			flag=false;
		}finally {
			releaseResource();
		}
		return flag;
	}
	
	/**
	 * 执行select语句
	 * @param sql
	 * @param os
	 * @return
	 */
	public static ResultSet executeSelect(String sql ,Object[] os) {
		try {
			pstmt = getConnection().prepareStatement(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		if (os!=null) {
			for (int i = 0; i < os.length; i++) {
				try {
					pstmt.setObject(i + 1, os[i]);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} 
		}
		try {
			resultSet = pstmt.executeQuery();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			//releaseResource();注意:这里不要资源释放,否则会提前关闭resultset,导致结果集不能为空
		}
		return resultSet;
	}
	
	public static void releaseResource() {
		if(resultSet !=null) {
			try {
				resultSet.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		if(pstmt !=null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		if(connection!=null) {
			try {
				connection.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

②数据库配置信息:
db.properties

#mysql配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/studentcrud
user=root
password=root

第二步:建表

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `no` int(11) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
--  Records of `student`
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES ('1', 'a', '10', '男'), ('2', 'b', '20', '女'), ('3', 'cc', '33', '女'), ('4', 'dd', '44', '男');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

第三步:写数据访问层(dao)

①dao层接口,写在com.lee.dao下

IStudentDao.java

package com.lee.dao;
public interface IStudentDao {

	public boolean addStudent(Student sutdent);//增加学生
	public boolean deleteStudentByNo(int studentNo);//通过学号删除学生
	public boolean updateStudent(Student student);//更新学生信息
	public Student queryStudentByNo(int studentNo);//通过学号查询学生信息
	public List<Student> queryAllStudent();//查询所有学生
	public boolean isExitByNo(int studentNo);//通过学号判断学生是否存在
}

②dao层接口实现类,写在com.lee.dao.impl下

StudentDaoImpl.java

package com.lee.dao.impl;
public class StudentDaoImpl implements IStudentDao{

	@Override
	public boolean addStudent(Student student) {
		String sql = "insert into student(no,name,age,sex) values(?,?,?,?) ";
		Object[] os = {student.getStuNo(),student.getStuName(),student.getStuAge(),student.getStuSex()};
		return DBUtil.executeInsert(sql, os);
	}

	@Override
	public boolean deleteStudentByNo(int studentNo) {
		String sql ="delete from student where no=?";
		Object[] os = {studentNo};
		return DBUtil.executeDelete(sql, os);
	}

	@Override
	public boolean updateStudent(Student student) {
		String sql ="update student set no=?,name=?,age=?,sex=? where no=? ";
		Object[] os = {student.getStuNo(),student.getStuName(),student.getStuAge(),student.getStuSex(),student.getStuNo()};
		return DBUtil.executeUpdate(sql, os);
	}

	@Override
	public Student queryStudentByNo(int studentNo) {
		String sql ="select no,age,name,sex from student where no=?";
		Object[] os = {studentNo};
		Student student = null;
		ResultSet rs = DBUtil.executeSelect(sql, os);
		
		try {
			
			if(rs.next()) {
				int stuNo =rs.getInt("no");
				String stuName = rs.getString("name");
				int stuAge = rs.getInt("age");
				String stuSex = rs.getString("sex");
				student = new Student(stuNo, stuName, stuAge, stuSex);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return student;
	}

	@Override
	public List<Student> queryAllStudent() {
		String sql ="select  no,name,age,sex from student";
		Object[] os= {};
		Student student = null;
		ArrayList<Student> students = new ArrayList<>();
		ResultSet rs = DBUtil.executeSelect(sql, os);
		try {
			while(rs.next()) {
				int stuNo =rs.getInt("no");
				String stuName = rs.getString("name");
				int stuAge = rs.getInt("age");
				String stuSex = rs.getString("sex");
				student = new Student(stuNo, stuName, stuAge, stuSex);
				students.add(student);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return students;
	}

	@Override
	public boolean isExitByNo(int studentNo) {
		Student studentByNo = this.queryStudentByNo(studentNo);
		if(studentByNo!=null) {
			return true;
		}
		return false;
	}
}

第四步:写业务逻辑层(service)

①service层接口,写在com.lee.service下

IStudentService.java

package com.lee.service;
public interface IStudentService {

	public boolean addStudent(Student student);//增加学生
	public boolean deleteStudentByNo(int studentNo);//根据学号删除学生
	public boolean updateStudent(Student student);//更新学生信息
	public Student queryStudentByNo(int studentNo);//根据学号查询学生
	public List<Student> queryAllStudent();//查询所有学生
	public boolean isExitStudent(int studentNo);//根据学号查询学生是否存在
}

②service层接口实现类,写在com.lee.service.impl下

StudentServiceImpl.java

package com.lee.service.impl;
public class StudentServiceImpl implements IStudentService {

	public IStudentDao studentDao =new StudentDaoImpl();
	
	@Override
	public boolean addStudent(Student student) {
		boolean isExit = studentDao.isExitByNo(student.getStuNo());
		if(isExit) {//如果学生存在
			return false;
		}
		return studentDao.addStudent(student);
	}

	@Override
	public boolean deleteStudentByNo(int studentNo) {
		boolean isExit = studentDao.isExitByNo(studentNo);
		if(isExit) {//如果学生存在
			return studentDao.deleteStudentByNo(studentNo);
		}
		return false;
	}

	@Override
	public boolean updateStudent(Student student) {
		boolean isExit = studentDao.isExitByNo(student.getStuNo());
		if(isExit) {//如果学生存在
			return studentDao.updateStudent(student);
		}
		return false;
	}

	@Override
	public Student queryStudentByNo(int studentNo) {
		return studentDao.queryStudentByNo(studentNo);
	}

	@Override
	public List<Student> queryAllStudent() {
		return studentDao.queryAllStudent();
	}

	@Override
	public boolean isExitStudent(int studentNo) {
		return studentDao.isExitByNo(studentNo);
	}

}

第五步:写视图层的后端部分(controller)

①添加学生的servlet

AddStudentServlet.java

package com.lee.servlet;
@WebServlet("/AddStudentServlet")
public class AddStudentServlet extends HttpServlet {

	private static final long serialVersionUID = -9067988240490709448L;
	private IStudentService studentService = new StudentServiceImpl();
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setCharacterEncoding("UTF-8");
		req.setCharacterEncoding("UTF-8");
		Integer sno = Integer.parseInt(req.getParameter("sno"));
		String sname = req.getParameter("sname");
		Integer sage = Integer.parseInt(req.getParameter("sage"));
		String ssex = req.getParameter("ssex");
		Student student = new Student(sno, sname, sage, ssex);
		boolean result = studentService.addStudent(student);
		if(!result) {//如果添加失败
			req.setAttribute("error", "添加失败!");
			req.getRequestDispatcher("addStudent.jsp").forward(req, resp);
		}else {
			req.setAttribute("success", "添加成功!");
			resp.sendRedirect("QueryAllStudentServlet");
		}
		
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doGet(req, resp);
	}
}

②删除学生的servlet

DeleteStudentServlet.java

package com.lee.servlet;
@WebServlet("/DeleteStudentServlet")
public class DeleteStudentServlet extends HttpServlet {

	private static final long serialVersionUID = -5246898354271104648L;
	private IStudentService studentService = new StudentServiceImpl();
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		Integer sno = Integer.parseInt(req.getParameter("sno"));
		boolean result = studentService.deleteStudentByNo(sno);
		if(!result) {//如果删除失败
			req.setAttribute("error", "删除失败!");
			req.getRequestDispatcher("index.jsp").forward(req, resp);;
		}else {
			req.setAttribute("success", "删除成功!");
			resp.sendRedirect("QueryAllStudentServlet");
		}
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
}

③查询所有学生的servlet

QueryAllStudentServlet.java

package com.lee.servlet;
@WebServlet("/QueryAllStudentServlet")
public class QueryAllStudentServlet extends HttpServlet {

	private static final long serialVersionUID = -5246898354271104648L;
	private IStudentService studentService = new StudentServiceImpl();
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		List<Student> students = studentService.queryAllStudent();
		if(students!=null) {//如果查询失败
			req.setAttribute("students", students);
			req.getRequestDispatcher("index.jsp").forward(req, resp);;
		}else {
			req.setAttribute("error", "查询失败!");
		}
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}	
}

④通过学号查询学生

QueryStudentByNOServlet.java

package com.lee.servlet;
@WebServlet("/QueryStudentByNOServlet")
public class QueryStudentByNOServlet extends HttpServlet {

	private static final long serialVersionUID = -5246898354271104648L;
	private IStudentService studentService = new StudentServiceImpl();
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		Integer sno = Integer.parseInt(req.getParameter("sno"));
		Student result = studentService.queryStudentByNo(sno);
		if(result!=null) {//如果查询失败
			req.setAttribute("student", result);
		}else {
			req.setAttribute("error", "查询失败!");
		}
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
}

⑤通过学号更新学生信息

UpdateStudentServlet.java

package com.lee.servlet;
@WebServlet("/UpdateStudentServlet")
public class UpdateStudentServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private IStudentService studentService = new StudentServiceImpl();
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		Integer sno = Integer.parseInt(req.getParameter("sno"));
		String sname = req.getParameter("sname");
		Integer sage = Integer.parseInt(req.getParameter("sage"));
		String ssex = req.getParameter("ssex");
		Student student = new Student(sno, sname, sage, ssex);
		boolean result = studentService.updateStudent(student);
		if(!result) {//如果更新失败
			req.setAttribute("error", "更新失败!");
			req.getRequestDispatcher("error.jsp").forward(req, resp);
		}else {
			req.setAttribute("success", "更新成功!");
			resp.sendRedirect("QueryAllStudentServlet");
		}
	}
}

第六步:写视图层的前端部分(jsp、html等)

①显示所有学生信息

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ page import="java.util.List" %>
<%@ page import="com.lee.entity.Student" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="./js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="./js/bootstrap.min.js"></script>
<link href="./css/bootstrap.min.css" rel="stylesheet">
<title>首页</title>
</head>
<body>
<table class="table table-striped table-hover">

<tr>
  <td class="success">学号</td>
  <td class="warning">姓名</td>
  <td class="danger">年龄</td>
  <td class="info">性别</td>
  <td class="active">操作</td>
</tr>
<%
			List<Student> students =(List<Student>) request.getAttribute("students") ;
			for(Student stu: students){
%>
	<tr>
		<td><%=stu.getStuNo() %></td>
		<td><%=stu.getStuName() %></td>
		<td><%=stu.getStuAge() %></td>
		<td><%=stu.getStuSex() %></td>
		<td>
			<a  class="btn btn-primary"  href="update.jsp?sno=<%=stu.getStuNo() %>&sname=<%=stu.getStuName() %>&sage=<%=stu.getStuAge()%>&ssex=<%=stu.getStuSex()%>">修改</a>
			<a  class="btn btn-danger" href="DeleteStudentServlet?sno=<%=stu.getStuNo()%>">删除</a>
	   </td>
	</tr>
<%
			}
%>
</table>
<a href="add.jsp">添加</a>
</body>
</html>

注意:引用bootstrap的时候,要先引入jQuery。

②添加学生信息

add.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>添加学生</title>
</head>
<body>
<form action="AddStudentServlet" method="post">
	学号:<input type="text"  name="sno"></br>
	姓名:<input type="text"  name="sname"></br>
	年龄:<input type="text"  name="sage"></br>
	性别:<input type="text"  name="ssex"></br>
	<input type="submit" value="添加">
</form>
</body>
</html>

③修改学生的信息

update.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>修改学生信息</title>
</head>
<body>
	<form action="UpdateStudentServlet" method="post">
		学号:<input type="text" name="sno" value=<%=request.getParameter("sno") %>></br>
		姓名:<input type="text" name="sname" value=<%=request.getParameter("sname") %>></br>
		年龄:<input type="text" name="sage" value=<%=request.getParameter("sage") %>></br>
		性别:<input type="text" name="ssex" value=<%=request.getParameter("ssex") %>></br>
		<input type="submit" value="修改">
	</form>
	
</body>
</html>

④错误页面

error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>错误页面</title>
</head>
<body>
<%
		out.print(request.getAttribute("error"));
%>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值