Java Spring MVC分层设计(转)

原文:https://blog.csdn.net/chris_mao/article/details/48694243

第一次尝试着用Java做Web开发,使用了Java Spring框架,顺便说一句,如果使用Spring开发,建议使用STS(Spring Tool Suite) IDE,它很好的集成了Spring、Maven等框架,使用起来特别方便,尤其是第一次使用Spring框架进行开发,它极大的方便了开发人员,通过快捷菜单及可很简单的配置好Spring开发环境,自动下载、更新Maven依赖包。话不多讲,回到文章的正题。

Spring是一个在Java业界很流行的MVC框架,所谓MVC即模型-视图-控制器,将应用程序的逻辑层与展现层进行分离的一种设计模式。

  • 模型(Model)代表数据控制器。数据的读取,插入,更新都是由模型来负责。
  • 视图(View)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。
  • 控制器(Controller)是模型,视图以及其他任何处理 HTTP 请求所必须的资源之前的中介

概述

一个典型的页面浏览行为在程序端的流程是这样的:

  1. 控制器最先被调用,并被赋予外部输入
  2. 控制器根据外部输入向模型请求数据
  3. 模型从数据库获取数据并发送数据到控制器
  4. 控制器处理该数据并发送封装好的数据到视图
  5. 视图根据接到的数据最终展示页面给用户浏览

使用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只处理一种数据类型。

 1 package com.emerson.etao.service;
 2  
 3 import java.sql.SQLException;
 4 import java.util.List;
 5  
 6 /**
 7  * 业务实现层接口
 8  * 
 9  * @author Chris Mao(Zibing)
10  *
11  * @param <T>
12  */
13 public interface IBaseService<T> {
14  
15     /**
16      * 将实体类对象持久化,写入到数据表中
17      * 
18      * @param T
19      * @return 返回新写入记录的自增ID
20      * @throws SQLException 
21      */
22     public long insert(T entity);
23     
24     /**
25      * 根据Id值,将实体类数据回写到数据库
26      * 
27      * @param id
28      * @param T
29      * @return 返回更新的记录笔数
30      * @throws SQLException 
31      */
32     public int update(long id, T entity);
33     
34     /**
35      * 根据Id值从数据库中删除实体类数据
36      * 
37      * @param id
38      * @return 返回删除的记录笔数
39      * @throws SQLException 
40      */
41     public int delete(long id);
42     
43     /**
44      * 根据Id查询具体的实体类信息,并返回实体类对象
45      * 若查询不到数据则返回null
46      * 
47      * @param id
48      * @return T
49      */
50     public T getById(long id);
51     
52     /**
53      * 获取列表
54      * 
55      * @return List
56      * @throws SQLException
57      */
58     public List<T> getAll();
59 }

下面是具体的Service层接口代码,

 1 package com.emerson.etao.service.base;
 2  
 3 import java.util.List;
 4  
 5 import com.emerson.etao.entity.base.BusinessApp;
 6 import com.emerson.etao.entity.base.Communicator;
 7 import com.emerson.etao.entity.base.Customer;
 8 import com.emerson.etao.service.IBaseService;
 9  
10 /**
11  * 客服类操作接口
12  * 
13  * @author Chris Mao(Zibing)
14  * @param <T>
15  *
16  * @param <T>
17  */
18 public interface ICommunicatorService extends IBaseService<Communicator> {
19     
20     public List<Communicator> getAll(Customer customer);
21     
22     /**
23      * 为客服分配商业应用
24      * 
25      * @param c
26      * @param appList
27      * @see BusinessApp
28      */
29     public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists);
30     
31     /**
32      * 为客服分配客户
33      * 
34      * @param c
35      * @param customerList
36      * @see Customer
37      */
38     public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists);
39 }

实现接口。

 1 /**
 2  * 
 3  */
 4 package com.emerson.etao.service.imp;
 5  
 6 import java.sql.SQLException;
 7 import java.util.List;
 8  
 9 import com.emerson.etao.dao.IBaseDao;
