DAO设计模式详细–实例开发
文章目录
Data Access Object,数据访问对象
javabean是把视图和业务进行分离,dao是把数据库的操作和业务逻辑进行分离.
主要的功能是用于进行数据操作的,在程序的标准开发框架中属于数据层的操作。
![20141207130012401](https://i-blog.csdnimg.cn/blog_migrate/e9607e464aafd4aaffde3b8df33147c4.png)
- 我们需要理解程序分层的思想
- DAO设计模式的组成以及各部分的开发
1.0 程序的分层思想
分层的思想:
我们可以这么理解,实际上分层概念可以应用到任何环境上,例如:公司里可以按照职位分层,每一层都是完全独立的,而且可以和其它层进行完整的交互
我们可以以人类交谈为例子做一个简单分层
- 两个人进行交谈的时候,所有的信息都存储在大脑里面,大脑是作为信息存储的单位存在,
- 我们需要依赖语言或者行为模式组合数据进行交流
- 为了友好交流,我们还需要一些辅助性的外表交流
如果要对程序进行划分, 那么最常见的划分方式:
显示层+控制层+业务层+数据层+数据库
![image 20200813201346403](https://i-blog.csdnimg.cn/blog_migrate/efef415241149a3cbbbd07a7888025e3.png)
![image 20200813225631109](https://i-blog.csdnimg.cn/blog_migrate/b4e274d34e1e44a975d4578447cb556a.png)
在一个项目中,实现的关键是业务层,前台显示同样重要,因为只有一个好的页面才能够让用户印象深刻.我们在DAO设计中主要观察业务层和数据层的开发,到了javaWeb后,才开始实习显示层和控制层的开发
项目的后台建立有着重要地位,不同层之间最为重要的联结组成就是接口,所有在整个项目开发之中,后台代码就一定要有两个组成接口
- 业务接口:以后给控制层使用
- 数据层接口:以后给业务层使用
我们聊了这么多,到底什么是业务层,和数据层那?
- 数据层(数据访问层,Database Access )指的是执行数据库的具体操作,而现在的开发之中,大多数都是针对与数据库的开发,所以在数据库中的主要任务是负责完成数据的CRUD.肯定使用数据库Preparedstatement接口完成
- 业务层(业务对象,Bussiness object,BO) 又或者将其称为Service,服务层,业务层的主要任务是根据业务需求进行数据层的操作,一个业务层要包含多个数据层的操作
![image 20200813224215486](https://i-blog.csdnimg.cn/blog_migrate/f90c9b9135754508f491ff278c31c9d0.png)
一个业务层需要多个数据层的操作共同完成
我们发现数据层里,完成的都是一个一个的原子性的操作
大的业务可能会被细分为小的业务
![image 20200813225801667](https://i-blog.csdnimg.cn/blog_migrate/5a0dc908d89fb56f87bac73a71303bf0.png)
2.0案例分析
现在要求使用emp表(empno,ename,job,hiredate,sal,comm)实现以下操作
- [业务层]增加雇员信息,但是需要保证新添加的雇员编号不会重复
- [数据层]判断我们要增加的雇员编号是否存在
- [数据层]如果雇员编号不存在,则进行数据的保存操作
- [业务层]实现雇员数据的修改操作
- [数据层]执行数据修改操作
- [业务层]实现多个雇员数据的删除操作
- [数据层]执行数据的限定删除
- [业务层]可以根据雇员编号查找到一个雇员的信息
- [数据层]根据雇员编号查询指定的雇员信息
- [业务层]可以查询雇员所有的信息
- [数据层]查询全部雇员数据
- [业务层]可以实现数据的分页显示,同时又可以返回所有的雇员数量
- [数据层]雇员信息的分页查询
- [数据层]视图count函数查询雇员数量
![image 20200813232311652](https://i-blog.csdnimg.cn/blog_migrate/c89ad7761757b2d992b1b23a268465f9.png)
我们可以得出结论:用户提出的所有需求都应该划分为业务层,因为它指的是功能,而开发人员必须根据业务机进行数据层的设计
3.0 项目的准备
我们的项目只需要一行表即可
drop table if exists emp;
/*==============================================================*/
/* Table: emp */
/*==============================================================*/
create table emp
(
empno int not null,
ename varchar(255),
job varchar(255),
hiredate date,
sal double,
comm double,
primary key (empno)
);
![image 20200814152003469](https://i-blog.csdnimg.cn/blog_migrate/33fdc7dde751669a3db74cbe8f8a9178.png)
我们首先进行项目的创建和数据库的配置
项目名称DAOProject
所有项目的父包叫com.rango
,子包根据具体功能进行细分
3.1数据库连接类
我们单独定义一个
DataConnection
类,这个类主要负责数据库连接对象,负责数据库的连接操作
我们将它保存在dbc子包之中,定义数据库连接类
package com.rango.dbc;
import java.sql.*;
/**
* @program: DAO设计模式学习
* @description: 整个项目的数据库连接类,项目的准备工作
* @author: JiaYadong
* @create:
**/
public class DatabaseConnection {
/* 工具类中的方法都是私有的
*因为工具类当中的方法都是静态的,不需要太new对象,直接采用类名调用
* */
private DatabaseConnection(){
}
// 静态代码在类加载时执行,并且只执行一次
static {
try{
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获取连接对象
/**
*
* @return 连接对象
* @throws SQLException 方法跑出异常,主方法里接收异常
*/
private static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test_db?characterEncoding=utf8",
"root", "123456");
}
//关闭数据库操作,我重写了好几个数据操作对象
private static void close(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2 开发value-object
程序分层后实际上都是指面向对象的分层,而不同层之间(这些层除了数据层要操作的是sql语句之外,其他层操作的数据都应该是对象)是需要数据传递的,那么就应该有一个负责传输的数据对象,而这个对象需要和一条数据进行完整的映射,我们想到了简单java类(po,pojo,vo,to)
数据层和业务层都是通过简单java类进行交互,在实际的工作之中,针对简单java类的开发给出如下的要求,
- 考虑到日后程序可能会出现的分布式应用问题,所以简单java类对必须实现
java.io.serializable
- 简单java类的名称必须与表名称保持一致
- 有可能采用这个样的名字(驼峰命名法),student_info,StudentInfo
- 类中的属性不允许使用基本数据类型,都必须使用基本数据类型的包装类
- 基本数据类型的默认值有可能是0,而如果是包装类默认值就是null
- 类中可以定义有多个构造方法,但是必须保留一个无参的构造方法
- 类中的属性必须是private,封装后的属性必须要有setter,getter
我们将所有简单类保存在vo包中,
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Date hiredate;
private Double sal;
private Double comm;
`````getter和setter省略
}
4.0 开发数据层
数据层的主要操作是使用SQL语句与数据库进行交互,其功能如下
![image 20200814074448817](https://i-blog.csdnimg.cn/blog_migrate/2c1864533b880c7a968b3dad0bb6c438.png)
数据层最终是交给业务层调用的,所有业务层必须知道数据层的执行标准,即:业务层需要明确的知道数据层的执行标准 ,但是不必知道具体实现方法
4.1 开发数据层操作标准
不同层直接如果要进行访问,那么必须要提供有接口,以定义操作标准,对于数据层也是一样的,数据层也是交给业务层处理,所以我们要知道数据层操作标准
对于数据层的接口给出如下的开发要求
-
数据层既然是进行数据操作的,那么就将其保存到dao包下面
-
既然不同数据表对于不用数据层操作开发,我们可以用一下命名
- emp表,我们的数据层的接口就应该定义为
IEmpDAO
- emp表,我们的数据层的接口就应该定义为
-
对于数据层的开发严格来讲就只有两类功能
-
数据更新:建议它的操作方法以doXxx()的形式命名,例如doCreate,doUpdate,doRemove
-
查询操作:查询操作分为两类
- **数据查询:**对于查询分为两种形式以findByXxx()为主,如findAll(),findById(),findByJob()
- **统计查询:**以getXxx()或者getByXxx()为主,如getAllCount,getByJobcount()
![image 20200814135429948](https://i-blog.csdnimg.cn/blog_migrate/da6724d7d72cf6b2466a5c508a26878c.png)
4.2 数据层实现类
实现类中比较难的几个点,
模糊分页查询,早oracle和mysql中分页查询不同
为了方便阐述,下面统一使用student表作为查询的表;colName表示student表中的某个字段名字。
1、mysql
select * from student (order by colName) limit m, n;
参数解释 m:表示要查询记录的上一行的行号,比如要从第1条记录开始查则m取0;
n:表示希望从m+1开始查询多少条记录;
select*from emp (order by )
2、oracle
select * from (select t.*,rownum rn from (select * from student) t where rownum<=m) where rn>=n;
参数解释 rownum:是oracle系统为查询返回的行顺序分配的编号
m:查询的最大行号;
n:查询的最小行号;
这样查询的结果行数为:m-n+1
插入知识[jdbc中如何实现动态分页]
二、jdbc中如何实现动态分页
1、四个重要参数
pageNow 当前的页码
pageCount 总页数
pageSize 每页中显示多少条记录
rowCount 表中记录的总行数
2、根据rowCount和pageSize计算pageCount的小算法
① if(rowCount % pageSize == 0) {
pageCount = rowCount / pageSize;
} else {
pageCount = rowCount / pageSize + 1;
}
② pageCount = rowCount % pageSize == 0 ? rowCount / pageSize : rowCount / pageSize + 1;
③ pageCount = (rowCount - 1) / pageSize + 1;
原理是一样的,只是给出了三种形式,个人比较喜欢③。
3、将pageSize和pageNow用在jdbc的sql语句中查询当前页的记录
⑴mysql
select * from student (order by colName) limit (pageNow-1)*pageSize, pageSize;
⑵oracel
select * from (select t.,rownum rn from (select * from student) t where rownum<=pageNowpageSize) where rn>=(pageNow-1)*pageSize+1;
⑶sql server
select top pageSize * from student where stuid>( select max(stuid) from ( select top (pageNow-1)*pageSize stuid from student order by stuid ) as t ) order by stuid;
sql server分页查询语句中,这条语句的效率较高,其他的几种查询形式这里就不讨论了。
4.3定义DAO工厂类
用户在开发过程中不用知道子类的具体实现方法,只需要依靠接口取得数据层对象,数据库连接对象,就行
public class DAOFactory {
public IEmpDAO getIEmpDAOInstance(Connection connection){
return new EmpDAOImpl(connection);
}
}
5.0 开发业务层
业务层的主要功能是调用数据层操作,而数据层之间的数据传输都是利用简单java类完成的
![image 20200814201911601](https://i-blog.csdnimg.cn/blog_migrate/05d4c5ea882348fbe51dd352b816b00e.png)
5.1 开发层标准–IEmpService
业务层以后也是需要留给其它层进行调用的,所以业务层定义时也需要首先定义出操作标准:任然是老三样 接口Interface—>实现类—>工厂类
对于业务层,接口命名要求为表名称+Service,如IEmpService,表示操作Emp表的业务
5.2定义业务层标准的实现类
业务层实现类的核心功能
- 负责控制数据库的打开和关闭,当存在了业务层对象之后其目的就是为了操作数据库,即:业务层对象实例化之后就必须准备好数据库连接
- 根据DAOFactory调用
getIEmpDAOInstance
方法之后取得IEmpDAO接口对象,业务层的实现类保存在
不同层直接的访问依靠的就是工厂类和接口进行操作
5.3定义业务层工厂类ServiceFactory
业务层最终依然需要被其他的层所使用,所以需要为其定义工作类
如果要取得IEmpService接口对象,一定需要使用工厂类避免耦合问题
从开发来讲,业务层分为两种:
- 前台逻辑:可以将其保存在service.front包中,工厂类,:serviceFrontFactory
- 后台逻辑:可以将其保存在service.back包中,工厂类:serviceBackFactory
在实际的编写之中,子类永远都是不可见的,同时在整个操作里面,控制层看不到任何的JDBC代码
6.0 测试操作
程序完成之后,我们需要编写测试程序,有两种方法完成测试程序
- 方法一:可以直接编写主方法,自己根据它的返回值结果进行判断是否成功
- 方法二:利用Junit完成,这样的做法标准,而且也方便日后调试
package com.rango.Test;
import com.rango.factory.ServiceFactory;
import com.rango.vo.Emp;
import java.util.Date;
/**
* @program: DAO设计模式学习
* @description: 测试插入类
* @author: JiaYadong
* @create: 2020-08-15 15:12
**/
public class TestEmpInsert {
public static void main(String[] args){
Emp vo=new Emp();
vo.setEmpno(1234);
vo.setEname("陈冠希");
vo.setJob("摄影师");
vo.setHiredate(new Date());
vo.setSal(1500.0);
vo.setComm(200.0);
// 数据保存之前要检查数据库里是否存在这些值
try {
System.out.println(ServiceFactory.getIEmpServiceInstance().insert(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时客户端的调用非常简单,就是调用业务层方法
![image 20200815152117650](https://i-blog.csdnimg.cn/blog_migrate/b120bc3f12e431e7e81a7d5834f247e8.png)
而且数据库中存在这样的值,我们再次执行这个方法
![image 20200815152337955](https://i-blog.csdnimg.cn/blog_migrate/b1cff90d26bff05cb2a89a0d6f1c8b9a.png)