在我們的教務管理系統中,除了需要有TeacherDao,我們在后面還需要KlassDao, StudentDao等等其實的數據訪問層。而這些數據訪問層在進行CURD操作時,代碼大部分都是相同的。在本節中,讓我們共同學習如何使用抽像類與接口來改寫DAO層的CURD操作。
# 基類
我們新建TestDao,并在該類中,寫入`get(Long id)`及`delete(Teacher teacher)`方法,然后我們用TeacherDao來進行繼承.

但現在問題來了,我們在調用TeacherDao的get()方法時,返回Teacher是沒有問題的。但如果調用StudentDao的get()方法,則應該返回的是Student實體,而非Teacher。所以TestDao中的get(Long id)方法的返回值其實是不確定的。即:它即有可能返回的的Teacher實體,也可以返回Student實體。而java又是強類型語言,是必須指定函數的返回值的。怎么辦呢?
為了解決這個問題,我們引入接口。
# 接口
新建實體接口,其它實體來繼承該接口:

此時,我們的返回值的類型,就可以聲明為該接口類型了:

同時,我們增加了`getFeaturedClass()`,它的返回值是一個實體類。我們共同來查看如下時序圖,來看看`getFeaturedClass()`的在StudentDao進行`get`操作時的具體作用.

我們看到,在TestDao在執行`get`操作時,又調用了StudentDao中的`getFeaturedClass()`方法。而該方法決定了第6步操作同樣將返回一個`Student`實體。
通過上圖我們看出,要使TestDao的get()操作成功,那么繼承它的實現類則必須存在`getFeaturedClass()`,否則,將導致找不到`getFeaturedClass`方法的異常。為了避免其它實現類在進行繼承后可能忘記寫`getFeaturedClass()`的問題,我們為其增加接口來進行約束。

# 規范接口
實際上,抽像類更多的,是在實現接口中一些具體通用性的方法。而在實現類中,去實現接口中的非通用方法。比如,在上圖中,TestDao做為抽像類,其實現了具體的數據獲取與刪除的方法。而StudentDao做為實現類,實現了接口中非通用的`getFeaturedClass()`方法。接口做的更多的,是規范與約束程序,按照這種思想,我們再次進行改寫:

至此,我們將接口做為返回類型,解決了返回值并不確認為哪種對象的問題;我們還應用接口對類進行了規范與約束;我們還應用了抽像類,來實現了那些通用的方法。如果本節中講過的,你非常清晰的明了其中的脈絡,那么恭喜你。在學習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
- 第十四章:重構服務層