这是一个使用JDBC技术来实现客户关系管理系统的案例。
在Eclipse中新创建一个day14_customer项目,导入项目所需要的开发包(jar包),创建项目所需要的包,在java开发中,架构的层次是以包的形式体现出来的。
项目所需要的开发包(jar包):
序号 | 开发包名称 | 描述 |
---|---|---|
1 | stl-1.2.jar | jstl标签库和EL表达式依赖包 |
2 | mysql-connector-java-5.1.38-bin.jar | MySQL数据库驱动包 |
3 | commons-beanutils-1.9.2.jar | 工具类,用于处理bean对象 |
4 | commons-logging-1.2.jar | commons-beanutils-1.9.2.jar的依赖jar包 |
5 | commons-collections-3.2.2.jar | commons-beanutils-1.9.2.jar的依赖jar包 |
项目所需要的包:
序号 | 包名 | 描述 | 所属层次 |
---|---|---|---|
1 | cn.itcast.domain | 存放系统的JavaBean类(只包含简单的属性以及属性对应的get和set方法,不包含具体的业务处理方法),提供给【数据访问层】、【业务逻辑层】、【Web层】来使用 | domain(域模型)层 |
2 | cn.itcast.dao | 存放访问数据库的操作接口类 | 数据访问层 |
3 | cn.itcast.dao.impl | 存放访问数据库的操作接口的实现类 | 数据访问层 |
4 | cn.itcast.service | 存放处理系统业务接口类 | 业务逻辑层 |
5 | cn.itcast.service.impl | 存放处理系统业务接口的实现类 | 业务逻辑层 |
6 | cn.itcast.web.controller | 存放作为系统控制器的Servlet(处理请求的servlet) | Web层(表现层) |
7 | cn.itcast.utils | 存放系统的通用工具类,提供给【数据访问层】、【业务逻辑层】、【Web层】来使用 |
以上就是根据此项目的实际情况创建的包,可能还需要创建其他的包,这个得根据项目的需要来定了。
为应用创建相应库和表:
根据如下数据库表customer的结构,在数据库中创建一张customer表。
建表SQL语句:
create database day14_customer character set utf8 collate utf8_general_ci; use day14_customer; create table customer ( id varchar(40) primary key, name varchar(40) not null, gender varchar(4) not null, birthday date, cellphone varchar(20), email varchar(40), preference varchar(255), type varchar(100) not null, description varchar(255) );
除此之外,还应在src目录下创建一个db.properties文件,在db.properties中编写MySQL数据库的连接信息,内容如下所示:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day14_customer username=root password=yezi
在WEB-INF目录下创建一个jsp目录,jsp目录存放系统的一些受保护(不允许用户直接通过URL地址访问)的jsp页面,用户要想访问这些受保护的jsp页面,一般来说只能通过cn.itcast.web.UI这个包里面的Servlet。但是在这个项目中我们在cn.itcast.web.UI这个包中并没有创建任何Servlet,关于要想访问这些受保护的jsp页面,后面会详细的介绍到。
创建好的项目架构如下图所示:
分层架构的代码也是按照【域模型层(domain)】→【数据访问层(dao、dao.impl)】→【业务逻辑层(service、service.impl)】→【表现层(web.controller、web.UI、web.filter、web.listener)】→【工具类(util)】→【测试类(junit.test)】的顺序进行编写的。
在cn.itcast.domain包下创建一个Customer类。
Customer类具体代码如下:
public class Customer { private String id; private String name; private String gender; private Date birthday; private String cellphone; private String email; private String preference; private String type; private String description; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getCellphone() { return cellphone; } public void setCellphone(String cellphone) { this.cellphone = cellphone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPreference() { return preference; } public void setPreference(String preference) { this.preference = preference; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
在开发数据访问层时,由于要编写得到MySQL数据库链接和释放资源这些性质的操作,所以应该把他们放在一个工具类JdbcUtils中。在cn.itcast.utils包下创建一个JdbcUtils类。
JdbcUtils类的具体代码如下:
public class JdbcUtils { private static Properties config = new Properties(); // 静态代码块只执行一次,因为静态代码块在类加载时执行,类永远只加载一次 static { try { config.load(JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties")); Class.forName(config.getProperty("driver")); } catch (Exception e) { /* * db.properties文件无法读取,那么整个应用程序无法连接数据库, * 驱动都加载不了,那么整个应用程序都无法工作, * 所以都应该抛一个错误(ExceptionInInitializerError) */ throw new ExceptionInInitializerError(e); // } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password")); } public static void release(Connection conn, Statement st, ResultSet rs) { if (rs!=null) { try { rs.close(); // 假设throw异常 } catch (Exception e) { e.printStackTrace(); // 只需在后台记录异常 } rs = null; // 假设rs对象没有释放,将其置为null,该对象就变成垃圾,由Java垃圾回收器回收 } if (st!=null) { try { st.close(); // 假设throw异常 } catch (Exception e) { e.printStackTrace(); // 只需在后台记录异常 } st = null; } if (conn!=null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); // 只需在后台记录异常 } } } }
在cn.itcast.dao包下创建一个CustomerDao接口类。
CustomerDao接口的具体代码如下:
public interface CustomerDao { void add(Customer c); void update(Customer c); void delete(String id); Customer find(String id); ListgetAll(); }
对于接口中的方法定义,这个只能是根据具体的业务来分析需要定义哪些方法了,但是无论是多么复杂的业务,都离不开基本的CRUD(增删改查)操作,Dao层是直接和数据库交互的,所以Dao层的接口一般都会有增删改查这四种操作的相关方法。
接着应该编写CustomerDao接口的实现类——CustomerDaoImpl,其里面一般都会有增删改查这四种操作的相关方法,增删改查时,难免会发生异常,所以在实际开发中,最好每一个层都编写一个自定义异常,例如在Dao层(数据访问层)自定义一个异常类——DaoException。
在cn.itcast.exception包中创建异常类DaoException,如下:
DaoException类的具体代码如下:
public class DaoException extends RuntimeException { public DaoException() { // TODO Auto-generated constructor stub } public DaoException(String message) { super(message); // TODO Auto-generated constructor stub } public DaoException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } public DaoException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public DaoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } }
在cn.itcast.dao.impl包下创建一个CustomerDao接口的实现类——CustomerDaoImpl。
CustomerDaoImpl类的具体代码如下:
public class CustomerDaoImpl implements CustomerDao { @Override public void add(Customer c) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "insert into customer(id, name,gender,birthday,cellphone,email,preference,type,description) values(?,?,?,?,?,?,?,?,?)"; st = conn.prepareStatement(sql); st.setString(1, c.getId()); st.setString(2, c.getName()); st.setString(3, c.getGender()); st.setDate(4, new java.sql.Date(c.getBirthday().getTime())); st.setString(5, c.getCellphone()); st.setString(6, c.getEmail()); st.setString(7, c.getPreference()); st.setString(8, c.getType()); st.setString(9, c.getDescription()); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public void update(Customer c) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "update customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=? where id=?"; st = conn.prepareStatement(sql); st.setString(1, c.getName()); st.setString(2, c.getGender()); st.setDate(3, new java.sql.Date(c.getBirthday().getTime())); st.setString(4, c.getCellphone()); st.setString(5, c.getEmail()); st.setString(6, c.getPreference()); st.setString(7, c.getType()); st.setString(8, c.getDescription()); st.setString(9, c.getId()); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public void delete(String id) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "delete from customer where id=?"; st = conn.prepareStatement(sql); st.setString(1, id); st.executeUpdate(); } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public Customer find(String id) { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from customer where id=?"; st = conn.prepareStatement(sql); st.setString(1, id); rs = st.executeQuery(); if (rs.next()) { Customer c = new Customer(); c.setBirthday(rs.getDate("birthday")); c.setCellphone(rs.getString("cellphone")); c.setDescription(rs.getString("description")); c.setEmail(rs.getString("email")); c.setGender(rs.getString("gender")); c.setId(rs.getString("id")); c.setName(rs.getString("name")); c.setPreference(rs.getString("preference")); c.setType(rs.getString("type")); return c; } return null; } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } @Override public ListgetAll() { Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); String sql = "select * from customer"; st = conn.prepareStatement(sql); rs = st.executeQuery(); List list = new ArrayList (); while (rs.next()) { Customer c = new Customer(); c.setBirthday(rs.getDate("birthday")); c.setCellphone(rs.getString("cellphone")); c.setDescription(rs.getString("description")); c.setEmail(rs.getString("email")); c.setGender(rs.getString("gender")); c.setId(rs.getString("id")); c.setName(rs.getString("name")); c.setPreference(rs.getString("preference")); c.setType(rs.getString("type")); list.add(c); } return list; } catch (Exception e) { throw new DaoException(e); } finally { JdbcUtils.release(conn, st, rs); } } }
照理说,开发完数据访问层,一定要对程序已编写好的部分代码进行测试。但我们有信心以上代码都不会有任何问题,这点自信都没有,搞鬼啊!
在cn.itcast.service包下创建一个BusinessService接口类。
BusinessService接口类的具体代码如下:
//业务类,统一对web层提供所有服务 public interface BusinessService { void addCustomer(Customer c); void updateCustomer(Customer c); void deleteCustomer(String id); Customer findCustomer(String id); ListgetAllCustomer(); }
接着在cn.itcast.service.impl包下编写BusinessService接口的一个实现类——BusinessServiceImpl。
BusinessServiceImpl实现类的具体代码如下:
/* * 此业务层代码很少,所以称为薄薄的业务层 */ public class BusinessServiceImpl implements BusinessService { private CustomerDao dao = new CustomerDaoImpl(); @Override public void addCustomer(Customer c) { dao.add(c); } @Override public void updateCustomer(Customer c) { dao.update(c); } @Override public void deleteCustomer(String id) { dao.delete(id); } @Override public Customer findCustomer(String id) { return dao.find(id); } @Override public ListgetAllCustomer() { return dao.getAll(); } }
同理,开发完业务逻辑层,一定要对程序已编写好的部分代码进行测试,但我们有信心业务逻辑层的代码没有任何问题,所以我们略过测试这一步。
在一个项目中,开发Web层是最麻烦的,我们一定要有耐心哟。此客户关系管理系统的首页index.jsp,我们是采用分帧的方式来设计的,index.jsp代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
接着我们创建head.jsp页面,head.jsp代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
使用IE浏览器通过http://localhost:8080/day14_customer/链接访问该客户关系管理系统,显示如下:
在cn.itcast.utils包下创建一个WebUtils工具类,该工具类的功能就是封装客户端提交的表单数据到Customer对象中。
WebUtils工具类的具体代码如下:
public class WebUtils { public staticE request2Bean(HttpServletRequest request, Class beanClass) { try { E bean = beanClass.newInstance(); // 得到request里面所有数据 Map map = request.getParameterMap(); // 注册一个转换器 ConvertUtils.register(new Converter() { @Override public T convert(Class type, Object value) { if (value==null) { return null; } String str = (String)value; if (str.trim().equals("")) { return null; } SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); try { return (T) df.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); BeanUtils.populate(bean, map); // map{name=aa,password=abc,birthday=1990-10-09} bean(name=aa,password=abc,birthday=Date) return bean; } catch (Exception e) { throw new RuntimeException(e); } } // 产生全球唯一的id public static String generateID() { return UUID.randomUUID().toString(); // UUID算法根据你系统的网卡的xx地址、CPU、机器的型号等等生成一个128位长的字符串,可以确保是全球唯一的。 } }