第一次尝试着用Java做Web开发,使用了Java Spring框架,顺便说一句,如果使用Spring开发,建议使用STS(Spring Tool Suite) IDE,它很好的集成了Spring、Maven等框架,使用起来特别方便,尤其是第一次使用Spring框架进行开发,它极大的方便了开发人员,通过快捷菜单及可很简单的配置好Spring开发环境,自动下载、更新Maven依赖包。话不多讲,回到文章的正题。
Spring是一个在Java业界很流行的MVC框架,所谓MVC即模型-视图-控制器,将应用程序的逻辑层与展现层进行分离的一种设计模式。
一个典型的页面浏览行为在程序端的流程是这样的:
使用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只处理一种数据类型。
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(); }
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层定义一个通个的基础接口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(); }
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); }
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; } }