Java Spring MVC分层设计
第一次尝试着用Java做Web开发,使用了Java Spring框架,顺便说一句,如果使用Spring开发,建议使用STS(Spring Tool Suite) IDE,它很好的集成了Spring、Maven等框架,使用起来特别方便,尤其是第一次使用Spring框架进行开发,它极大的方便了开发人员,通过快捷菜单及可很简单的配置好Spring开发环境,自动下载、更新Maven依赖包。话不多讲,回到文章的正题。
Spring是一个在Java业界很流行的MVC框架,所谓MVC即模型-视图-控制器,将应用程序的逻辑层与展现层进行分离的一种设计模式。
- 模型(Model)代表数据控制器。数据的读取,插入,更新都是由模型来负责。
- 视图(View)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。
- 控制器(Controller)是模型,视图以及其他任何处理 HTTP 请求所必须的资源之前的中介
概述
一个典型的页面浏览行为在程序端的流程是这样的:
- 控制器最先被调用,并被赋予外部输入
- 控制器根据外部输入向模型请求数据
- 模型从数据库获取数据并发送数据到控制器
- 控制器处理该数据并发送封装好的数据到视图
- 视图根据接到的数据最终展示页面给用户浏览
使用Java进行MVC模式开发时,往往将数据模型分为两部分,即DAO(Data Access Object,数据访问对象)和Service(业务逻辑模型)。在第2步中,控制器向模型请求数据时,并不是直接向DAO请求数据,而是通过Service向DAO请求数据。这样做的好处是,可以将业务逻辑与数据库访问独立开,为将来系统更换数据保存介质(如目前系统使用文件系统存储数据,将来可以更换为使用数据库存储,又或者是现在使用了MSSQL存储数据,将来更换为Oracle或是Mysql等)提供了很大的灵活性。
下图给出了分层设计模型。控制器只需要调用Service接口中的方法获取或是处理数据,Service层对控制器传入的数据进行业务逻辑处理封装后,传给DAO层,由DAO层负责将处理后的数据写入数据库中。
在Service层使用了抽象工厂模式来实现Service层与DAO层的低耦合,Service层并不知道DAO层是如何实现的,实际上也不需要知道系统使用了哪种数据库或是文件系统。
在DAO层使用工厂模式来创建数据模型的实体对象。
Service层设计
接口代码,这里使用了泛型技巧,确保每个Service只处理一种数据类型。
- package com.emerson.etao.service;
- import java.sql.SQLException;
- import java.util.List;
- /**
- * 业务实现层接口
- *
- * @author Chris Mao(Zibing)
- *
- * @param <T>
- */
- public interface IBaseService<T> {
- /**
- * 将实体类对象持久化,写入到数据表中
- *
- * @param T
- * @return 返回新写入记录的自增ID
- * @throws SQLException
- */
- public long insert(T entity);
- /**
- * 根据Id值,将实体类数据回写到数据库
- *
- * @param id
- * @param T
- * @return 返回更新的记录笔数
- * @throws SQLException
- */
- public int update(long id, T entity);
- /**
- * 根据Id值从数据库中删除实体类数据
- *
- * @param id
- * @return 返回删除的记录笔数
- * @throws SQLException
- */
- public int delete(long id);
- /**
- * 根据Id查询具体的实体类信息,并返回实体类对象
- * 若查询不到数据则返回null
- *
- * @param id
- * @return T
- */
- public T getById(long id);
- /**
- * 获取列表
- *
- * @return List
- * @throws SQLException
- */
- public List<T> getAll();
- }
下面是具体的Service层接口代码,
- package com.emerson.etao.service.base;
- import java.util.List;
- import com.emerson.etao.entity.base.BusinessApp;
- import com.emerson.etao.entity.base.Communicator;
- import com.emerson.etao.entity.base.Customer;
- import com.emerson.etao.service.IBaseService;
- /**
- * 客服类操作接口
- *
- * @author Chris Mao(Zibing)
- * @param <T>
- *
- * @param <T>
- */
- public interface ICommunicatorService extends IBaseService<Communicator> {
- public List<Communicator> getAll(Customer customer);
- /**
- * 为客服分配商业应用
- *
- * @param c
- * @param appList
- * @see BusinessApp
- */
- public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists);
- /**
- * 为客服分配客户
- *
- * @param c
- * @param customerList
- * @see Customer
- */
- public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists);
- }
实现接口。
- /**
- *
- */
- package com.emerson.etao.service.imp;
- import java.sql.SQLException;
- import java.util.List;
- import com.emerson.etao.dao.IBaseDao;
- import com.emerson.etao.dao.IDaoFactory;
- import com.emerson.etao.service.IBaseService;
- /**
- * 业务层实现类基类
- *
- * 为了降低与数据访问层的耦合,在构造函数中传入DaoFactory接口用于创建具体的数据访问对象实例
- *
- * @author Chris Mao(Zibing)
- *
- */
- public abstract class BaseServiceImp<T> implements IBaseService<T> {
- private IBaseDao<T> dao = null;
- protected IBaseDao<T> getDao() {
- return this.dao;
- }
- /**
- *
- * @param factory 降低耦合,传入DaoFactory接口
- * @see IDaoFactory
- */
- public BaseServiceImp(IDaoFactory<T> factory) {
- super();
- this.dao = factory.getDao();
- }
- @Override
- public long insert(T entity) {
- try {
- return this.getDao().insert(entity);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return 0;
- }
- @Override
- public int update(long id, T entity) {
- try {
- return this.getDao().update(id, entity);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return 0;
- }
- @Override
- public int delete(long id) {
- try {
- return this.getDao().delete(id);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return 0;
- }
- @Override
- public List<T> getAll() {
- try {
- return this.getDao().getAll();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return null;
- }
- @Override
- public T getById(long id) {
- try {
- return this.getDao().getById(id);
- } catch (SQLException e1) {
- e1.printStackTrace();
- }
- return null;
- }
- }
- package com.emerson.etao.service.base.imp;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.Iterator;
- import java.util.List;
- import com.emerson.etao.dao.IDaoFactory;
- import com.emerson.etao.entity.base.BusinessApp;
- import com.emerson.etao.entity.base.Communicator;
- import com.emerson.etao.entity.base.Customer;
- import com.emerson.etao.service.base.ICommunicatorService;
- import com.emerson.etao.service.imp.BaseServiceImp;
- /**
- *
- * @author Chris Mao(Zibing)
- *
- */
- public class CommunicatorServiceImp extends BaseServiceImp<Communicator>implements ICommunicatorService {
- @Override
- public List<Communicator> getAll(Customer customer) {
- List<Communicator> result = new ArrayList<Communicator>();
- try {
- result = this.getDao()
- .getAll(“SELECT a.* FROM communicator AS a INNER JOIN customer_communicator cc USING(communicator_id) WHERE cc.customer_id = ”
- + customer.getCustomerId());
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return result;
- }
- public CommunicatorServiceImp(IDaoFactory<Communicator> factory) {
- super(factory);
- }
- @Override
- public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists) {
- try {
- if (true == deleteExists) {
- this.getDao().getStatement().executeUpdate(“DELETE FROM communicator_application WHERE communicator_id = ” + communicator.getCommunicatorId());
- }
- if (null == appList || appList.isEmpty()) {
- return;
- }
- PreparedStatement pstmt = this.getDao().getConnection().prepareStatement(“INSERT IGNORE INTO communicator_application(communicator_id, application_id, created_time) VALUES(?, ?, ?)”);
- BusinessApp app = null;
- SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
- Iterator<BusinessApp> ite = appList.iterator();
- while (ite.hasNext()) {
- app = ite.next();
- pstmt.setLong(1, communicator.getCommunicatorId());
- pstmt.setLong(2, app.getApplicationId());
- pstmt.setString(3, sdf.format(new Date()));
- pstmt.executeUpdate();
- }
- pstmt.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- /**
- * 为客服人员分配客户
- *
- * 如果需要删除客服人员名下所有客户,只需将customers设为null或是空列表
- *
- * @param communicator
- * @param apps
- * @param deleteExists
- */
- @Override
- public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists) {
- try {
- if (true == deleteExists) {
- this.getDao().getStatement().executeQuery(“DELETE FROM customer_communicator WHERE communicator_id = ” + communicator.getCommunicatorId());
- }
- if (null == customerList || customerList.isEmpty()) {
- return;
- }
- PreparedStatement pstmt = this.getDao().getConnection().prepareStatement(“INSERT IGNORE INTO customer_communicator(communicator_id, customer_id, created_time) VALUES(?, ?, ?)”);
- Customer customer = null;
- SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
- Iterator<Customer> ite = customerList.iterator();
- while (ite.hasNext()) {
- customer = ite.next();
- pstmt.setLong(1, communicator.getCommunicatorId());
- pstmt.setLong(2, customer.getCustomerId());
- pstmt.setString(3, sdf.format(new Date()));
- pstmt.executeUpdate();
- }
- pstmt.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
DAO层的设计
这里需在为DAO层定义一个通个的基础接口IBaseDao,这里包含了对数据的增、删、改、查基础操作。抽象类BaseDao实现接口IBaseDao,并添加了访问限制为protected的数据库连接对象,方便子类使用。
DAO接口代码。
- /**
- *
- */
- package com.emerson.etao.dao;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.List;
- /**
- *
- * 数据访问层接口
- *
- * @author Chris Mao(Zibing)
- *
- */
- public interface IBaseDao<T> {
- /**
- *
- * @return Connection
- */
- public Connection getConnection();
- /**
- *
- * @return Statement
- */
- public Statement getStatement();
- /**
- * 将值对象写入到数据表中,并返回其自增ID值
- *
- * @param entity
- * @return 返回新写入记录的自增ID
- * @throws SQLException
- */
- public long insert(T entity) throws SQLException;
- /**
- * 将值对象修改后的内容写入到数据表中,并返回其影响的记录笔数
- *
- * @param id
- * @param entity
- * @return 返回更新的记录笔数
- * @throws SQLException
- */
- public int update(long id, T entity) throws SQLException;
- /**
- * 删除ID值,并返回其删除的记录笔数
- *
- * @param id
- * @return 返回删除的记录笔数
- * @throws SQLException
- */
- public int delete(long id) throws SQLException;
- /**
- * 依据Id值到数据表中查询数据,并返回值对象
- *
- * @param id
- * @return
- */
- public T getById(long id) throws SQLException;
- /**
- * 返回数据表中所有记录
- *
- * @return List<T>
- * @throws SQLException
- */
- public List<T> getAll() throws SQLException;
- /**
- * 返回符合条件的所有记录
- *
- * @param queryStr
- * @return List<T>
- * @throws SQLException
- */
- public List<T> getAll(String queryStr) throws SQLException;
- }
抽象工厂接口。
- package com.emerson.etao.dao;
- /**
- * 数据访问类工厂接口
- *
- * 负责创建具体的数据访问对象实例
- *
- * @author Chris Mao(Zibing)
- *
- * @param <T>
- */
- public interface IDaoFactory<T> {
- /**
- * 创建数据访问对象实例
- *
- * @return
- * @see IBaseDao
- */
- public IBaseDao<T> getDao();
- }
抽象类BaseDao实现接口IBaseDao。
- package com.emerson.etao.dao;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.emerson.etao.db.DBUtils;
- /**
- *
- * 数据访问层基类
- *
- * 所有数据访问对象都需要继承此类
- *
- * @author Chris Mao(Zibing)
- *
- */
- public abstract class BaseDao<T> implements IBaseDao<T> {
- private static final Logger logger = LoggerFactory.getLogger(BaseDao.class);
- public Connection getConnection() {
- return DBUtils.getConnection();
- }
- public Statement getStatement() {
- Statement stmt = null;
- try {
- Connection conn = DBUtils.getConnection();
- stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- } catch (SQLException e) {
- logger.error(“创建 Statement 对象发生错误!!”);
- e.printStackTrace();
- }
- return stmt;
- }
- }
实体类创建工厂接口。
- package com.emerson.etao.entity;
- import java.sql.ResultSet;
- /**
- *
- * 实体类工厂接口
- *
- * 所有实体类对象实例需要通过此工厂接口创建
- *
- * @author Chris Mao(Zibing)
- *
- */
- public interface IEntityFactory<T> {
- /**
- * 创建空的实体类
- *
- * @return
- */
- public T createEntity();
- /**
- * 创建实体类,并将参数rs中的内容赋值到实体类属性当中
- *
- * @param rs
- * @return
- */
- public T createEntity(ResultSet rs);
- }
具体的DAO对象,继承BaseDao,并实现实体类创建工厂接口。这里使用了内部匿名类实现DAO的抽象工厂接口。
- package com.emerson.etao.dao.base;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.List;
- import com.emerson.etao.dao.BaseDao;
- import com.emerson.etao.dao.IBaseDao;
- import com.emerson.etao.dao.IDaoFactory;
- import com.emerson.etao.entity.IEntityFactory;
- import com.emerson.etao.entity.base.Communicator;
- /**
- * 客服人员数据访问对象
- *
- * @author Chris Mao(Zibing)
- *
- */
- public class CommunicatorDao extends BaseDao<Communicator>implements IEntityFactory<Communicator> {
- public static IDaoFactory<Communicator> factory = new IDaoFactory<Communicator>() {
- @Override
- public IBaseDao<Communicator> getDao() {
- return new CommunicatorDao();
- }
- };
- @Override
- public Communicator createEntity() {
- return new Communicator();
- }
- @Override
- public Communicator createEntity(ResultSet rs) {
- Communicator c = this.createEntity();
- try {
- c.setCommunicatorId(rs.getInt(“communicator_id”));
- c.setCommunicatorName(rs.getString(“communicator_name”));
- c.setPhone(rs.getString(“phone”));
- c.setFax(rs.getString(“fax”));
- c.setEmail(rs.getString(“email”));
- c.setReportTo(rs.getInt(“report_to”));
- c.setReportToName(rs.getString(“report_to_name”));
- c.setValid(rs.getByte(“is_valid”));
- c.setCreatedTime(rs.getTimestamp(“created_time”));
- c.setUpdatedTime(rs.getTimestamp(“update_time”));
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return c;
- }
- @Override
- public Communicator getById(long id) throws SQLException {
- Communicator result = null;
- ResultSet rs = this.getStatement().executeQuery(“SELECT * FROM vw_communicator WHERE communicator_id = ” + id);
- while (rs.next()) {
- result = this.createEntity(rs);
- }
- rs.close();
- return result;
- }
- @Override
- public long insert(Communicator entity) throws SQLException {
- Long newId = (long) 0;
- StringBuilder sql = new StringBuilder();
- sql.append(“INSERT IGNORE INTO communicator”);
- sql.append(“(communicator_name, phone, fax, email, report_to, created_time) ”);
- sql.append(“VALUES(?, ? ,? ,?, ?, ?)”);
- PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
- pstmt.setString(1, entity.getCommunicatorName());
- pstmt.setString(2, entity.getPhone());
- pstmt.setString(3, entity.getFax());
- pstmt.setString(3, entity.getFax());
- pstmt.setString(4, entity.getEmail());
- pstmt.setInt(5, entity.getReportTo());
- SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);// 设置日期格式
- pstmt.setString(6, df.format(new Date()));
- pstmt.executeUpdate();
- ResultSet rs = pstmt.getGeneratedKeys();
- if (rs.next()) {
- newId = rs.getLong(1);
- entity.setCommunicatorId(rs.getInt(1));
- // System.out.println(“新增客服记录ID为:” + newId);
- }
- rs.close();
- pstmt.close();
- return newId;
- }
- @Override
- public int update(long id, Communicator entiry) throws SQLException {
- int result = 0;
- StringBuffer sql = new StringBuffer();
- Communicator c = (Communicator) entiry;
- // System.out.println(c);
- sql.append(“UPDATE communicator”);
- sql.append(“ SET communicator_name = ?, phone = ?, fax = ?, email = ?, report_to = ?, is_valid = ?”);
- sql.append(“ WHERE communicator_id = ?”);
- PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString());
- pstmt.setString(1, c.getCommunicatorName());
- pstmt.setString(2, c.getPhone());
- pstmt.setString(3, c.getFax());
- pstmt.setString(3, c.getFax());
- pstmt.setString(4, c.getEmail());
- pstmt.setInt(5, c.getReportTo());
- pstmt.setInt(6, c.getIsValid());
- pstmt.setLong(7, c.getCommunicatorId());
- result = pstmt.executeUpdate();
- // System.out.println(“更新客服记录数为:” + result);
- pstmt.close();
- return result;
- }
- @Override
- public int delete(long id) throws SQLException {
- int result = 0;
- String sql = ”DELETE FROM communicator WHERE communicator_id = ?”;
- PreparedStatement pstmt;
- pstmt = this.getConnection().prepareStatement(sql);
- pstmt.setLong(1, id);
- result = pstmt.executeUpdate();
- // System.out.println(“删除客服记录数为:” + result);
- pstmt.close();
- return result;
- }
- @Override
- public List<Communicator> getAll() throws SQLException {
- List<Communicator> result = null;
- ResultSet rs = this.getStatement().executeQuery(“SELECT * FROM vw_communicator”);
- result = new ArrayList<Communicator>();
- while (rs.next()) {
- result.add(this.createEntity(rs));
- }
- rs.close();
- return result;
- }
- @Override
- public List<Communicator> getAll(String queryStr) throws SQLException {
- List<Communicator> result = new ArrayList<Communicator>();
- ResultSet rs = this.getStatement().executeQuery(queryStr);
- while (rs.next()) {
- result.add(this.createEntity(rs));
- }
- rs.close();
- return result;
- }
- }