10 import com.emerson.etao.dao.IDaoFactory;
11 import com.emerson.etao.service.IBaseService;
12  
13 /**
14  * 业务层实现类基类
15  * 
16  * 为了降低与数据访问层的耦合,在构造函数中传入DaoFactory接口用于创建具体的数据访问对象实例
17  * 
18  * @author Chris Mao(Zibing)
19  *
20  */
21 public abstract class BaseServiceImp<T> implements IBaseService<T> {
22  
23     private IBaseDao<T> dao = null;
24  
25     protected IBaseDao<T> getDao() {
26         return this.dao;
27     }
28  
29     /**
30      * 
31      * @param factory 降低耦合,传入DaoFactory接口
32      * @see IDaoFactory
33      */
34     public BaseServiceImp(IDaoFactory<T> factory) {
35         super();
36         this.dao = factory.getDao();
37     }
38  
39     @Override
40     public long insert(T entity) {
41         try {
42             return this.getDao().insert(entity);
43         } catch (SQLException e) {
44             e.printStackTrace();
45         }
46         return 0;
47     }
48  
49     @Override
50     public int update(long id, T entity) {
51         try {
52             return this.getDao().update(id, entity);
53         } catch (SQLException e) {
54             e.printStackTrace();
55         }
56         return 0;
57     }
58  
59     @Override
60     public int delete(long id) {
61         try {
62             return this.getDao().delete(id);
63         } catch (SQLException e) {
64             e.printStackTrace();
65         }
66         return 0;
67     }
68  
69     @Override
70     public List<T> getAll() {
71         try {
72             return this.getDao().getAll();
73         } catch (SQLException e) {
74             e.printStackTrace();
75         }
76         return null;
77     }
78  
79     @Override
80     public T getById(long id) {
81         try {
82             return this.getDao().getById(id);
83         } catch (SQLException e1) {
84             e1.printStackTrace();
85         }
86         return null;
87     }
88 }
  1 package com.emerson.etao.service.base.imp;
  2  
  3 import java.sql.PreparedStatement;
  4 import java.sql.SQLException;
  5 import java.text.SimpleDateFormat;
  6 import java.util.ArrayList;
  7 import java.util.Date;
  8 import java.util.Iterator;
  9 import java.util.List;
 10  
 11 import com.emerson.etao.dao.IDaoFactory;
 12 import com.emerson.etao.entity.base.BusinessApp;
 13 import com.emerson.etao.entity.base.Communicator;
 14 import com.emerson.etao.entity.base.Customer;
 15 import com.emerson.etao.service.base.ICommunicatorService;
 16 import com.emerson.etao.service.imp.BaseServiceImp;
 17  
 18 /**
 19  * 
 20  * @author Chris Mao(Zibing)
 21  *
 22  */
 23 public class CommunicatorServiceImp extends BaseServiceImp<Communicator>implements ICommunicatorService {
 24  
 25     @Override
 26     public List<Communicator> getAll(Customer customer) {
 27         List<Communicator> result = new ArrayList<Communicator>();
 28         try {
 29             result = this.getDao()
 30                     .getAll("SELECT a.* FROM communicator AS a INNER JOIN customer_communicator cc USING(communicator_id) WHERE cc.customer_id = "
 31                             + customer.getCustomerId());
 32         } catch (SQLException e) {
 33             e.printStackTrace();
 34         }
 35         return result;
 36     }
 37  
 38     public CommunicatorServiceImp(IDaoFactory<Communicator> factory) {
 39         super(factory);
 40     }
 41  
 42     @Override
 43     public void assignBusinessApp(Communicator communicator, List<BusinessApp> appList, boolean deleteExists) {
 44         try {
 45             if (true == deleteExists) {
 46                 this.getDao().getStatement().executeUpdate("DELETE FROM communicator_application WHERE communicator_id = " + communicator.getCommunicatorId());
 47             }
 48             
 49             if (null == appList || appList.isEmpty()) {
 50                 return;
 51             }
 52  
 53              PreparedStatement pstmt = this.getDao().getConnection().prepareStatement("INSERT IGNORE INTO communicator_application(communicator_id, application_id, created_time) VALUES(?, ?, ?)");
 54              BusinessApp app = null;
 55              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置日期格式
 56              Iterator<BusinessApp> ite = appList.iterator();
 57              while (ite.hasNext()) {
 58              app = ite.next();
 59              pstmt.setLong(1, communicator.getCommunicatorId());
 60              pstmt.setLong(2, app.getApplicationId());
 61              pstmt.setString(3, sdf.format(new Date()));
 62              pstmt.executeUpdate();
 63              }
 64              pstmt.close();
 65         } catch (SQLException e) {
 66             e.printStackTrace();
 67         }
 68     }
 69     
 70     /**
 71      * 为客服人员分配客户
 72      * 
 73      * 如果需要删除客服人员名下所有客户,只需将customers设为null或是空列表
 74      * 
 75      * @param communicator
 76      * @param apps
 77      * @param deleteExists
 78      */
 79     @Override
 80     public void assingCustomer(Communicator communicator, List<Customer> customerList, boolean deleteExists) {
 81         try {
 82             if (true == deleteExists) {
 83                 this.getDao().getStatement().executeQuery("DELETE FROM customer_communicator WHERE communicator_id = " + communicator.getCommunicatorId());
 84             }
 85             
 86             if (null == customerList || customerList.isEmpty()) {
 87                 return;
 88             }
 89  
 90              PreparedStatement pstmt = this.getDao().getConnection().prepareStatement("INSERT IGNORE INTO customer_communicator(communicator_id, customer_id, created_time) VALUES(?, ?, ?)");
 91              Customer customer = null;
 92              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置日期格式
 93              Iterator<Customer> ite = customerList.iterator();
 94              while (ite.hasNext()) {
 95              customer = ite.next();
 96              pstmt.setLong(1, communicator.getCommunicatorId());
 97              pstmt.setLong(2, customer.getCustomerId());
 98              pstmt.setString(3, sdf.format(new Date()));
 99              pstmt.executeUpdate();
100              }
101              pstmt.close();
102         } catch (SQLException e) {
103             e.printStackTrace();
104         }
105     }
106 }

