原文: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 请求所必须的资源之前的中介
概述
一个典型的页面浏览行为在程序端的流程是这样的:
控制器最先被调用,并被赋予外部输入
控制器根据外部输入向模型请求数据
模型从数据库获取数据并发送数据到控制器
控制器处理该数据并发送封装好的数据到视图
视图根据接到的数据最终展示页面给用户浏览
使用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 packagecom.emerson.etao.service;2
3 importjava.sql.SQLException;4 importjava.util.List;5
6 /**
7 * 业务实现层接口8 *9 *@authorChris Mao(Zibing)10 *11 *@param12 */
13 public interface IBaseService{14
15 /**
16 * 将实体类对象持久化,写入到数据表中17 *18 *@paramT19 *@return返回新写入记录的自增ID20 *@throwsSQLException21 */
22 public longinsert(T entity);23
24 /**
25 * 根据Id值,将实体类数据回写到数据库26 *27 *@paramid28 *@paramT29 *@return返回更新的记录笔数30 *@throwsSQLException31 */
32 public int update(longid, T entity);33
34 /**
35 * 根据Id值从数据库中删除实体类数据36 *37 *@paramid38 *@return返回删除的记录笔数39 *@throwsSQLException40 */
41 public int delete(longid);42
43 /**
44 * 根据Id查询具体的实体类信息,并返回实体类对象45 * 若查询不到数据则返回null46 *47 *@paramid48 *@returnT49 */
50 public T getById(longid);51
52 /**
53 * 获取列表54 *55 *@returnList56 *@throwsSQLException57 */
58 public ListgetAll();59 }
下面是具体的Service层接口代码,
1 packagecom.emerson.etao.service.base;2
3 importjava.util.List;4
5 importcom.emerson.etao.entity.base.BusinessApp;6 importcom.emerson.etao.entity.base.Communicator;7 importcom.emerson.etao.entity.base.Customer;8 importcom.emerson.etao.service.IBaseService;9
10 /**
11 * 客服类操作接口12 *13 *@authorChris Mao(Zibing)14 *@param15 *16 *@param17 */
18 public interface ICommunicatorService extends IBaseService{19
20 public ListgetAll(Customer customer);21
22 /**
23 * 为客服分配商业应用24 *25 *@paramc26 *@paramappList27 *@seeBusinessApp28 */
29 public void assignBusinessApp(Communicator communicator, List appList, booleandeleteExists);30
31 /**
32 * 为客服分配客户33 *34 *@paramc35 *@paramcustomerList36 *@seeCustomer37 */
38 public void assingCustomer(Communicator communicator, List customerList, booleandeleteExists);39 }
实现接口。
1 /**
2 *3 */
4 packagecom.emerson.etao.service.imp;5
6 importjava.sql.SQLException;7 importjava.util.List;8
9 importcom.emerson.etao.dao.IBaseDao;10 importcom.emerson.etao.dao.IDaoFactory;11 importcom.emerson.etao.service.IBaseService;12
13 /**
14 * 业务层实现类基类15 *16 * 为了降低与数据访问层的耦合,在构造函数中传入DaoFactory接口用于创建具体的数据访问对象实例17 *18 *@authorChris Mao(Zibing)19 *20 */
21 public abstract class BaseServiceImp implements IBaseService{22
23 private IBaseDao dao = null;24
25 protected IBaseDaogetDao() {26 return this.dao;27 }28
29 /**
30 *31 *@paramfactory 降低耦合,传入DaoFactory接口32 *@seeIDaoFactory33 */
34 public BaseServiceImp(IDaoFactoryfactory) {35 super();36 this.dao =factory.getDao();37 }38
39 @Override40 public longinsert(T entity) {41 try{42 return this.getDao().insert(entity);43 } catch(SQLException e) {44 e.printStackTrace();45 }46 return 0;47 }48
49 @Override50 public int update(longid, 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 @Override60 public int delete(longid) {61 try{62 return this.getDao().delete(id);63 } catch(SQLException e) {64 e.printStackTrace();65 }66 return 0;67 }68
69 @Override70 public ListgetAll() {71 try{72 return this.getDao().getAll();73 } catch(SQLException e) {74 e.printStackTrace();75 }76 return null;77 }78
79 @Override80 public T getById(longid) {81 try{82 return this.getDao().getById(id);83 } catch(SQLException e1) {84 e1.printStackTrace();85 }86 return null;87 }88 }
1 packagecom.emerson.etao.service.base.imp;2
3 importjava.sql.PreparedStatement;4 importjava.sql.SQLException;5 importjava.text.SimpleDateFormat;6 importjava.util.ArrayList;7 importjava.util.Date;8 importjava.util.Iterator;9 importjava.util.List;10
11 importcom.emerson.etao.dao.IDaoFactory;12 importcom.emerson.etao.entity.base.BusinessApp;13 importcom.emerson.etao.entity.base.Communicator;14 importcom.emerson.etao.entity.base.Customer;15 importcom.emerson.etao.service.base.ICommunicatorService;16 importcom.emerson.etao.service.imp.BaseServiceImp;17
18 /**
19 *20 *@authorChris Mao(Zibing)21 *22 */
23 public class CommunicatorServiceImp extends BaseServiceImpimplementsICommunicatorService {24
25 @Override26 public ListgetAll(Customer customer) {27 List result = new ArrayList();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 returnresult;36 }37
38 public CommunicatorServiceImp(IDaoFactoryfactory) {39 super(factory);40 }41
42 @Override43 public void assignBusinessApp(Communicator communicator, List appList, booleandeleteExists) {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 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(newDate()));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 *@paramcommunicator76 *@paramapps77 *@paramdeleteExists78 */
79 @Override80 public void assingCustomer(Communicator communicator, List customerList, booleandeleteExists) {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 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(newDate()));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 packagecom.emerson.etao.dao;5
6 importjava.sql.Connection;7 importjava.sql.SQLException;8 importjava.sql.Statement;9 importjava.util.List;10
11 /**
12 *13 * 数据访问层接口14 *15 *@authorChris Mao(Zibing)16 *17 */
18 public interface IBaseDao{19
20 /**
21 *22 *@returnConnection23 */
24 publicConnection getConnection();25
26 /**
27 *28 *@returnStatement29 */
30 publicStatement getStatement();31
32 /**
33 * 将值对象写入到数据表中,并返回其自增ID值34 *35 *@paramentity36 *@return返回新写入记录的自增ID37 *@throwsSQLException38 */
39 public long insert(T entity) throwsSQLException;40
41 /**
42 * 将值对象修改后的内容写入到数据表中,并返回其影响的记录笔数43 *44 *@paramid45 *@paramentity46 *@return返回更新的记录笔数47 *@throwsSQLException48 */
49 public int update(long id, T entity) throwsSQLException;50
51 /**
52 * 删除ID值,并返回其删除的记录笔数53 *54 *@paramid55 *@return返回删除的记录笔数56 *@throwsSQLException57 */
58 public int delete(long id) throwsSQLException;59
60 /**
61 * 依据Id值到数据表中查询数据,并返回值对象62 *63 *@paramid64 *@return
65 */
66 public T getById(long id) throwsSQLException;67
68 /**
69 * 返回数据表中所有记录70 *71 *@returnList72 *@throwsSQLException73 */
74 public List getAll() throwsSQLException;75
76 /**
77 * 返回符合条件的所有记录78 *79 *@paramqueryStr80 *@returnList81 *@throwsSQLException82 */
83 public List getAll(String queryStr) throwsSQLException;84 }
抽象工厂接口。
1 packagecom.emerson.etao.dao;2
3 /**
4 * 数据访问类工厂接口5 *6 * 负责创建具体的数据访问对象实例7 *8 *@authorChris Mao(Zibing)9 *10 *@param11 */
12 public interface IDaoFactory{13 /**
14 * 创建数据访问对象实例15 *16 *@return
17 *@seeIBaseDao18 */
19 public IBaseDaogetDao();20 }
抽象类BaseDao实现接口IBaseDao。
1 packagecom.emerson.etao.dao;2
3 importjava.sql.Connection;4 importjava.sql.ResultSet;5 importjava.sql.SQLException;6 importjava.sql.Statement;7
8 importorg.slf4j.Logger;9 importorg.slf4j.LoggerFactory;10
11 importcom.emerson.etao.db.DBUtils;12
13 /**
14 *15 * 数据访问层基类16 *17 * 所有数据访问对象都需要继承此类18 *19 *@authorChris Mao(Zibing)20 *21 */
22 public abstract class BaseDao implements IBaseDao{23
24 private static final Logger logger = LoggerFactory.getLogger(BaseDao.class);25
26 publicConnection getConnection() {27 returnDBUtils.getConnection();28 }29
30 publicStatement 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 returnstmt;40 }41 }
实体类创建工厂接口。
1 packagecom.emerson.etao.entity;2
3 importjava.sql.ResultSet;4
5 /**
6 *7 * 实体类工厂接口8 *9 * 所有实体类对象实例需要通过此工厂接口创建10 *11 *@authorChris Mao(Zibing)12 *13 */
14 public interface IEntityFactory{15
16 /**
17 * 创建空的实体类18 *19 *@return
20 */
21 publicT createEntity();22
23 /**
24 * 创建实体类,并将参数rs中的内容赋值到实体类属性当中25 *26 *@paramrs27 *@return
28 */
29 publicT createEntity(ResultSet rs);30
31 }
具体的DAO对象,继承BaseDao,并实现实体类创建工厂接口。这里使用了内部匿名类实现DAO的抽象工厂接口。
1 packagecom.emerson.etao.dao.base;2
3 importjava.sql.PreparedStatement;4
5 importjava.sql.ResultSet;6 importjava.sql.SQLException;7 importjava.sql.Statement;8 importjava.text.SimpleDateFormat;9 importjava.util.ArrayList;10 importjava.util.Date;11 importjava.util.List;12
13 importcom.emerson.etao.dao.BaseDao;14 importcom.emerson.etao.dao.IBaseDao;15 importcom.emerson.etao.dao.IDaoFactory;16 importcom.emerson.etao.entity.IEntityFactory;17 importcom.emerson.etao.entity.base.Communicator;18
19 /**
20 * 客服人员数据访问对象21 *22 *@authorChris Mao(Zibing)23 *24 */
25 public class CommunicatorDao extends BaseDaoimplements IEntityFactory{26
27 public static IDaoFactory factory = new IDaoFactory() {28
29 @Override30 public IBaseDaogetDao() {31 return newCommunicatorDao();32 }33
34 };35
36 @Override37 publicCommunicator createEntity() {38 return newCommunicator();39 }40
41 @Override42 publicCommunicator 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 returnc;59 }60
61 @Override62 public Communicator getById(long id) throwsSQLException {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 returnresult;70 }71
72 @Override73 public long insert(Communicator entity) throwsSQLException {74 Long newId = (long) 0;75
76 StringBuilder sql = newStringBuilder();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(newDate()));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 returnnewId;100 }101
102 @Override103 public int update(long id, Communicator entiry) throwsSQLException {104 int result = 0;105 StringBuffer sql = newStringBuffer();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 returnresult;125 }126
127 @Override128 public int delete(long id) throwsSQLException {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 returnresult;140 }141
142 @Override143 public List getAll() throwsSQLException {144 List result = null;145 ResultSet rs = this.getStatement().executeQuery("SELECT * FROM vw_communicator");146 result = new ArrayList();147 while(rs.next()) {148 result.add(this.createEntity(rs));149 }150 rs.close();151 returnresult;152 }153
154 @Override155 public List getAll(String queryStr) throwsSQLException {156 List result = new ArrayList();157
158 ResultSet rs = this.getStatement().executeQuery(queryStr);159 while(rs.next()) {160 result.add(this.createEntity(rs));161 }162 rs.close();163 returnresult;164 }165 }