- 工厂类介绍
- 工厂模式演示步骤
- 步骤1:新建 DAOFactory.java,在 DAOFactory 中确定 EmployeeDAO 接口的具体实现类
- 步骤 2:Servlet 中增删改查操作都使用 DAOFactory 生成具体实例
- DAO 及工厂模式
- 项目结构
- 参考代码
- 改进 DAO 工厂模式
- 步骤 1:新建配置文件 dao_config.properties
- 步骤 2:新建读取配置文件的工具类 ConfigUtil.java
- 步骤 3:修改 DAOFactory.java
定义一个类,由该类来提供程序需要使用的某些对象,这个类就是工厂类,工厂类封装了对象的创建细节,为调用者提供符合要求的对象。示意图如下:
现在做这样一个假设,假如我们的底层数据访问不再使用 JDBC,而采用 Hibernate。 那么我们需要为 EmployeeDAO 增加一个实现 EmployeeDAOHibernateImpl.java 并且修改之前的代码,DelEmpServlet、AddEmpServlet、ListEmpServlet、LoadEmpServlet、 ModifyEmpServlet 都要做相应修改,如下所示:
注:模拟实现 1) EmployeeDAOHibernateImpl.java
/**
* DAO 的另外一个实现
*/
public class EmployeeDAOHibernateImpl implements EmployeeDAO {
public void delete(long id) throws Exception {}
public List findAll() throws Exception {
System.out.println("Hibernate 实现 findAll ...");
return null;
}
public Employee findById(long id) throws Exceptino {
return null;
}
public void save(Employee e) throws Exception {}
public void update(Employee e) throws Exception {}
}
2) DelEmpServlet.java
public class DelEmpServlet extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
long id = Long.parseLong(request.getParameter("id"));
try {
// EmployeeDAO dao = new EmployeeDAOJdbcImpl();
EmployeeDAO dao = new EmpployeeDAOHibernateImpl();
dao.delete(id);
response.sendRedirect("list");
} catch (Exception e) {
e.printStackTrace();
throw new ServletException(e);
}
}
}
3) AddEmpServlet.java(略)
4) ListEmpServlet(略)
5) LoadEmpServlet(略)
6) ModifyEmpServlet(略)
然而,我们希望尽量不修改写好的代码(Servlet 代码)。 此时,我们可以使用工厂模式
工厂模式演示步骤 步骤1:新建 DAOFactory.java,在 DAOFactory 中确定 EmployeeDAO 接口的具体实现类ok,当查询数据库需要不同实现时,只要修改工厂类 DAOFacory 即可,这样我们就不用修改每个 Servlet 了
提问 :这样的方式已经使代码的可扩展性提高很多,但是我们还需要修改 DAOFactory,如果我们不想修 改 DAOFacotry 该如何实现?
回答:使用配置文件,写一个类专门读取配置文件(属性文件)的 value
,需要变更时,修改配置文件即可
1) Employee.java
package entity;
/**
* 实体类
* @author liaowenxiong
*/
public class Employee {
private long id;
private String name;
private double salary;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
2) EmployeeDAO.java
package dao;
import java.util.List;
import entity.Employee;
/**
* DAO 接口
* @author liaowenxiong
*/
public interface EmployeeDAO {
/*
* public void save(Employee e) throws SQLException;
* 这种方式不好,因为出现了 jdbc 的异常类,这就要求实现该方法只能使用 jdbc
*
*/
public List findAll() throws Exception;
public void delete(long id) throws Exception;
public void save(Employee e) throws Exception;
public Employee findById(long id) throws Exception;
public void update(Employee e) throws Exception;
}
3) EmployeeDAOJdbcImpl.java
package dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import util.DBUtil;
import dao.EmployeeDAO;
import entity.Employee;
public class EmployeeDAOJdbcImpl implements EmployeeDAO {
/*
* 查询员工列表
*/
public List findAll() throws Exception {
Connection conn = DBUtil.getConnection();
Statement stat = conn.createStatement();
ResultSet rst = stat.executeQuery("select * from t_emp");
List employees = new ArrayList();
while (rst.next()) {
Employee e = new Employee();
e.setId(rst.getLong("id"));
e.setName(rst.getString("name"));
e.setSalary(rst.getDouble("salary"));
e.setAge(rst.getInt("age"));
employees.add(e);
}
// 关闭数据库连接
DBUtil.close(conn);
return employees;
}
/*
* 删除员工
*/
public void delete(long id) throws Exception {
Connection conn = DBUtil.getConnection();
PreparedStatement prep = conn.prepareStatement("delete from t_emp where id = ?");
prep.setLong(1, id);
prep.executeUpdate();
DBUtil.close(conn);
}
/*
* 保存员工
*/
public void save(Employee e) throws Exception {
Connection conn = DBUtil.getConnection();
PreparedStatement prep = conn.prepareStatement( "insert into t_emp(name,salary,age) values(?,?,?)"); prep.setString(1, e.getName());
prep.setDouble(2, e.getSalary());
prep.setInt(3, e.getAge());
prep.executeUpdate();
DBUtil.close(conn);
}
/*
* 通过员工ID号查询员工信息
*/
public Employee findById(long id) throws Exception {
Connection conn = DBUtil.getConnection();
PreparedStatement prep = conn.prepareStatement( "select * from t_emp where id = ?");
prep.setLong(1, id);
ResultSet rst = prep.executeQuery();
Employee e = null;
if (rst.next()) { // 这样的写法只能查询一个员工,如果查询到多个员工,也只是获取结果集中的第一个员工的数据
e = new Employee();
e.setId(id);
e.setName(rst.getString("name"));
e.setSalary(rst.getDouble("salary"));
e.setAge(rst.getInt("age"));
}
DBUtil.close(conn);
return e;
}
public void update(Employee e) throws Exception {
Connection conn = DBUtil.getConnection();
PreparedStatement prep = conn.prepareStatement( "update t_emp" +
"set name = ?, salary = ?, age = ?" +
"where id = ?");
prep.setString(1, e.getName());
prep.setDouble(2, e.getSalary());
prep.setInt(3, e.getAge());
prep.setLong(4, e.getId());
prep.executeUpdate();
DBUtil.close(conn); }
}
4) EmployeeDAOHibernateImpl.java
package dao.impl;
import java.util.List;
import dao.EmployeeDAO;
import entity.Employee;
/**
* dao 的另外一个实现
* @author liaowenxiong
*/
public class EmployeeDAOHibernateImpl implements EmployeeDAO {
public void delete(long id) throws Exception {}
public List findAll() throws Exception {
System.out.printl n("hibernate 实现 findAll...");
return null;
}
public Employee findById(long id) throws Exception {
return null;
}
public void save(Employee e) throws Exception {}
public void update(Employee e) throws Exception {}
}
5) TestDAO.java
package test;
import java.util.List;
import dao.EmployeeDAO;
import dao.impl.EmployeeDAOJdbcImpl;
import entity.Employee;
/**
* 测试类,用来测试程序用
*/
public class TestDAO {
public static void main(String[] args) throws Exception {
EmployeeDAO dao = new EmployeeDAOJdbcImpl();
// 查找全部
List employees = dao.findAll();
System.out.println(employees.size());
// 删除
// dao.delete(1);
// 插入数据
// Employee e = new Employee();
// e.setName("abc9");
// e.setSalary(1000);
// e.setAge(22);
// dao.save(e);
// 按id查找
// Employee e = dao.findById(7);
// System.out.println(e.getName());
}
}
6) DAOFactory.java
package util;
import dao.impl.EmployeeDAOHibernateImpl;
import dao.impl.EmployeeDAOJdbcImpl;
/**
* dao 工厂,为调用者提供 dao 实现类的实例
* @author liaowenxiong
*/
public class DAOFactory {
public static Object getInstance(String type) {
Object obj = null;
if (type.equals("EmployeeDAO")) {
//obj = new EmployeeDAOJdbcImpl();
obj = new EmployeeDAOHibernateImpl();
}
return obj;
}
}
7) DBUtil.java
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* jdbc 工具类,提供获得连接、关闭连接的方法
* @author liaowenxiong
*/
public class DBUtil {
/**
* 获取数据库连接对象
*/
public static Connection getConnection() throws Exception {
Connection conn = null;
try {
// 内部会通过类加载器将 Driver 读取到内存,得到一个用于保存 Driver 信息的 Class实例
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/qpw","root","root");
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return conn;
}
/**
* 关闭数据库连接
*/
public static void close(Connection conn) {
if(conn!= null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//用于测试 DBUtil
public static void main(String[] args) throws Exception {
System.out.println(getConnection());
}
}
8) AddEmpServlet.java
package web;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import dao.EmployeeDAO;
import dao.impl.EmployeeDAOJdbcImpl;
import entity.Employee;
public class AddEmpServlet extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
/*
* 指定字符编码的代码要放在 getParameter() 执行之前,
* 因为客户端传过来的参数内容可能含有中文字符,常规下是
* 使用 utf-8 编码后再传给服务端,服务端接收请求数据包
* 后,要读取内容就需要解码,所以就要提前设置好用于解码
* 的字符编码类型
*/
request.setCharacterEncoding("utf-8"); // 指定用来解码的字符编码为 utf-8
String name = request.getParameter("name");
double salary = Double.parseDouble(request.getParameter("salary"));
int age = Integer.parseInt(request.getParameter("age"));
System.out.println("name:" + name);
System.out.println("salary:" + salary);
System.out.println("age:" + age);
Employee e = new Employee();
e.setName(name);
e.setSalary(salary);
e.setAge(age);
// EmployeeDAO dao = new EmployeeDAOJdbcImpl();
// 使用工厂模式
EmployeeDAO dao = (EmployeeDAO)DAOFactory.getInstance("EmployeeDAO");
try {
// 将员工信息保存到数据库
dao.save(e);
} catch (Exception e) {
// step1 先记录日志
e.printStackTrace();
// step2 抛出
throw new ServletException(e);
}
response.sendRedirect("list");
}
}
9) DelEmpServlet.java
package web;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import dao.EmployeeDAO;
import dao.impl.EmployeeDAOHibernateImpl;
import dao.impl.EmployeeDAOJdbcImpl;
public class DelEmpServlet extends HttpServlet {
public void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
long id = Long.parseLong(
request.getParameter("id"));
// EmployeeDAO dao = new EmployeeDAOJdbcImpl();
// EmployeeDAO dao = new EmployeeDAOHibernateImpl();
// 使用工厂模式
EmployeeDAO dao = (EmployeeDAO)DAOFactory.getInstance("EmployeeDAO");
try {
dao.delete(id);
}catch(Exception e){
e.printStackTrace();
throw new ServletException(e);
}
response.sendRedirect("list");
}
}
10) ListEmpServlet.java
package web;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import util.DAOFactory;
import dao.EmployeeDAO;
import dao.impl.EmployeeDAOJdbcImpl;
import entity.Employee;
public class ListEmpServlet extends HttpServlet {
public void service(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {
// 使用 dao 访问数据库
try {
// EmployeeDAO dao = new EmployeeDAOJdbcImpl();
// 使用工厂模式
EmployeeDAO dao = (EmployeeDAO)DAOFactory.getInstance("EmployeeDAO");
List employees = dao.findAll();
//使用查询得到的结果,生成一个表格
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("");
out.println("id姓名薪水年龄操作");
for (int i = 0; i
addEmpServlet
web.AddEmpServlet
listEmpServlet
web.ListEmpServlet
delEmpServlet
web.DelEmpServlet
loadEmpServlet
web.LoadEmpServlet
modifyEmpServlet
web.ModifyEmpServlet
addEmpServlet
/add
listEmpServlet
/list
delEmpServlet
/del
loadEmpServlet
/load
modifyEmpServlet
/modify
500
/error.html
14) addEmp.html
addEmp
添加雇员
姓名:
薪水:
年龄:
15) error.html
Insert title here
发生了系统错误,请稍后
重试
改进 DAO 工厂模式
我们在实现“数据库访问技术由 JDBC 更改为 Hibernate”的时候,还需要修改 DAOFactory,如果我们不想修改 DAOFacotry 该如何实现?
步骤 1:新建配置文件 dao_config.propertiespublic class ConfigUtil {
private static Properties props = new Properties();
static {
ClassLoader loader = ConfigUtil.class.getClassLoader();
InputStream ips = loader.getResourceAsStream("util/dao_config.properties");
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getValue(String key) {
return props.getProperty(key);
}
/**
* @return
* @MethodName
* @Author liaowenxiong
* @Description TODO
* @Date 下午4:10 2021/3/31
* @Param
*/
public static void main(String[] args) {
System.out.println(getValue("EmployeeDAO"));
}
}
步骤 3:修改 DAOFactory.java
public class DAOFactory {
/**
* @MethodName
* @Author liaowenxiong
* @Description 读取属性文件,根据传入的 key 获得对应的 value,key 是接口名称,value 就是实现类的包路径名称,
* 然后通过反射机制创建实现类的实例并返回给调用者
* @Date 下午5:08 2021/3/31
* @Param
* @return
*/
public static Object getInstance(String type) {
Object obj = null;
String className = ConfigUtil.getValue(type);
// newInstance 已经过时了
// obj = Class.forName(className).newInstance();
try {
obj = Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
在以后的修改,我们只需要修改配置文件 dao_config.properties 即可。