DAO层的设计

这里需在为DAO层定义一个通个的基础接口IBaseDao,这里包含了对数据的增、删、改、查基础操作。抽象类BaseDao实现接口IBaseDao,并添加了访问限制为protected的数据库连接对象,方便子类使用。

DAO接口代码。

 1 /**
 2  * 
 3  */
 4 package com.emerson.etao.dao;
 5  
 6 import java.sql.Connection;
 7 import java.sql.SQLException;
 8 import java.sql.Statement;
 9 import java.util.List;
10  
11 /**
12  * 
13  * 数据访问层接口
14  * 
15  * @author Chris Mao(Zibing)
16  *
17  */
18 public interface IBaseDao<T> {
19     
20     /**
21      * 
22      * @return Connection
23      */
24     public Connection getConnection();
25     
26     /**
27      * 
28      * @return Statement
29      */
30     public Statement getStatement();
31     
32     /**
33      * 将值对象写入到数据表中,并返回其自增ID值
34      * 
35      * @param entity
36      * @return 返回新写入记录的自增ID
37      * @throws SQLException 
38      */
39     public long insert(T entity) throws SQLException;
40     
41     /**
42      * 将值对象修改后的内容写入到数据表中,并返回其影响的记录笔数
43      * 
44      * @param id
45      * @param entity
46      * @return 返回更新的记录笔数
47      * @throws SQLException 
48      */
49     public int update(long id, T entity) throws SQLException;
50     
51     /**
52      * 删除ID值,并返回其删除的记录笔数
53      * 
54      * @param id
55      * @return 返回删除的记录笔数
56      * @throws SQLException 
57      */
58     public int delete(long id) throws SQLException;
59     
60     /**
61      * 依据Id值到数据表中查询数据,并返回值对象
62      * 
63      * @param id
64      * @return 
65      */
66     public T getById(long id) throws SQLException;
67     
68     /**
69      * 返回数据表中所有记录
70      * 
71      * @return List<T>
72      * @throws SQLException 
73      */
74     public List<T> getAll() throws SQLException;
75     
76     /**
77      * 返回符合条件的所有记录
78      * 
79      * @param queryStr
80      * @return List<T>
81      * @throws SQLException
82      */
83     public List<T> getAll(String queryStr) throws SQLException;
84 }

抽象工厂接口。

 1 package com.emerson.etao.dao;
 2  
 3 /**
 4  * 数据访问类工厂接口
 5  * 
 6  * 负责创建具体的数据访问对象实例
 7  * 
 8  * @author Chris Mao(Zibing)
 9  *
10  * @param <T>
11  */
12 public interface IDaoFactory<T> {
13     /**
14      * 创建数据访问对象实例
15      * 
16      * @return
17      * @see IBaseDao
18      */
19     public  IBaseDao<T> getDao();
20 }

