这几天开始和社会董还有小孟愿开始写小组的图书管理系统,将在此期间遇到的一些问题和学到的一些知识总结在博客里。
这篇博客要总结的就是在WEB项目中用到的很重要的一种数据库设计模式DAO。
DAO是什么
DAO是WEB项目里面的数据层,主要负责为其他各层(MVC(Model View Controller))提供数据。DAO层里封装了对数据库的各种操作的代码。
为什么要使用DAO
我们在写WEB项目时,经常会有这样的需求:需要从数据库拿到数据,然后再展示再前端页面上,这也就说明我们需要在JSP页面中使用JDBC连接数据库进行各种操作。且不说在JSP页面上操作数据库有多繁复,单是大段大段的JAVA代码就已经使得JSP页面变得很复杂了。JSP页面应该专注于数据的展示结果,而非数据的取得过程。所以我们使用DAO设计模式,提供一组通用的数据库操作方法,简化代码,增强程序的可移植性。
DAO层的组成
DAO层主要由五个部分组成:DBUtil(数据库连接类),VO(valueObject值对象),IDAO(DAO接口),DAOImpl(DAO实现类),DAOFactory(DAO工厂类)。
下面来详细介绍这五个部分。
1. DBUtil(数据库连接类)
在DBUtil数据库连接类中,主要是封装了数据库的连接和关闭操作。
在使用JDBC连接数据库中,不同的数据库操作都有一段相同的代码,那就是连接数据库和关闭数据库。为了避免代码重复多次,我们将这种重复性的操作封装进一个类中,即数据库连接类。
DBUtil类代码如下:
import java.sql.*;
import java.util.ResourceBundle;
/**
* Created by dela on 7/17/17.
*/
public class DBUtils {
public static String URL;
public static String USERNAME;
public static String PASSWD;
public static String Driver;
//加载配置文件
//如果利用resourse目录操作配置文件时,应该用resourceBundel类来操作文件
private static ResourceBundle rb = ResourceBundle.getBundle("db-config");
//静态代码块在加载类时只会被执行一次
static {
URL = rb.getString("jdbc.URL");
USERNAME = rb.getString("jdbc.USERNAME");
PASSWD = rb.getString("jdbc.PASSWD");
Driver = rb.getString("jdbc.Driver");
try {
Class.forName(Driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//开启数据库连接
public static Connection getConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(URL, USERNAME, PASSWD);
} catch (SQLException e) {
e.printStackTrace();
System.out.println("获取连接失败!");
}
return conn;
}
//关闭数据库连接
public static void close(ResultSet rs, Statement stat, Connection conn){
try{
if(rs != null){
rs.close();
}
if(stat != null){
stat.close();
}
if(conn != null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
相关代码的大概含义在注释中都说明了,db-config.properties里用来存储数据库连接URL/用户名/密码的相关信息,一般放在工程的resource目录下,使用resourceBundel类来操作配置文件。具体格式如下:
jdbc.URL = jdbc:mysql://localhost:3306/BookManager?characterEncoding=utf-8&useUnicode=true&useSSL=true
jdbc.USERNAME = root
jdbc.PASSWD = ******
jdbc.Driver = com.mysql.jdbc.Driver
2. VO(valueObject值对象)
VO层里面都是JAVA类,每一个JAVA类都具备Bean的特性。VO层里面的每一个类,分别对应数据库中的每一张表。VO提供了一种面向对象的思想和方法来操作数据库,以后我们的DAO接口就是通过调用VO来进行数据库操作的。
用图书系统数据库中的Book表来举例,book表有如下字段:
int id;
String name;
String author;
String owner;
int amount;
String upload_date;
String describe;
int borrow_num;
则其对应的VO类应该为:
/**
* Created by dela on 7/20/17.
*/
public class Book {
private int id;
private String name;
private String author;
private String owner;
private int amount;
private String upload_date;
private String describe;
private int borrow_num;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getUpload_date() {
return upload_date;
}
public void setUpload_date(String upload_date) {
this.upload_date = upload_date;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public int getBorrow_num() {
return borrow_num;
}
public void setBorrow_num(int borrow_num) {
this.borrow_num = borrow_num;
}
}
看过代码,应该很容易能理解VO层的内容,就不过多描述了。
3. IDAO(DAO接口)
IDAO定义了对一张表的所有可能操作,比如增删改查,但是IDAO中只是定义这些方法的接口,具体实现留给DAOImpl层。一个IDAO类可以有不同的实现,比如同一个接口可以实现出它的多个不同子类,甚至对同一个接口用不同的数据库来实现,这也是根据需求而定的。
BookIDAO的代码如下:
import Dao.ValueObject.Book;
import java.sql.SQLException;
import java.util.List;
/**
* Created by dela on 7/20/17.
*/
public interface BookDao {
//添加操作
public void insert(Book book);
//删除操作
public void delete(String name) throws SQLException;
//更改操作
public void update(Book book);
//查询操作
//书名|作者|所属者模糊查询
public List<Book> queryByNAO(String keyWords);
//书ID查询
public List<Book> queryByBookId(int bookId);
//所有书查询
public List<Book> queryAllBook();
//分类查询
public List<Book> queryByFatherClassId(int fatherClassId);
//后续有其他需求的操作再行添加
}
4. DAOImpl(DAO实现类)
DAOImpl层主要是对IDAO层的实现,如上述所说,可以对同一接口有多种实现。
BookDAOImpl的代码如下:
import Dao.DBConn.DBUtils;
import Dao.IDao.BookDao;
import Dao.ValueObject.Book;
import Model.DealKeywords;
import org.apache.commons.lang.StringEscapeUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by dela on 7/21/17.
*/
public class BookDaoImpl implements BookDao {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
//添加操作
public void insert(Book book) {
String sql = "insert into book_info values(?, ?, ?, ?, ?, ?, ?, ?);";
connection = DBUtils.getConnection();
try {
statement = connection.prepareStatement(sql);
statement.setInt(1, book.getId());
statement.setString(2, book.getName());
statement.setString(3, book.getAuthor());
statement.setString(4, book.getOwner());
statement.setInt(5, book.getAmount());
statement.setString(6, book.getUpload_date());
statement.setString(7, book.getDescribe());
statement.setInt(8, book.getBorrow_num());
sql = StringEscapeUtils.escapeSql(sql);
statement.execute();
DBUtils.close(resultSet, statement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
//删除操作
public void delete(String name) throws SQLException {
String sql = "delete * from book_info where owner = ?;";
sql = StringEscapeUtils.escapeSql(sql);
connection = DBUtils.getConnection();
try {
statement = connection.prepareStatement(sql);
statement.setString(1, name);
statement.executeUpdate();
DBUtils.close(resultSet, statement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
//更改操作
public void update (Book book) {
String sql = "update book_info set name = ?, author = ?, " +
"owner = ?, amount = ?, upload_date = ?, describe = ?, borrow_num = ?" +
"where id = ?;";
connection = DBUtils.getConnection();
try {
statement = connection.prepareStatement(sql);
statement.setString(1, book.getName());
statement.setString(2, book.getAuthor());
statement.setString(3, book.getOwner());
statement.setInt(4, book.getAmount());
statement.setString(5, book.getUpload_date());
statement.setString(6, book.getDescribe());
statement.setInt(7, book.getBorrow_num());
statement.setInt(8, book.getId());
sql = StringEscapeUtils.escapeSql(sql);
statement.executeUpdate();
DBUtils.close(resultSet, statement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
//查询操作
//书名|作者|所属者模糊查询
public List<Book> queryByNAO(String keyWords) {
keyWords = StringEscapeUtils.escapeSql(keyWords);
System.out.println("***");
System.out.println(keyWords);
keyWords = DealKeywords.dealKeyWords(keyWords);
String sql = "select * from book_info where (owner like '" + keyWords + "' or name like '"
+ keyWords + "' or author like '" + keyWords + "');";
System.out.println(sql);
List<Book> books = new ArrayList<Book>();
Book book = null;
connection = DBUtils.getConnection();
try {
statement = connection.prepareStatement(sql);
resultSet = statement.executeQuery();
if(resultSet.next()){
book = new Book();
book.setId(resultSet.getInt(1));
book.setName(resultSet.getString(2));
book.setAuthor(resultSet.getString(3));
book.setOwner(resultSet.getString(4));
book.setAmount(resultSet.getInt(5));
book.setUpload_date(resultSet.getString(6));
book.setDescribe(resultSet.getString(7));
book.setBorrow_num(resultSet.getInt(8));
books.add(book);
}
DBUtils.close(resultSet, statement, connection);
} catch (SQLException e) {
e.printStackTrace();
}
return books;
}
//剩下三种查询方式的实现由于篇幅关系在这里就不贴上了,实现起来也很简单
}
5. DAOFactory(DAO工厂类)
DAOFactory到底是什么呢?DAO工厂模式又该怎样理解呢?为什么要有DAO工厂类的存在?先来模拟一个场景。
当我和社会董还有小孟愿千辛万苦终于把图书系统各自负责的功能模块写完了,要合并代码了,好开心!但是合并代码的时候却傻眼了,我和小孟愿用的是JDBC连接数据库,社会董用的是hibernate连接数据库(很社会!),那么合并代码时,DAO层的合并存在分歧,到底是遵循哪种连接呢?最终少数服从多数,社会董决定将自己的DAO层hibernate操作改为JDBC,但是他仍想保留他的hibernate操作,以便日后劝我和小孟愿也将JDBC操作改为hibernate操作。于是他开始改代码,反正我们的数据库操作都用DAO层封装了嘛,改起来也没多大事。前面说过,DAOImpl可以对同一接口实现不同数据库类型的子类,大不了之前是BookIDAOImpl,现在改为BookIDAOJDBCImpl,社会董就将自己的数据库操作用JDBC重新实现了一遍(恩,没毛病!)。但是,他在改JSP页面的时候震惊了,各种数据库操作全部都是BookDAO bookDAO=new BookDAOImpl();这时候要怎么办呢?
为了避免上述情况的发生,就要引进DAOFactory。
先来看看DAOFactory的内容是什么样的:
import Dao.DaoObject.*;
import Dao.IDao.*;
/**
* Created by dela on 7/21/17.
*/
public class DaoFactory {
public static BookDao getBookDaoInstance(){
return new BookDaoImpl();
}
}
引入DAOFactory后,在JSP中只需要使用工厂里面的静态方法BookDAO,不需要BookDAO bookDAO=new BookDAOImpl();就可直接操作DAO层,而即使社会董用JDBC重新实现了数据库操作,也不用在JSP页面中逐一去修改BookDAO bookDAO=new BookDAOImpl();只需要在DAOFactory中将getBookDaoInstance()的返回值改为他新实现的子类BookIDAOJDBCImpl就好啦,如此操作,是不是很简单!
JSP中使用DAO
在JSP中使用DAO来获取所有图书的数据并显示,示例代码如下:
<%@ page import="Dao.ValueObject.Book" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="Dao.DaoFactory.DaoFactory" %>
<%@ page import="java.util.Iterator" %><%--
Created by IntelliJ IDEA.
User: dela
Date: 7/25/17
Time: 3:53 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>所有图书</title>
</head>
<body>
<div id="content" class="contents">
<%
List<Book> books = new ArrayList<Book>();
Book book = null;
books = DaoFactory.getBookDaoInstance().queryAllBook();
Iterator<Book> bookIterator = books.iterator();
while (bookIterator.hasNext()){
book = bookIterator.next();
%>
<div class="books">
<p>
<img src="img/kernel.jpg">
</p>
<p><%=book.getName()%></p>
<p><%=book.getAuthor()%></p>
<p><%=book.getDescribe()%></p>
<p><%=book.getOwner()%></p>
<p>被借:<%=book.getBorrow_num()%>次</p>
<button>我要借阅</button>
</div>
<%
}
%>
</div>
</body>
</html>