概要
三层架构
三层架构分为:数据(dao)层、 业务(service)层、控制(controller)层
(1)表示层(USL,即User Show Layer):视图层
a. 前台:对应于MVC中的View,用于和用户交互、界面的显示。
b. 对应于MVC中的Controller,用于控制跳转、调用业务逻辑层。
(2)业务逻辑层(BLL,即Business Logic Layer):Service层
a. 接收表示层的请求及调用
b. 组装数据访问层
(3)数据访问层(DAL,即Data Access Layer):Dao层
直接访问数据库的操作
整体架构流程
代码顺序:
1.数据库表
2.实体层
3.dao层、daoimpl
4.service层、serviceimpl
5.controller层
就像搭房子,又下往上,像领导向下调用。controller层调用service层,再由service调用dao层。
技术名词解释
三层架构来源于后端开发的一种分层的思想。
引用自百科的解释:
三层架构(3-tier architecture)通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。
区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。
各层的作用如下:
表示层:主要对用户的请求接受,以及数据的返回,为客户端提供应用程序的访问。
业务逻辑层:主要负责对数据层的操作。也就是说把一些数据层的操作进行组合。
数据访问层:主要看数据层里面有没有包含逻辑处理,实际上它的各个函数主要完成各个对数据文件的操作。而不必管其他操作。
技术细节
一.案例(JSP连接MySQL数据库并实现增删改查)
1.代码目录
2.应用的MySQL数据库中的表(包含username和password,后面也只会用到这两个):
3.代码介绍
(1)先要写的是pojo包中的Dept类和Emp类,最后写User类
package com.hp.pojo; public class Dept { private int did; private String dname; private String dlocation; private String leader; public static void main(String[] args) { System.out.println(new Dept()); } @Override public String toString() { return "Dept{" + "did=" + did + ", dname='" + dname + '\'' + ", dlocation='" + dlocation + '\'' + ", leader='" + leader + '\'' + '}'; } //有参构造 public Dept(int did, String dname, String dlocation, String leader) { this.did = did; this.dname = dname; this.dlocation = dlocation; this.leader = leader; } //无参构造 public Dept() { } //封装 public int getDid() { return did; } public void setDid(int did) { this.did = did; } public String getDname() { return dname; } public void setDname(String dname) { this.dname = dname; } public String getDlocation() { return dlocation; } public void setDlocation(String dlocation) { this.dlocation = dlocation; } public String getLeader() { return leader; } public void setLeader(String leader) { this.leader = leader; } }
package com.hp.pojo; import java.util.Date; public class Emp { private String id; private String name; private String sex; private Date birth; private double salary; private int deptId; public Emp(String id, String name, String sex, Date birth, double salary, int deptId) { this.id = id; this.name = name; this.sex = sex; this.birth = birth; this.salary = salary; this.deptId = deptId; } public Emp() { } @Override public String toString() { return "Emp{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", birth=" + birth + ", salary=" + salary + ", deptId=" + deptId + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public int getDeptId() { return deptId; } public void setDeptId(int deptId) { this.deptId = deptId; } }
(2)接下来进行dao层的增删改查操作(这里再附带JdbcUtil,即对jdbc的封装)
package com.hp.dao.impl; import com.hp.dao.IDeptDao; import com.hp.pojo.Dept; import com.hp.utils.JdbcUtil; import com.hp.utils.JdbcUtilPlus; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.List; /* DeptDaoImpl: Dept 表示操作的实体 Dao 表示执行层/数据访问层/基层 Impl 表示实现类 */ public class DeptDaoImpl implements IDeptDao { @Override public List<Dept> list() { return JdbcUtil.list("select * from t_dept where did=13",Dept.class); } @Override public Dept selectRow(int did) { return JdbcUtilPlus.selectRow("select * from t_Dept where did= ?",Dept.class,did); } }
//查询多行多列 public static <T> List<T> list(String sql,Class<T> c) { //创建一个集合,存放所有对象 List<T> tList = new ArrayList<>(); try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/salary?characterEncoding=utf-8", "root", "root"); Statement statement = conn.createStatement(); //statement执行sql,返回结果集 ResultSet rs = statement.executeQuery(sql); //结果集rs得到结果集元数据 ResultSetMetaData md = rs.getMetaData(); //获取结果集的总列数 int columnCount = md.getColumnCount(); //解析rs(循环打印) while (rs.next()) {//读取结果集的光标向下移动,光标默认在哪一行,列名所在的那一行 //根据每一行的数据,封装成一个实体对象 T t = c.newInstance(); //1.取出某一行的每一列数据,封装到对象t的属性中 for (int i = 1; i <= columnCount; i++) { //通过列的序号,获取每一列的值 Object value = rs.getObject(i); if (value!=null){ //通过列的序号,获取每一列的列名 String columnName = md.getColumnName(i); //因为列名和实体类t中的属性名一致,为每一个属性构造一个反射中的set方法 Field f = c.getDeclaredField(columnName); //赋予私有属性的赋值权限 f.setAccessible(true); //使用反射,把value给到对象t的属性中 f.set(t,value);//理解为:把value赋值给对象t的columnName属性,相当于set方法 } } //把对象存入集合中 tList.add(t); } //关闭资源 statement.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } return tList; } //查询一行 public static <T> T selectRow(String sql,Class<T> c) { try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/salary?characterEncoding=utf-8", "root", "root"); Statement statement = conn.createStatement(); //statement执行sql,返回结果集 ResultSet rs = statement.executeQuery(sql); //结果集rs得到结果集元数据 ResultSetMetaData md = rs.getMetaData(); //获取结果集的总列数 int columnCount = md.getColumnCount(); //解析rs(循环打印) // 根据每一行的数据,封装成一个实体对象 T t=null; if (rs.next()) {//读取结果集的光标向下移动,光标默认在哪一行,列名所在的那一行 t = c.newInstance(); //1.取出某一行的每一列数据,封装到对象t的属性中 for (int i = 1; i <= columnCount; i++) { //通过列的序号,获取每一列的值 Object value = rs.getObject(i); if (value!=null){ //通过列的序号,获取每一列的列名 String columnName = md.getColumnName(i); //因为列名和实体类t中的属性名一致,为每一个属性构造一个反射中的set方法 Field f = c.getDeclaredField(columnName); //赋予私有属性的赋值权限 f.setAccessible(true); //使用反射,把value给到对象t的属性中 f.set(t,value);//理解为:把value赋值给对象t的columnName属性,相当于set方法 } } } //关闭资源 statement.close(); conn.close(); return t; } catch (Exception e) { e.printStackTrace(); } return null; } //查询一列 public static <T> List<T> selectColumn(String sql,Class<T> c) { //创建一个集合,存放所有对象 List<T> tList = new ArrayList<>(); try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/salary?characterEncoding=utf-8", "root", "root"); Statement statement = conn.createStatement(); //statement执行sql,返回结果集 ResultSet rs = statement.executeQuery(sql); //结果集rs得到结果集元数据 ResultSetMetaData md = rs.getMetaData(); //获取结果集的总列数 int columnCount = md.getColumnCount(); //解析rs(循环打印) while (rs.next()) {//读取结果集的光标向下移动,光标默认在哪一行,列名所在的那一行 //通过列的序号,获取每一列的值 T t = (T) rs.getObject(1); //把对象存入集合中 tList.add(t); } //关闭资源 statement.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } return tList; } //查询单个 public static <T> T selectOne(String sql,Class<T> c) { try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/salary?characterEncoding=utf-8", "root", "root"); Statement statement = conn.createStatement(); //statement执行sql,返回结果集 ResultSet rs = statement.executeQuery(sql); //结果集rs得到结果集元数据 ResultSetMetaData md = rs.getMetaData(); //获取结果集的总列数 int columnCount = md.getColumnCount(); //解析rs(循环打印) T t=null; if (rs.next()){ t = (T) rs.getObject(1); } //关闭资源 statement.close(); conn.close(); return t; } catch (Exception e) { e.printStackTrace(); } return null; } //增 删 改 public static int update(String sql) { try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/salary?characterEncoding=utf-8", "root", "root"); Statement statement = conn.createStatement(); //statement执行sql,返回结果集 int i = statement.executeUpdate(sql); //关闭资源 statement.close(); conn.close(); return i; } catch (Exception e) { e.printStackTrace(); } return 0; }
上述难度跳动比较大,涉及到反射、泛类,而且这还是没有升级后的代码,存在sql注入的问题。我们后续还需要改进代码,让代码变得更加安全简洁。需要帮助理解的可以留言私信我!我们接着往下面看
(3)是逻辑性的service包啦
package com.hp.service.impl; import com.hp.dao.IDeptDao; import com.hp.dao.impl.DeptDaoImpl; import com.hp.pojo.Dept; import com.hp.service.IDeptService; import java.util.List; /* DeptServiceImpl: Dept 操作的实体类,或者操作某个表 Service 表示业务层 Impl 表示实现类*/ public class DeptServiceImpl implements IDeptService { //引入dao层 private IDeptDao deptDao=new DeptDaoImpl(); @Override public int add() { return 0; } @Override public List<Dept> list() { return deptDao.list(); } @Override public Dept selectRow(int did) { return deptDao.selectRow(did); } }
因为我们都已经把实现类写出来了,所以说它的接口类很容易就能写出,这里就不一一给大家说明了
(4)最后我想给大家实现一个入口测试类,用来判断所写的功能是否实现
@Test public void testRow(){ Dept dept = selectRow("select * from t_Dept where did=6", Dept.class); System.out.println(dept); } @Test public void testColumn(){ List<String> list = selectColumn("select dname from t_Dept", String.class); System.out.println(list); } @Test public void testOne(){ String dname = selectOne("select dname from t_Dept where did=1", String.class); System.out.println(dname); } @Test public void testUpdate(){ int j = update("insert into t_Dept values (null,'a','b','c')"); System.out.println(j); } @Test//测试登录 public void testLogin(){ String username="cc'or'1'='1"; String password="cc'or'1'='1"; User user = selectRow("select * from t_users where username='" + username + "' and password='" + password+"'", User.class); System.out.println(user!=null?"登陆成功":"登录失败"); }
最后一个也是下次讲述的内容之一,这里我留一个悬念,聪明的小伙伴们也看到这里我用了@Test,注意这里要导一个包junit-4.12.jar,需要的可以私信我!有什么问题后续也可以提出来,知无不言言无不尽。
小结
本节课我们大家搭建了三层架构:控制层、业务层、数据访问层,让我们的数据在各个层之间传递,最后我们要简化代码——抽取工具类。从总结来看,我们的核心就是抽取工具类。所以我们最重要的就是理解工具类的方法,在java基础中好好学习泛类,面向对象,反射,这样在后续的java进阶学习中,才能更加如鱼得水,游刃有余!最后我们为什么要用三层架构的设计模式?这一点也困惑我许久。通过老师的讲解和自己的总结得出dao层是基层,而service是中层管理人员,最后controller是高层,一个项目需要每个人发挥自己的能力,这样反映了java编程工作团队合作的重要性。只有我们一层管理着下一层,秩序才不会紊乱,功能模块才会更加稳定。有不同见解的朋友也可以底下留言,也可以私下联系我,让我们遨游在java的海洋里面吧!