抽象类BaseDao实现接口IBaseDao。

 1 package com.emerson.etao.dao;
 2  
 3 import java.sql.Connection;
 4 import java.sql.ResultSet;
 5 import java.sql.SQLException;
 6 import java.sql.Statement;
 7  
 8 import org.slf4j.Logger;
 9 import org.slf4j.LoggerFactory;
10  
11 import com.emerson.etao.db.DBUtils;
12  
13 /**
14  * 
15  * 数据访问层基类
16  * 
17  * 所有数据访问对象都需要继承此类
18  * 
19  * @author Chris Mao(Zibing)
20  *
21  */
22 public abstract class BaseDao<T> implements IBaseDao<T> {
23  
24     private static final Logger logger = LoggerFactory.getLogger(BaseDao.class);
25  
26     public Connection getConnection() {
27         return DBUtils.getConnection();
28     }
29  
30     public Statement getStatement() {
31         Statement stmt = null;
32         try {
33             Connection conn = DBUtils.getConnection();
34             stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
35         } catch (SQLException e) {
36             logger.error("创建 Statement 对象发生错误!!");
37             e.printStackTrace();
38         }
39         return stmt;
40     }
41 }

实体类创建工厂接口。

 1 package com.emerson.etao.entity;
 2  
 3 import java.sql.ResultSet;
 4  
 5 /**
 6  * 
 7  * 实体类工厂接口
 8  * 
 9  * 所有实体类对象实例需要通过此工厂接口创建
10  * 
11  * @author Chris Mao(Zibing)
12  *
13  */
14 public interface IEntityFactory<T> {
15     
16     /**
17      * 创建空的实体类
18      * 
19      * @return
20      */
21     public T createEntity();
22     
23     /**
24      * 创建实体类,并将参数rs中的内容赋值到实体类属性当中
25      * 
26      * @param rs
27      * @return
28      */
29     public T createEntity(ResultSet rs);
30  
31 }

