目录
程序分层
整个业务的核心在于后台业务层,业务层是整个程序提供的操作功能,而一个业务要想完成需要多个数据层的操作一起共同完成,而数据层的操作只是一个个原子性的数据库开发。而在实际开发之中,每个操作的业务往往需要牵扯到多个原子性的操作,也就是所有的原子性操作业务最终都在业务层中完成。
在实际开发中,业务的设计是非常复杂的,往往需要一个总业务层,而后牵扯到若干个子业务层。
数据层(数据访问层(Data Access Object)DAO),是专门进行数据库的原子性操作,在数据层之中最需要控制的是JDBC中的PreparedStatement接口的使用;
业务层(业务对象Business Object BO)也可以被称为服务层(Service),业务层的核心目的是调用多个数据层的操作以完成整体的项目设计,这个是整个项目的核心所在。
案例分析
使用emp表,实现以下操作:
结论:用户所提供的所有的需求(功能)都应该划分为业务层,而开发人员必须通过业务层去进行数据层的设计。
首先定义项目名称DAOproject,并配置MySQL数据库。并且保证数据库已经打开。
为了方便程序的统一管理,所有的项目的父包名称为cn.ren。而子包根据不同的功能模块进行划分。
定义数据库连接类
需要进行数据库的开发,就需要取得数据库连接的取得与关闭才能正常工作,那么几乎所有的数据库的连接操作都是固定的步骤。那么就可单独定义一个DataBaseConnection,这个类主要实现连接对象的取得和数据库的关闭操作,保存在dbc子包中。下图是Oracle的图,MySQL一样。
范例:定义数据库连接
package cn.ren.dbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 本类专门负责数据库的连接与关闭操作,所以在本类构造方法里面需要进行数据库驱动加载和数据连接取得。
* @author ren
*
*/
public class DataBaseConnection {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "***********" ;
private Connection conn = null;
/**
* 在构造方法里面为conn对象进行实例化,可以直接取得数据库的连接对象
* 由于所有的操作都是基于数据库完成的,如果数据库得不到连接,那么就意味着所有的操作都可以停止了
*/
public DataBaseConnection() {
try {
Class.forName(DATABASE_DRIVER);
this.conn = DriverManager.getConnection(DATABASE_URL,DATABASE_USER,DATABASE_PASSWORD);
} catch (Exception e) { // 虽然此处有异常但是抛出的意义不大
e.printStackTrace();
}
}
/**
* 取得一个数据库的连接对象
* @return Connection实例化对象
*/
public Connection getConnection() {
return this.conn ;
}
/**
* 负责数据库的关闭
*/
public void close() {
try {
this.conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
在本程序之之中,DataBaseConnection只是无条件的提供数据库的连接,而至于说有多少个线程获取连接对象它并不关心。
补充:在以前的DAO设计模式之中还会考虑多数据库之间的移植问题,结构如下:
在实际的开发之中,所提供的第三方平台越来越完善了,所以以上的设计被慢慢忽略了,也不会再用了。
开发Value Object
现在的程序严格来说已经给出了四个层次。不同层次之间一定要进行数据的传递,但是既然要操作的是指定的数据表,所以数据的结构必须要与表的结构一一对应,那么就使用简单Java类。
在实际工作之中,针对简单Java类的开发提供如下要求:
- 考虑到日后可能出现的分布式应用问题,所以简单Java类必须要实现java.io.Serializable接口;
- 简单Java类名称需要与数据表的名称一一对应
- 类中属性不允许使用基本数据类型,所有的基本数据类型都必须使用包装类,因为包装类的默认值为null;
- 类中的属性必须使用private进行封装,并且提供setter、getter;
- 类中可以定义多个构造方法,但是必须保留一个无参构造方法;
- 【可选要求】覆写equals()、 toString()、hashCode()
将所有的简单Java类保存在vo包中
范例:定义Emp.java
package cn.ren.vo;
import java.io.Serializable;
import java.util.Date;
@SuppressWarnings("serial")
public class Emp implements Serializable {
private Integer empno ;
private String ename ;
private String job ;
private Date hiredate ;
private Double sal ;
private Double comm ;
public Emp(){}
// setter getter 不复制了
}
不管多少张表,只要是实体表,那么一定要写简单Java类,而且不要试图一次性将所有表转换,随用随转。
开发数据层
数据层最终交给业务层进行调用,所以业务层必须知道数据层的执行标准,即:业务层必须明确知道数据层的操作方法,但是不需要知道它的具体实现。不同层之间的访问一定要有标准存在。
开发数据层操作标准
不同层之间要进行访问,一定要定义接口,描述操作访问。
对于数据层的接口给出如下的开发要求:
-
数据层既然进行数据操作的,那么将其保存在dao包下;
-
不同的数据表操作有可能使用不同的数据层开发,那么就针对于数据表进行命名
|- emp表,那么数据层的接口就应该定义为IEmpDAO;
-
对于整个数据层的开发严格来讲就只有两类功能:
|- 数据更新:建议它的操作方法以doXxx()形式命名,例如:doCreate()、doUpdate、doRemove();
|- 数据查询:对于数据查询分为两种形式:
\- 查询表中数据:以findXxx()形式命名,例如:findById() findByName() findAll();
\- 统计表中数据,以getXxx()形式命名,例如:getAllCount();
范例:定义IEmpDAO接口
package cn.ren.dao;
import java.util.List;
import java.util.Set;
import cn.ren.vo.Emp;
/**
* 定义数据层的开发标准
* @author ren
*
*/
public interface IEmpDAO {
/**
* 实现数据的增加操作
* @param vo 包含要增加的vo对象
* @return 数据保存成功返回true,否则返回false;
* @throws Exception SQL执行异常;
*/
public boolean doCreate(Emp vo) throws Exception ;
/**
* 实现数据的修改操作,本次修改是更具id进行全部字段的修改
* @param vo 包含要修改的信息,一定要提供有 ID内容
* @return 数据修改成功返回true,否则返回false
* @throws Exception SQL执行异常
*/
public boolean doUpdate(Emp vo) throws Exception ;
/**
* 执行数据的批量删除操作,所有要删除的数据以Set集合形式保存
* @param ids 包含有所要删除数据的id,不包含重复内容
* @return 删除成功返回true(删除数据的个数与要删除的数据个数相同),否则返回false ;
* @throws Exception SQL执行异常
*/
public boolean doRemoveBatch(Set<Integer> ids) throws Exception ;
/**
* 根据雇员编号,查询指定的信息
* @param id 要查询的雇员编号
* @return 如果雇员的信息存在,则以VO对象的形式返回,如果雇员信息不存在,则返回null
* @throws Exception SQL执行异常
*/
public Emp findById(Integer id) throws Exception ;
/**
* 查询指定数据表的全部数据,以集合的形式返回
* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,<br>
* 如果没有数据集合的长度为0 (size() == 0)
* @throws Exception SQL执行异常
*/
public List<Emp> findAll() throws Exception;
/**
* 分页进行数据的模糊查询,查询的结果以集合的形式返回
* @param currentPage 当前所在的页
* @param lineSize 每页显示的数据行数
* @param column 要进行模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,<br>
* 如果没有数据集合的长度为0 (size() == 0)
* @throws Exception SQL执行异常
*/
public List<Emp> findAllSplit(Integer currentPage, Integer lineSize, String column, String keyword) throws Exception ;
/**
* 进行模糊查询数据量的统计,如果表中没有记录统计的结果就是0
* @param column 要进行模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 返回表中的数据量,如果没有数据返回0
* @throws Exception SQL执行异常
*/
public Integer getAllCountt(String column, String keyword) throws Exception;
}
数据层的实现类
数据层需要被业务层调用,数据层需要进行数据库的连接(PreparedStatments()),由于在开发之中一个业务层操作需要执行多个数据层的调用,所以数据库的打开与关闭操作,应该由业务层控制会比较合理。
所有的数据层的实现类保存在dao.impl子包下。
范例:EmpDAOImpl子类
package cn.ren.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import cn.ren.dao.IEmpDAO;
import cn.ren.vo.Emp;
public class EmpDAOImpl implements IEmpDAO {
private Connection conn ; // 需要利用Connection对象操作
private PreparedStatement psmt ;
/**
* 如果想要使用数据层进行原子性操作,必须提供有Connection接口对象<br>
* 另外,业务层需要调用数据层,所以数据库的打开和关闭交由业务层进行处理
* @param conn 表示数据库的连接对象
*/
public EmpDAOImpl(Connection conn) {
this.conn = conn;
}
@Override
public boolean doCreate(Emp vo) throws Exception {
String sql = "insert into emp(empno,ename,job,hiredate,sal,comm) values (?,?,?,?,?,?)" ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setInt(1, vo.getEmpno());
this.psmt.setString(2, vo.getEname());
this.psmt.setString(3, vo.getJob());
this.psmt.setDate(4, new java.sql.Date(vo.getHiredate().getTime()));
this.psmt.setDouble(5,vo.getSal());
this.psmt.setDouble(6, vo.getComm());
return this.psmt.executeUpdate() > 0;
}
@Override
public boolean doUpdate(Emp vo) throws Exception {
String sql = "update emp set ename=?,job=?,hiredate=?,sal=?,comm=? where empno=?";
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setString(1, vo.getEname());
this.psmt.setString(2, vo.getJob());
this.psmt.setDate(3, new java.sql.Date(vo.getHiredate().getTime()));
this.psmt.setDouble(4,vo.getSal());
this.psmt.setDouble(5, vo.getComm());
this.psmt.setInt(6, vo.getEmpno());
return this.psmt.executeUpdate() > 0;
}
@Override
public boolean doRemoveBatch(Set<Integer> ids) throws Exception {
if (ids == null || ids.size() == 0) {
return false ;
}
StringBuffer sql = new StringBuffer() ;
sql.append("delete from emp where empno in (") ;
Iterator<Integer> iter = ids.iterator() ;
while(iter.hasNext()) {
sql.append(iter.next());
sql.append(",") ;
}
sql.delete(sql.length()-1, sql.length()).append(")") ;
this.psmt = this.conn.prepareStatement(sql.toString());
return this.psmt.executeUpdate() == ids.size();
}
@Override
public Emp findById(Integer id) throws Exception {
Emp vo = null;
String sql = "select empno,ename,job,hiredate,sal,comm from emp where empno=?" ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setInt(1, id);
ResultSet rs = this.psmt.executeQuery() ;
if (rs.next()) {
vo = new Emp() ;
vo.setEmpno(rs.getInt(1));
vo.setEname(rs.getString(2));
vo.setJob(rs.getString(3));
vo.setHiredate(rs.getDate(4));
vo.setSal(rs.getDouble(5));
vo.setComm(rs.getDouble(6));
}
return vo;
}
@Override
public List<Emp> findAll() throws Exception {
List<Emp> all = new ArrayList<Emp>();
String sql = "select empno,ename,job,hiredate,sal,comm from emp" ;
this.psmt = this.conn.prepareStatement(sql) ;
ResultSet rs = this.psmt.executeQuery() ;
while(rs.next()) {
Emp vo = new Emp() ;
vo.setEmpno(rs.getInt(1));
vo.setEname(rs.getString(2));
vo.setJob(rs.getString(3));
vo.setHiredate(rs.getDate(4));
vo.setSal(rs.getDouble(5));
vo.setComm(rs.getDouble(6));
all.add(vo) ;
}
return all;
}
@Override
public List<Emp> findAllSplit(Integer currentPage, Integer lineSize, String column, String keyword)
throws Exception { // 分页
List<Emp> all = new ArrayList<Emp>();
String sql = " select empno,ename,job,hiredate,sal,comm from emp "
+ " where ? like ? limit ?,? " ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setString(1, column);
this.psmt.setString(2, "%" + keyword + "%");
this.psmt.setInt(3, (currentPage-1)*lineSize);
this.psmt.setInt(4, lineSize);
ResultSet rs = this.psmt.executeQuery() ;
while(rs.next()) {
Emp vo = new Emp() ;
vo.setEmpno(rs.getInt(1));
vo.setEname(rs.getString(2));
vo.setJob(rs.getString(3));
vo.setHiredate(rs.getDate(4));
vo.setSal(rs.getDouble(5));
vo.setComm(rs.getDouble(6));
all.add(vo) ;
}
return all;
}
@Override
public Integer getAllCountt(String column, String keyword) throws Exception {
String sql = "select count(empno) from emp where ? like ?" ;
this.psmt = this.conn.prepareStatement(sql);
this.psmt.setString(1,column);
this.psmt.setString(2, '%' + keyword + '%');
ResultSet rs = this.psmt.executeQuery() ;
if (rs.next()) {
return rs.getInt(1);
}
return null;
}
}
子类之中需要注意的是构造方法需要接收一个Connection接口对象。
定义数据层工厂类——DAOFactory
业务层要想进行数据层的调用,那么就必须取得IEmpDAO接口对象,但是不同层之间如果要想取得接口对象实例,需要使用工厂设计模式,这个工厂类保存在factory子包下。
范例:定义工厂类
package cn.ren.factory;
import java.sql.Connection;
import cn.ren.dao.IEmpDAO;
import cn.ren.dao.impl.EmpDAOImpl;
public class DAOFactory {
public static IEmpDAO getIEmpDAOInstance(Connection conn) {
return new EmpDAOImpl(conn) ;
}
}
使用工厂的特征是外层不需要知道具体的子类
开发业务层
业务层是真正留给外部调用的,可能是控制层或者直接调用,既然业务层也是不同层之间的调用,所以业务层开发的首要任务就是定义业务层的操作标准。
开发业务层标准 -——IEmpService
业务层也可以称为Service层,既然描述的是emp表的操作,所以名称就定义为IEmpService,并且保存在Service子包下,但是对于业务层的方法定义并没有明确的要求。
范例:定义IEmpService操作标准
package cn.ren.service;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.ren.vo.Emp;
/**
* 定义emp表的业务层的执行标准 ,此类一定要负责数据库的打开与关闭操作
*
* @author ren
*
*/
public interface IEmpService {
/**
* 实现雇员数据的增加操作,本次操作调用IEmpDAO接口的如下方法:<br>
* <li>判断id是否存在,IEmpDAO.findById()
* <li>如果id不存在,调用IEmpDAO.doCreate()方法
*
* @param vo 包含要增加的数据对象
* @return id重复或者保存失败,返回false,否则返回true
* @throws Exception SQL执行异常
*/
public boolean insert(Emp vo) throws Exception;
/**
* 实现雇员数据的修改操作,调用IEmpDAO.doUpdate()方法,本次修改属于全部内容的一次修改
*
* @param vo 包含要修改 的vo对象
* @return 修改成功返回true。否则返回false
* @throws Exception SQL执行异常
*/
public boolean update(Emp vo) throws Exception;
/**
* 执行雇员的数据删除操作,可以删除多个雇员信息,调用IEmpDAO.doRemoveBatch()方法
* @param ids 包含全部要删除数据的集合,没有重复数据
* @return 删除成功返回true,否则返回false;
* @throws Exception
*/
public boolean delete(Set<Integer> ids) throws Exception ;
/**
* 根据id查找雇员完整信息,调用IEmpDAO.findById()方法
* @param id 要查找的雇员编号
* @return 如果找到了则雇员信息以vo对象返回,否则返回false
* @throws Exception
*/
public Emp get(int id) throws Exception ;
/**
* 查询全部雇员信息,调用的是IEmpDAO.findAll()方法
* @return 查询结果以list集合的形式返回,如果没有数据集合的长度为0
* @throws Exception
*/
public List<Emp> list() throws Exception ;
/**
* 实现数据的模糊查询与数据统计,调用IEmpDAO接口的两个方法:<br>
* <li> 调用IEmpDAO.findAllSplit()方法,查询出所有的表数据,返回List<Emp>;
* <li> 调用IEmpDAO.getAllCount()方法,查询所有的数据量,返回的Integer类型
* @param currentPage 当前所在页
* @param lineSize 每页显示的记录数
* @param column 模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 本方法需要返回多种数据类型,所以使用Map集合返回,由于类型不同一,所有的value设置为Object,返回内容如下:
* <li> key = allEmpsa, value = IEmpDAO.findAllSplit()返回结果,List<Emp> ;
* <li> key = empCount, value = IEmpDAO.getAllCount()返回结果,Integer
* @throws Exception
*/
public Map<String,Object> list(int currentPage, int lineSize, String column, String keyword) throws Exception ;
}
业务层的实现类
业务层实现类的核心功能:
-
负责数据库的打开与关闭,当存在了业务层对象后其目的就是为了操作数据库,即:业务层对象实例化之后就必须准备好数据库连接;
-
根据factoryDAO调用getIEmpDAOInstance()方法而后取得IEmpDAO接口对象。
-
业务层的实现类保存在dao.Impl子包中
范例:定义EmpServiceImpl子类
package cn.ren.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.ren.dbc.DataBaseConnection;
import cn.ren.factory.DAOFactory;
import cn.ren.service.IEmpService;
import cn.ren.vo.Emp;
public class EmpServiceImpl implements IEmpService{
// 在这个类的对象内部就提供一个数据库连接类的实例化对象
private DataBaseConnection dbc = new DataBaseConnection() ;
@Override
public boolean insert(Emp vo) throws Exception {
try {
// 要增加雇员编号如果不存在,则findById返回的结果就是null,NULL表示可以进行新雇员数据的保存
if(DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(vo.getEmpno()) == null) {
return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doCreate(vo) ;
}
return false ;
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public boolean update(Emp vo) throws Exception {
try {
return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doUpdate(vo) ;
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public boolean delete(Set<Integer> ids) throws Exception {
try {
return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).doRemoveBatch(ids) ;
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public Emp get(int id) throws Exception {
try {
return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findById(id);
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public List<Emp> list() throws Exception {
try {
return DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findAll() ;
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public Map<String, Object> list(int currentPage, int lineSize, String column, String keyword) throws Exception {
try {
Map<String,Object> map = new HashMap<String,Object>() ;
map.put("allEmps", DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).findAllSplit(currentPage, lineSize, column, keyword));
map.put("empCount", DAOFactory.getIEmpDAOInstance(this.dbc.getConnection()).getAllCountt(column, keyword));
return map ;
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
}
不同层之间的访问依靠的是工厂类和接口进行操作。
定义业务层的工厂类——ServiceFactory
业务层需要被其它层所使用,所以需要为其定义工厂类,该类也保存在factory中,但是实际开发中,业务层应该分为两种:
- 前台业务逻辑:将其保存在service.font包中,工厂类:ServiceFontFactory
- 后台业务逻辑:将其保存在service.back包中,工厂类:ServiceBackFactory
范例:定义ServiceFactory
package cn.ren.factory;
import cn.ren.service.IEmpService;
import cn.ren.service.impl.EmpServiceImpl;
public class ServiceFactory {
public static IEmpService getIEmpServiceInstance() {
return new EmpServiceImpl() ;
}
}
在实际编写之中,子类永远是不可见的,同时在整个操作里面,控制层完全看不到数据库的操作(没有任何的JDBC代码)。
代码测试
因为最终的业务层是需要用户去调用的,所以测试分为两种。
第一种调用测试
按照传统的方式产生对象而后调用里面的方法进行操作。保存在test子包之中
范例:测试增加操作
package cn.ren.test;
import java.util.Date;
import cn.ren.factory.ServiceFactory;
import cn.ren.vo.Emp;
public class TestEmpInsert {
public static void main(String[] args) {
Emp vo = new Emp() ;
vo.setEmpno(5757);
vo.setEname("老弟");
vo.setJob("程序员");
vo.setHiredate(new Date());
vo.setSal(100.00);
vo.setComm(1.00);
try {
System.out.println(ServiceFactory.getIEmpServiceInstance().insert(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
}
整个操作流程客户端的调用非常的容易,不需要涉及具体的数据存储细节。
范例:测试分页查询
package cn.ren.test;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import cn.ren.factory.ServiceFactory;
import cn.ren.vo.Emp;
public class TestEmpSplit {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Map<String, Object> map;
try {
map = ServiceFactory.getIEmpServiceInstance().list(1, 5, "ename", "");
int count = (Integer) map.get("empCount");
System.out.println("数据量是:" + count);
List<Emp> all = (List<Emp>) map.get("allEmps");
Iterator<Emp> iter = all.iterator() ;
while (iter.hasNext()) {
Emp vo = iter.next() ;
System.out.println("姓名:" + vo.getEname() + "、职位:" + vo.getJob());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里的模糊查询并没有作用。
第二种利用junit进行测试
1、选择要进行测试的类或者接口,这里选择IEmpService接口进行测试
范例:编写测试代码
package cn.ren.test.junit;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.junit.jupiter.api.Test;
import cn.ren.factory.ServiceFactory;
import cn.ren.vo.Emp;
import junit.framework.TestCase;
class IEmpServiceTest {
private static int empno ;
static {
empno = new Random().nextInt(10000); // 动态生成一个empno数据
}
@Test
void testInsert() {
Emp vo = new Emp() ;
vo.setEmpno(empno);
vo.setEname("老弟-" + empno);
vo.setJob("程序员-" + empno);
vo.setHiredate(new Date());
vo.setSal(100.00);
vo.setComm(1.00);
try {
TestCase.assertTrue(ServiceFactory.getIEmpServiceInstance().insert(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testUpdate() {
Emp vo = new Emp() ;
vo.setEmpno(5757);
vo.setEname("老弟");
vo.setJob("失业");
vo.setHiredate(new Date());
vo.setSal(100.00);
vo.setComm(1.00);
try {
TestCase.assertTrue(ServiceFactory.getIEmpServiceInstance().update(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testDelete() {
Set<Integer> ids = new HashSet<Integer>();
ids.add(5757);
try {
TestCase.assertTrue(ServiceFactory.getIEmpServiceInstance().delete(ids));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testGet() {
try {
TestCase.assertNotNull(ServiceFactory.getIEmpServiceInstance().get(7369));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testList() {
try {
TestCase.assertTrue( ServiceFactory.getIEmpServiceInstance().list().size() > 0);
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
@Test
void testListIntIntStringString() {
Map<String, Object> map;
try {
map = ServiceFactory.getIEmpServiceInstance().list(1, 5, "ename", "");
int count = (Integer) map.get("empCount");
List<Emp> all = (List<Emp>) map.get("allEmps");
TestCase.assertTrue(count > 0 && all.size() > 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
此时测试通过,就可以让用户去调用了。
实现部门操作
1、定义Dept.java类
package cn.ren.vo;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Dept implements Serializable {
private Integer deptno;
private String dname ;
private String loc ;
// setter getter 没复制
}
2、定义IDeptDAO接口
需要考虑一个问题,几乎所有的表都要进行CRUD功能(增加、修改全部、删除数据、根据编号查询、查询全部、分页显示、数据统计),那么这些功能的方法在每个接口都要定义,那么是否可以进行改进?在整个DAO接口定义的过程之中,不同表的区别在于:VO类、主键类型。那么为了解决重复问题,可以使用泛型接口的继承操作。
package cn.ren.dao;
import java.util.List;
import java.util.Set;
/**
* 定义公共的 DAO操作标准,基本核心的功能包括:增加、修改全部、删除数据、根据编号查询、查询全部、分页显示、数据统计
* @author ren
* @param <K> 表示要操作的主键类型,有子接口实现
* @param <V> 表示要操作的vo类型,由子接口实现
*/
public interface IDAO<K,V> {
/**
* 实现数据的增加操作
* @param vo 包含要增加的vo对象
* @return 数据保存成功返回true,否则返回false;
* @throws Exception SQL执行异常;
*/
public boolean doCreate(V vo) throws Exception ;
/**
* 实现数据的修改操作,本次修改是更具id进行全部字段的修改
* @param vo 包含要修改的信息,一定要提供有 ID内容
* @return 数据修改成功返回true,否则返回false
* @throws Exception SQL执行异常
*/
public boolean doUpdate(V vo) throws Exception ;
/**
* 执行数据的批量删除操作,所有要删除的数据以Set集合形式保存
* @param ids 包含有所要删除数据的id,不包含重复内容
* @return 删除成功返回true(删除数据的个数与要删除的数据个数相同),否则返回false ;
* @throws Exception SQL执行异常
*/
public boolean doRemoveBatch(Set<K> ids) throws Exception ;
/**
* 根据雇员编号,查询指定的信息
* @param id 要查询的雇员编号
* @return 如果雇员的信息存在,则以VO对象的形式返回,如果雇员信息不存在,则返回null
* @throws Exception SQL执行异常
*/
public V findById(K id) throws Exception ;
/**
* 查询指定数据表的全部数据,以集合的形式返回
* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,<br>
* 如果没有数据集合的长度为0 (size() == 0)
* @throws Exception SQL执行异常
*/
public List<V> findAll() throws Exception;
/**
* 分页进行数据的模糊查询,查询的结果以集合的形式返回
* @param currentPage 当前所在的页
* @param lineSize 每页显示的数据行数
* @param column 要进行模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,<br>
* 如果没有数据集合的长度为0 (size() == 0)
* @throws Exception SQL执行异常
*/
public List<V> findAllSplit(Integer currentPage, Integer lineSize, String column, String keyword) throws Exception ;
/**
* 进行模糊查询数据量的统计,如果表中没有记录统计的结果就是0
* @param column 要进行模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 返回表中的数据量,如果没有数据返回0
* @throws Exception SQL执行异常
*/
public Integer getAllCountt(String column, String keyword) throws Exception;
}
范例:定义IEmpDAO子接口
package cn.ren.dao;
import cn.ren.vo.Emp;
/**
* 定义数据层的开发标准
* @author ren
*
*/
public interface IEmpDAO extends IDAO<Integer,Emp>{
}
范例:定义IDeptDAO子接口
package cn.ren.dao;
import cn.ren.vo.Dept;
public interface IDeptDAO extends IDAO<Integer,Dept>{
}
3、定义DeptDAOImpl子类
package cn.ren.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import cn.ren.dao.IDeptDAO;
import cn.ren.vo.Dept;
public class DeptDAOImpl implements IDeptDAO {
private Connection conn ;
private PreparedStatement psmt ;
public DeptDAOImpl(Connection conn) {
this.conn = conn ;
}
@Override
public boolean doCreate(Dept vo) throws Exception {
String sql = "insert into dept(deptno,dname,loc) values (?,?,?) " ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setInt(1, vo.getDeptno());
this.psmt.setString(2, vo.getDname());
this.psmt.setString(3, vo.getLoc());
return this.psmt.executeUpdate() > 0;
}
@Override
public boolean doUpdate(Dept vo) throws Exception {
String sql = "update dept set dname=?,loc=? where deptno=? " ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setString(1, vo.getDname());
this.psmt.setString(2, vo.getLoc());
this.psmt.setInt(3, vo.getDeptno());
return this.psmt.executeUpdate() > 0;
}
@Override
public boolean doRemoveBatch(Set<Integer> ids) throws Exception {
if (ids == null || ids.size() == 0) {
return false ;
}
StringBuffer sql = new StringBuffer() ;
sql.append("delete from dept where deptno in (") ;
Iterator<Integer> iter = ids.iterator() ;
while(iter.hasNext()) {
sql.append(iter.next());
sql.append(",") ;
}
sql.delete(sql.length()-1, sql.length()).append(")") ;
this.psmt = this.conn.prepareStatement(sql.toString());
return this.psmt.executeUpdate() == ids.size();
}
@Override
public Dept findById(Integer id) throws Exception {
Dept vo = null;
String sql = "select deptno,dname,loc from dept where deptno=?" ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setInt(1, id);
ResultSet rs = this.psmt.executeQuery() ;
if (rs.next()) {
vo = new Dept() ;
vo.setDeptno(rs.getInt(1));
vo.setDname(rs.getString(2));
vo.setLoc(rs.getString(3));
}
return vo;
}
@Override
public List<Dept> findAll() throws Exception {
List<Dept> all = new ArrayList<Dept>();
String sql = "select deptno,dname,loc from dept" ;
this.psmt = this.conn.prepareStatement(sql) ;
ResultSet rs = this.psmt.executeQuery() ;
while(rs.next()) {
Dept vo = new Dept() ;
vo.setDeptno(rs.getInt(1));
vo.setDname(rs.getString(2));
vo.setLoc(rs.getString(3));
all.add(vo) ;
}
return all;
}
@Override
public List<Dept> findAllSplit(Integer currentPage, Integer lineSize, String column, String keyword)
throws Exception {
List<Dept> all = new ArrayList<Dept>();
String sql = " select deptno,dname,loc from dept"
+ " where ? like ? limit ?,? " ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setString(1, column);
this.psmt.setString(2, "%" + keyword + "%");
this.psmt.setInt(3, (currentPage-1)*lineSize);
this.psmt.setInt(4, lineSize);
ResultSet rs = this.psmt.executeQuery() ;
while(rs.next()) {
Dept vo = new Dept() ;
vo.setDeptno(rs.getInt(1));
vo.setDname(rs.getString(2));
vo.setLoc(rs.getString(3));
all.add(vo) ;
}
return all;
}
@Override
public Integer getAllCountt(String column, String keyword) throws Exception {
String sql = "select count(deptno) from dept where ? like ?" ;
this.psmt = this.conn.prepareStatement(sql);
this.psmt.setString(1,column);
this.psmt.setString(2, '%' + keyword + '%');
ResultSet rs = this.psmt.executeQuery() ;
if (rs.next()) {
return rs.getInt(1);
}
return null;
}
}
明显发现操作功能的代码和EmpDAOImpl子类的代码相似度大,可以利用反射的机制来改进
4、修改DAOFactoryctory类增加新的接口对象的取得方法
package cn.ren.factory;
import java.sql.Connection;
import cn.ren.dao.IDeptDAO;
import cn.ren.dao.IEmpDAO;
import cn.ren.dao.impl.DeptDAOImpl;
import cn.ren.dao.impl.EmpDAOImpl;
public class DAOFactory {
public static IEmpDAO getIEmpDAOInstance(Connection conn) {
return new EmpDAOImpl(conn) ;
}
public static IDeptDAO getIDeptDAOInstance(Connection conn) {
return new DeptDAOImpl(conn);
}
}
5、开发IDeptService接口
package cn.ren.service;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.ren.vo.Dept;
public interface IDeptService {
public boolean insert(Dept vo) throws Exception;
public boolean update(Dept vo) throws Exception;
public boolean delete(Set<Integer> ids) throws Exception ;
public Dept get(int id) throws Exception ;
public List<Dept> list() throws Exception ;
public Map<String,Object> list(int currentPage, int lineSize, String column, String keyword) throws Exception ;
}
6、实现IDeptService接口的子类DeptServiceerviceImpl;
package cn.ren.service.impl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.ren.dbc.DataBaseConnection;
import cn.ren.factory.DAOFactory;
import cn.ren.service.IDeptService;
import cn.ren.vo.Dept;
public class DeptServiceImpl implements IDeptService {
private DataBaseConnection dbc = new DataBaseConnection() ;
@Override
public boolean insert(Dept vo) throws Exception {
try {
if(DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findById(vo.getDeptno()) == null) {
return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doCreate(vo);
}
return false;
} catch (Exception e) {
throw e;
} finally {
this.dbc.close();
}
}
@Override
public boolean update(Dept vo) throws Exception {
try {
return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doUpdate(vo);
} catch (Exception e) {
throw e;
} finally {
this.dbc.close();
}
}
@Override
public boolean delete(Set<Integer> ids) throws Exception {
try {
return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).doRemoveBatch(ids);
} catch (Exception e) {
throw e ;
} finally {
this.dbc.close();
}
}
@Override
public Dept get(int id) throws Exception {
try {
return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findById(id) ;
} catch (Exception e) {
throw e;
} finally {
this.dbc.close();
}
}
@Override
public List<Dept> list() throws Exception {
try {
return DAOFactory.getIDeptDAOInstance(this.dbc.getConnection()).findAll() ;
} catch (Exception e) {
throw e;
} finally {
this.dbc.close();
}
}
}
7、修改服务层工厂
package cn.ren.factory;
import cn.ren.service.IDeptService;
import cn.ren.service.IEmpService;
import cn.ren.service.impl.DeptServiceImpl;
import cn.ren.service.impl.EmpServiceImpl;
public class ServiceFactory {
public static IEmpService getIEmpServiceInstance() {
return new EmpServiceImpl() ;
}
public static IDeptService getIDeptServiceInstance() {
return new DeptServiceImpl() ;
}
}
在使用之前使用测试
package cn.ren.test;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Test;
import cn.ren.factory.ServiceFactory;
import cn.ren.vo.Dept;
import junit.framework.TestCase;
class IDeptServiceTest {
@Test
void testInsert() {
Dept vo = new Dept();
vo.setDeptno(11);
vo.setDname("财务部");
vo.setLoc("上海");
try {
TestCase.assertTrue(ServiceFactory.getIDeptServiceInstance().insert(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testUpdate() {
Dept vo = new Dept();
vo.setDeptno(11);
vo.setDname("上部");
vo.setLoc("上海");
try {
TestCase.assertTrue(ServiceFactory.getIDeptServiceInstance().update(vo));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testDelete() {
Set<Integer> ids = new HashSet<Integer>() ;
ids.add(11);
try {
TestCase.assertTrue(ServiceFactory.getIDeptServiceInstance().delete(ids));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testGet() {
try {
TestCase.assertNotNull(ServiceFactory.getIDeptServiceInstance().get(10));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void testList() {
try {
TestCase.assertTrue(ServiceFactory.getIDeptServiceInstance().list().size() > 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
处理关系
已经实现了雇员和部门的基础操作,但是在雇员里面需要有mgr和deptno两个关联字段。
1、修改VO类的定义
修改Emp.java类,增加如下代码:
private Emp mgr ;
private Dept dept ;
public void setMgr(Emp mgr) {
this.mgr = mgr;
}
public Emp getMgr() {
return mgr;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public Dept getDept() {
return dept;
}
修改Dept.java类,增加如下代码:
private List<Emp> emps ;
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
public List<Emp> getEmps() {
return emps;
}
2、修改EmpDAOImpl子类
增加数据时需要考虑到雇员的领导和部门编号
@Override
public boolean doCreate(Emp vo) throws Exception {
String sql = "insert into emp(empno,ename,job,hiredate,sal,comm,mgr,deptno) values (?,?,?,?,?,?,?,?)" ;
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setInt(1, vo.getEmpno());
this.psmt.setString(2, vo.getEname());
this.psmt.setString(3, vo.getJob());
this.psmt.setDate(4, new java.sql.Date(vo.getHiredate().getTime()));
this.psmt.setDouble(5,vo.getSal());
this.psmt.setDouble(6, vo.getComm());
if (vo.getMgr() == null) {
this.psmt.setNull(7, Types.NULL);
} else {
this.psmt.setInt(7, vo.getMgr().getEmpno());
}
if (vo.getDept() == null) {
this.psmt.setNull(8, Types.NULL);
} else {
this.psmt.setInt(8, vo.getDept().getDeptno());
}
return this.psmt.executeUpdate() > 0;
}
修改数据时也要考虑变化
@Override
public boolean doUpdate(Emp vo) throws Exception {
String sql = "update emp set ename=?,job=?,hiredate=?,sal=?,comm=?, mgr=?, deptno=? where empno=?";
this.psmt = this.conn.prepareStatement(sql) ;
this.psmt.setString(1, vo.getEname());
this.psmt.setString(2, vo.getJob());
this.psmt.setDate(3, new java.sql.Date(vo.getHiredate().getTime()));
this.psmt.setDouble(4,vo.getSal());
this.psmt.setInt(6, vo.getEmpno());
if (vo.getMgr() == null) {
this.psmt.setNull(6, Types.NULL);
} else {
this.psmt.setInt(6, vo.getMgr().getEmpno());
}
if (vo.getDept() == null) {
this.psmt.setNull(7, Types.NULL);
} else {
this.psmt.setInt(7, vo.getDept().getDeptno());
}
this.psmt.setDouble(8, vo.getComm());
return this.psmt.executeUpdate() > 0;
}
在查询单个雇员信息的时候也需要进行全部内容的查询,也就是现在的查询里面需要定义新的功能,是为了与之前的单表查询区分开。扩充IEmpDAO方法;
package cn.ren.dao;
import java.util.List;
import cn.ren.vo.Emp;
/**
* 定义数据层的开发标准
* @author ren
*
*/
public interface IEmpDAO extends IDAO<Integer,Emp>{
/**
* 查询雇员的详情信息,包括雇员的领导信息及所在部门信息
* @param id 要查询的雇员编号
* @return 所有的数据以VO对象返回,没有返回NUll
* @throws Exception
*/
public Emp findByIdDetails(Integer id) throws Exception;
/**
* 查询雇员的完整信息
* @return 所有的数据对象,以List集合返回,如果没有数据集合长度为0;
* @throws EcWxception
*/
public List<Emp> findAllDetails() throws Exception ;
/**
* 分页查询雇员的完整信息
* @param currentPage 当前所在的页
* @param lineSize 每页显示的数据行数
* @param column 要进行模糊查询的数据列
* @param keyword 模糊查询的关键字
* @return 如果表中有数据,则所有的数据会封装为VO对象而后利用List集合返回,<br>
* 如果没有数据集合的长度为0 (size() == 0)
* @throws Exception SQL执行异常
*/
public List<Emp> findAllSplitDetails(Integer currentPage, Integer LinSize, String column, String keyword) throws Exception ;
}
服务层增加方法等:
测试结果:
以上还未配置部门到部员的关系,完整代码奉上