在我们的教务管理系统中,除了需要有TeacherDao,我们在后面还需要KlassDao, StudentDao等等其实的数据访问层。而这些数据访问层在进行CURD操作时,代码大部分都是相同的。在本节中,让我们共同学习如何使用抽像类与接口来改写DAO层的CURD操作。
# 基类
我们新建TestDao,并在该类中,写入`get(Long id)`及`delete(Teacher teacher)`方法,然后我们用TeacherDao来进行继承.
![https://box.kancloud.cn/36eb70c7a526600a85c85d4bf6eb396a_1234x568.png](https://box.kancloud.cn/36eb70c7a526600a85c85d4bf6eb396a_1234x568.png)
但现在问题来了,我们在调用TeacherDao的get()方法时,返回Teacher是没有问题的。但如果调用StudentDao的get()方法,则应该返回的是Student实体,而非Teacher。所以TestDao中的get(Long id)方法的返回值其实是不确定的。即:它即有可能返回的的Teacher实体,也可以返回Student实体。而java又是强类型语言,是必须指定函数的返回值的。怎么办呢?
为了解决这个问题,我们引入接口。
# 接口
新建实体接口,其它实体来继承该接口:
![https://box.kancloud.cn/38935d774d89fa371d40d9d21029ef80_962x600.png](https://box.kancloud.cn/38935d774d89fa371d40d9d21029ef80_962x600.png)
此时,我们的返回值的类型,就可以声明为该接口类型了:
![https://box.kancloud.cn/8aa51d6c6f58b34d63b88d821e881f55_1222x562.png](https://box.kancloud.cn/8aa51d6c6f58b34d63b88d821e881f55_1222x562.png)
同时,我们增加了`getFeaturedClass()`,它的返回值是一个实体类。我们共同来查看如下时序图,来看看`getFeaturedClass()`的在StudentDao进行`get`操作时的具体作用.
![https://box.kancloud.cn/512feaf0b512909e92344f2852cef0ac_1396x668.png](https://box.kancloud.cn/512feaf0b512909e92344f2852cef0ac_1396x668.png)
我们看到,在TestDao在执行`get`操作时,又调用了StudentDao中的`getFeaturedClass()`方法。而该方法决定了第6步操作同样将返回一个`Student`实体。
通过上图我们看出,要使TestDao的get()操作成功,那么继承它的实现类则必须存在`getFeaturedClass()`,否则,将导致找不到`getFeaturedClass`方法的异常。为了避免其它实现类在进行继承后可能忘记写`getFeaturedClass()`的问题,我们为其增加接口来进行约束。
![https://box.kancloud.cn/c6e81667f52ae7dd6f613c85a50f6d56_1288x854.png](https://box.kancloud.cn/c6e81667f52ae7dd6f613c85a50f6d56_1288x854.png)
# 规范接口
实际上,抽像类更多的,是在实现接口中一些具体通用性的方法。而在实现类中,去实现接口中的非通用方法。比如,在上图中,TestDao做为抽像类,其实现了具体的数据获取与删除的方法。而StudentDao做为实现类,实现了接口中非通用的`getFeaturedClass()`方法。接口做的更多的,是规范与约束程序,按照这种思想,我们再次进行改写:
![https://box.kancloud.cn/aaa639a9093a38c845ed3f057fdbda27_1334x888.png](https://box.kancloud.cn/aaa639a9093a38c845ed3f057fdbda27_1334x888.png)
至此,我们将接口做为返回类型,解决了返回值并不确认为哪种对象的问题;我们还应用接口对类进行了规范与约束;我们还应用了抽像类,来实现了那些通用的方法。如果本节中讲过的,你非常清晰的明了其中的脉络,那么恭喜你。在学习JAVA的路上,你迈出了进阶的一步。
<hr />
具体代码:
我们对Dao接口进行补充,同时将TestDao改个名字:AbstractDao,并声明为Abstract。最后,增加TeacherDao中的接口方法.
```
/*
* 在接口中声明方法
*/
package com.mengyunzhi.javaee.dao;
import com.mengyunzhi.javaee.entity.IdEntity;
import java.io.Serializable;
import java.util.Collection;
/**
* Dao. Interface.
*/
public interface Dao {
Class<?> getFeaturedClass();
IdEntity get(Serializable id);
Serializable create(IdEntity object);
IdEntity update(IdEntity object);
int delete(Serializable id);
int delete(IdEntity object);
Collection<?> paginate(int page, int pageSize);
Collection<?> all();
}
```
抽像类继承了接口,并实现接口声明的方法
```
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.mengyunzhi.javaee.dao;
import com.mengyunzhi.javaee.exception.CreateException;
import com.mengyunzhi.javaee.exception.UpdateException;
import com.mengyunzhi.javaee.entity.IdEntity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
/**
* AbstractDao.
*/
public abstract class AbstractDao implements Serializable, Dao {
/**
*
*/
private static final long serialVersionUID = 1L;
private static SessionFactory sessionFactory;
protected static Session getCurrentSession() {
// 每个数据库只需要一个sessionFactory,在这里进行单一实例处理。
if (null == sessionFactory) {
// 实例化并加载数据库配置文件
Configuration configuration = new Configuration().configure();
// 构造服务注册对象
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
// 创建会话工厂(session factory))
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
return sessionFactory.getCurrentSession();
}
public IdEntity get(Serializable id) {
IdEntity object = null;
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 使用Teacher.class来获取到Teacher的类名(包括包名)
object = (IdEntity) session.get(getFeaturedClass(), id);
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
transaction.rollback();
}
// 打印异常
e.printStackTrace();
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return object;
}
public Serializable create(IdEntity object) {
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 新增数据
session.save(object);
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
try {
transaction.rollback();
} catch (TransactionException te) {
// 抛出异常
te.printStackTrace();
}
}
// 抛出异常
e.printStackTrace();
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return object.getId();
}
public IdEntity update(IdEntity object) {
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 删除
session.update(object);
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
try {
transaction.rollback();
} catch (TransactionException te) {
// 抛出异常
throw (te);
}
}
// 抛出异常
throw (e);
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return object;
}
public int delete(Serializable id) {
IdEntity idEntity = this.get(id);
return this.delete(idEntity);
}
public int delete(IdEntity object) {
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 删除
session.delete(object);
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
try {
transaction.rollback();
} catch (TransactionException te) {
// 抛出异常
throw (te);
}
}
// 抛出异常
throw (e);
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return 0;
}
@SuppressWarnings("unchecked")
public Collection<?> all() {
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
Collection<IdEntity> entities = new ArrayList<IdEntity>();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 查询Teacher表,注意:是Teacher ,而不是 teacher
Query query = session.createQuery("from "
+ getFeaturedClass().getSimpleName());
// 预查询,只有在事务提交时,才进行查询操作
entities = query.list();
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
try {
transaction.rollback();
} catch (TransactionException te) {
te.printStackTrace();
}
}
// 打印异常
e.printStackTrace();
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return entities;
}
@SuppressWarnings("unchecked")
public Collection<?> paginate(int page, int pageSize) {
// 创建会话(这里的session也是会话的意思,我们以前接触的http中的session,处理的是用户与服务器的对话)
Session session = getCurrentSession();
// 开启事务(使用缓冲池进行数据库的连接)
Transaction transaction = session.beginTransaction();
Collection<IdEntity> entities = new ArrayList<IdEntity>();
// 在这里,必须使用try catch finally语句。来确定会话正常关闭.
// 否则,当操作数据库产生错误时,你可能需要重启mysql服务
try {
// 查询Teacher表,注意:是Teacher ,而不是 teacher
String hql = "from " + getFeaturedClass().getSimpleName();
// 预查询,只有在事务提交时,才进行查询操作
entities = (ArrayList<IdEntity>) session.createQuery(hql)
.setFirstResult(page)
.setMaxResults(pageSize)
.list();
// 提交事务
transaction.commit();
// 捕获异常
} catch (HibernateException e) {
// 如果事务执行异常,则回滚事务
if (null != transaction) {
try {
transaction.rollback();
} catch (TransactionException te) {
te.printStackTrace();
}
}
// 打印异常
e.printStackTrace();
} finally {
// 如果session处于开启状态,则关闭session
if (session.isOpen()) {
// 关闭会话
session.close();
}
}
return entities;
}
}
```
在TeacherDao中,实现抽象类中未实现的方法:
```
package com.mengyunzhi.javaee.dao;
import com.mengyunzhi.javaee.entity.Teacher;
public class TeacherDao extends AbstractDao {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public Class<Teacher> getFeaturedClass() {
// 返回Teacher实体类
return Teacher.class;
}
}
```
- README
- 第一章:准备
- 第二章:Hello World!
- 第一节:查看工程文件
- 第二节:JDK、JRE与环境变量
- 第三节:index.jsp
- 第三章:Hello Struts
- 第一节:Web.xml
- 第二节:单入口
- 第三节:Hello Struts
- 第四节:触发C层
- 第四章:建立数据表
- 第一节:建立实体类
- 第二节:测试一
- 第三节:测试二
- 第四节:引入Hibernate
- 第五节:配置Hibernate
- 第六节:建立连接
- 第七节:实体类映射数据表
- 第八节:完善数据表
- 第五章:教师管理
- 第一节:增加数据--add
- 第二节:增加数据--save
- 1 获取传入数据数据
- 2 数据写入测试
- 3 对接C层
- 第三节:数据列表
- 1 获取数据
- 2 重构代码
- 3 C层对接--初始化
- 4 C层添加数据
- 5 V层显示数据
- 6 获取数据库中数据
- 7 显示性别
- 8 分页
- 9 条件查询
- 第四节:修改数据
- 1 edit
- 2 update
- 第五节:删除数据
- 第六节:总结
- 第六章:重构C层
- 第一节:继承ActionSupport类
- 第二节:数据验证
- 第七章:前台分离(前台)
- 第一节:环境搭建
- 第二节:运行环境
- 第三节:共享开发环境
- 第四节:生产环境
- 第八章:前台开发(前台)
- 第一节:本地化
- 第二节:教师列表
- 1 引入M层
- 2 模拟后台返回数据
- 3 C与M对接
- 4 C与V对接
- 第九章:前后台对接(前后台)
- 第一节:后台输出json(后台)
- 第二节:对接前台(全栈)
- 第二节:对接API(前台)
- 第二节:跨域请求(后台)
- 第三节:重构代码(前台)
- 第十章:重构后台M层
- 第一节:数据访问DAO层
- 第二节:项目整体重构
- 第十一章:用户登陆(前后台)
- 第一节:制定规范
- 第二节:定制测试用例
- 第三节:后台输入测试代码(后台)
- 第四节:postman(后台)
- 第五节:新建用户登陆模块(前台)
- 第六节:代码重构(前台)
- 第十二章:班级管理(前后台)
- 第一节:班级列表
- 1 原型开发
- 2 制定规范
- 3 后台对接开发
- 4 前台对接开发
- 第二节:Add
- 1 原型开发
- 2 制定规范
- 3 后台对接开发
- 4 前台对接开发
- 第三节:Save
- 1 制定规范
- 2 后台对接开发
- 3 前台对接开发
- 第四节:Edit
- 1 原型开发
- 2 制定规范
- 3 后台对接开发
- 4 前台对接开发
- 第五节:Update
- 1 制定规范
- 2 后台对接开发
- 3 前台对接开发
- 第六节:Delete
- 1 制定规范
- 2 后台对接开发
- 3 前台对接开发
- 第七节:小结
- 第十三章:班级管理(API)
- 第一节:ER图
- 第二节:create
- 1 实体层
- 2 dao层
- 3 service(server)层
- 4 action层
- 第三节:ManyToOne
- 第四节:Read
- 1 service(server)层
- 2 action层
- 第五节:update
- 1 service(server)层
- 2 action层
- 第六节:update
- 第十四章:重构服务层