具体的DAO对象,继承BaseDao,并实现实体类创建工厂接口。这里使用了内部匿名类实现DAO的抽象工厂接口。

  1 package com.emerson.etao.dao.base;
  2  
  3 import java.sql.PreparedStatement;
  4  
  5 import java.sql.ResultSet;
  6 import java.sql.SQLException;
  7 import java.sql.Statement;
  8 import java.text.SimpleDateFormat;
  9 import java.util.ArrayList;
 10 import java.util.Date;
 11 import java.util.List;
 12  
 13 import com.emerson.etao.dao.BaseDao;
 14 import com.emerson.etao.dao.IBaseDao;
 15 import com.emerson.etao.dao.IDaoFactory;
 16 import com.emerson.etao.entity.IEntityFactory;
 17 import com.emerson.etao.entity.base.Communicator;
 18  
 19 /**
 20  * 客服人员数据访问对象
 21  * 
 22  * @author Chris Mao(Zibing)
 23  *
 24  */
 25 public class CommunicatorDao extends BaseDao<Communicator>implements IEntityFactory<Communicator> {
 26  
 27     public static IDaoFactory<Communicator> factory = new IDaoFactory<Communicator>() {
 28  
 29         @Override
 30         public IBaseDao<Communicator> getDao() {
 31             return new CommunicatorDao();
 32         }
 33  
 34     };
 35  
 36     @Override
 37     public Communicator createEntity() {
 38         return new Communicator();
 39     }
 40  
 41     @Override
 42     public Communicator createEntity(ResultSet rs) {
 43         Communicator c = this.createEntity();
 44         try {
 45             c.setCommunicatorId(rs.getInt("communicator_id"));
 46             c.setCommunicatorName(rs.getString("communicator_name"));
 47             c.setPhone(rs.getString("phone"));
 48             c.setFax(rs.getString("fax"));
 49             c.setEmail(rs.getString("email"));
 50             c.setReportTo(rs.getInt("report_to"));
 51             c.setReportToName(rs.getString("report_to_name"));
 52             c.setValid(rs.getByte("is_valid"));
 53             c.setCreatedTime(rs.getTimestamp("created_time"));
 54             c.setUpdatedTime(rs.getTimestamp("update_time"));
 55         } catch (SQLException e) {
 56             e.printStackTrace();
 57         }
 58         return c;
 59     }
 60  
 61     @Override
 62     public Communicator getById(long id) throws SQLException {
 63         Communicator result = null;
 64         ResultSet rs = this.getStatement().executeQuery("SELECT * FROM vw_communicator WHERE communicator_id = " + id);
 65         while (rs.next()) {
 66             result = this.createEntity(rs);
 67         }
 68         rs.close();
 69         return result;
 70     }
 71  
 72     @Override
 73     public long insert(Communicator entity) throws SQLException {
 74         Long newId = (long) 0;
 75  
 76         StringBuilder sql = new StringBuilder();
 77         sql.append("INSERT IGNORE INTO communicator");
 78         sql.append("(communicator_name, phone, fax, email, report_to, created_time) ");
 79         sql.append("VALUES(?, ? ,? ,?, ?, ?)");
 80  
 81         PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
 82         pstmt.setString(1, entity.getCommunicatorName());
 83         pstmt.setString(2, entity.getPhone());
 84         pstmt.setString(3, entity.getFax());
 85         pstmt.setString(3, entity.getFax());
 86         pstmt.setString(4, entity.getEmail());
 87         pstmt.setInt(5, entity.getReportTo());
 88         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 设置日期格式
 89         pstmt.setString(6, df.format(new Date()));
 90         pstmt.executeUpdate();
 91         ResultSet rs = pstmt.getGeneratedKeys();
 92         if (rs.next()) {
 93             newId = rs.getLong(1);
 94             entity.setCommunicatorId(rs.getInt(1));
 95             // System.out.println("新增客服记录ID为:" + newId);
 96         }
 97         rs.close();
 98         pstmt.close();
 99         return newId;
100     }
101  
102     @Override
103     public int update(long id, Communicator entiry) throws SQLException {
104         int result = 0;
105         StringBuffer sql = new StringBuffer();
106         Communicator c = (Communicator) entiry;
107         // System.out.println(c);
108         sql.append("UPDATE communicator");
109         sql.append(" SET communicator_name = ?, phone = ?, fax = ?, email = ?, report_to = ?, is_valid = ?");
110         sql.append(" WHERE communicator_id = ?");
111  
112         PreparedStatement pstmt = this.getConnection().prepareStatement(sql.toString());
113         pstmt.setString(1, c.getCommunicatorName());
114         pstmt.setString(2, c.getPhone());
115         pstmt.setString(3, c.getFax());
116         pstmt.setString(3, c.getFax());
117         pstmt.setString(4, c.getEmail());
118         pstmt.setInt(5, c.getReportTo());
119         pstmt.setInt(6, c.getIsValid());
120         pstmt.setLong(7, c.getCommunicatorId());
121         result = pstmt.executeUpdate();
122         // System.out.println("更新客服记录数为:" + result);
123         pstmt.close();
124         return result;
125     }
126  
127     @Override
128     public int delete(long id) throws SQLException {
129         int result = 0;
130         String sql = "DELETE FROM communicator WHERE communicator_id = ?";
131  
132         PreparedStatement pstmt;
133  
134         pstmt = this.getConnection().prepareStatement(sql);
135         pstmt.setLong(1, id);
136         result = pstmt.executeUpdate();
137         // System.out.println("删除客服记录数为:" + result);
138         pstmt.close();
139         return result;
140     }
141  
142     @Override
143     public List<Communicator> getAll() throws SQLException {
144         List<Communicator> result = null;
145         ResultSet rs = this.getStatement().executeQuery("SELECT * FROM vw_communicator");
146         result = new ArrayList<Communicator>();
147         while (rs.next()) {
148             result.add(this.createEntity(rs));
149         }
150         rs.close();
151         return result;
152     }
153  
154     @Override
155     public List<Communicator> getAll(String queryStr) throws SQLException {
156         List<Communicator> result = new ArrayList<Communicator>();
157  
158         ResultSet rs = this.getStatement().executeQuery(queryStr);
159         while (rs.next()) {
160             result.add(this.createEntity(rs));
161         }
162         rs.close();
163         return result;
164     }
165 }

猜你喜欢

转载自www.cnblogs.com/sanxiao/p/9616070.html