一、客户管理-保存客户上传客户资质图片
1、文件上传回顾
什么是文件上传
将本地文件通过流的形式写到服务器上。
文件上传技术
JspSmartUpload(很少用)
FileUpload
Servlet3.0
* 文件上传
* 注解开发
* 异步请求
Struts2框架
* 底层的实现FileUpload,对FileUpload进行封装
文件上传要素
* 表单的提交方式必须是POST
* 表单中需要提供<input type="file" name="upload"/>而且这个文件项必须有name属性和值
* 表单的enctype属性必须是multipart/form-data
2、文件上传代码实现
第一步:修改JSP页面(添加)
提供文件上传项
<TR>
<td>客户资质:</td>
<td colspan="3"><INPUT type="file" name="upload"></td>
</TR>
修改表单的enctype属性
<FORM id=form1 name=form1
action="${pageContext.request.contextPath }/customer_save.action"
method=post enctype="multipart/form-data">
第二步:修改Action中save方法
Struts2的文件上传:
在Action中提供三个属性,对三个属性提供set方法
字符串类型:上传项名称+FileName
文件类型:上传项名称
字符串类型:上传项名称+ContentType
/**
* 文件上传提供的三个属性
*/
// uploadFileName upload uploadContentType中的upload必须和add.jsp中客户资质中
的<INPUT type="file" name="upload">中的name属性的值一致
private String uploadFileName;// 文件名称
private File upload;// 上传文件
private String uploadContentType;// 文件类型
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
public void setUpload(File upload) {
this.upload = upload;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
/**
* 文件上传的工具类
*
* @author Administrator
*
*/
public class UploadUtils {
/**
* 解决目录下文件名重复的问题
*
* @param fileName
* @return
*/
public static String getUuidFileName(String fileName) {
int index = fileName.lastIndexOf(".");
String extions = fileName.substring(index);
return UUID.randomUUID().toString().replace("-", "") + extions;
}
/**
* 目录分离的方法
*
* @param uuidFileName
* @return
*/
public static String getPath(String uuidFileName) {
int code1 = uuidFileName.hashCode();
int d1 = code1 & 0xf;// 作为一级目录
int code2 = code1 >>> 4;
int d2 = code2 & 0xf;// 作为二级目录
return "/" + d1 + "/" + d2;
}
}
/**
* 客户保存的方法
*
* @throws IOException
*/
public String save() throws IOException {
// 上传图片
if (upload != null) {
// 文件上传
// 设置文件上传路径
String path = "E:/upload";
// 一个目录下存放的相同文件名:随机文件名
String uuidFileName = UploadUtils.getUuidFileName(uploadFileName);
// 一个目录下存放的文件过多:目录分离
String realPath = UploadUtils.getPath(uuidFileName);
// 创建目录
String url = path + realPath;
File file = new File(url);
if (!file.exists()) {
file.mkdirs();
}
// 文件上传
File dictFile = new File(url + "/" + uuidFileName);
FileUtils.copyFile(upload, dictFile);
// 设置image的属性的值
customer.setCust_image(url + "/" + uuidFileName);
}
// 保存客户
customerService.save(customer);
return "saveSuccess";
}
第三步:将文件上传路径存入到数据库中
修改实体和映射
在Customer实体增加cust_image属性,并提供get、set方法和映射
上传路径
第四步:设置拦截器(控制文件上传大小,格式)
二、客户管理-删除客户
1、客户删除操作
第一步:修改列表页面上链接地址
第二步:编写Action的delete方法
/**
* 客户删除的方法
*/
public String delete() {
// 先查询,再删除。(做到级联删除)
customer = customerService.findById(customer.getCust_id());
// 删除图片
if (customer.getCust_image() != null) {
File file = new File(customer.getCust_image());
if (file.exists()) {
file.delete();
}
// 删除空目录
String path = "E:/upload";
file = new File(path);
removeDir(file);
}
// 删除客户
customerService.delete(customer);
return "deleteSuccess";
}
跳转页面
<result name="deleteSuccess" type="redirectAction">customer_findAll.action</result>
/**
* 删除空目录的方法
*
* @param dir
*/
public static void removeDir(File dir) {
File[] files = dir.listFiles();
for (int x = 0; x < files.length; x++) {
if (files[x].isDirectory())
removeDir(files[x]);
// System.out.println(files[x].toString()+"-file-"+files[x].delete());//加上可删除所有文件
}
System.out.println(dir + "-dir-" + dir.delete());
}
第三步:编写Service
@Override
// 业务层根据ID查询客户的方法
public Customer findById(Long cust_id) {
return customerDao.findById(cust_id);
}
@Override
// 业务层删除客户的方法
public void delete(Customer customer) {
customerDao.delete(customer);
}
第四步:编写DAO
@Override
// DAO中根据ID查询客户的方法
public Customer findById(Long cust_id) {
return this.getHibernateTemplate().get(Customer.class, cust_id);
}
@Override
//DAO中删除客户的方法
public void delete(Customer customer) {
this.getHibernateTemplate().delete(customer);
}
三、客户管理-修改客户
1、客户的修改操作
第一步:修改列表页面路径
第二步:编写Action中的edit方法
/**
* 编辑客户的方法:edit
*/
public String edit() {
// 根据id查询,跳转页面,回显数据
customer = customerService.findById(customer.getCust_id());
// 将customer传递到页面
// 两种方式:一种,手动压栈;二种,因为模型驱动的对象,默认在栈顶
// 如果使用第一种方式:回显数据:<s:property value="cust_name" />
// 如果使用UI标签<s:textfield name="cust_name" />,不需要写value就可以进行数据回显
// ActionContext.getContext().getValueStack().push(customer);
// 如果使用第二种方式:回显数据:<s:property value="model.cust_name" />
// <s:property/>可自动解析ONGL表达式,如果此时使用UI标签<s:textfield name="cust_name"
// />,需要写value并且使用%符号强制解析OGNL表达式才可以进行数据回显
// 第二种方式可以什么都不写
// 跳转页面
return "editSuccess";
}
页面跳转
第三步:在页面中回显数据
使用Struts2标签回显
<TABLE cellSpacing=0 cellPadding=5 border=0>
<TR>
<td>客户名称:</td>
<td>
<!--对于push过来的对象数据的回显,只需要写name属性就可以 --> <%-- <s:textfield
cssClass="textbox" cssStyle="WIDTH:180px" maxlength="50"
name="cust_name" /> --%>
<!--对于第二种方式,此时需要写%符号强制解析OGNL表达式-->
<s:textfield cssClass="textbox" cssStyle="WIDTH:180px"
maxlength="50" name="cust_name" value="%{model.cust_name}" />
</td>
<td>客户级别 :</td>
<td><select id="cust_level" name="baseDictLevel.dict_id">
<option value="">-请选择-</option>
</select></td>
</TR>
<TR>
<td>信息来源 :</td>
<td><select id="cust_source" name="baseDictSource.dict_id">
<option value="">-请选择-</option>
</select></td>
<td>所属行业:</td>
<td><select id="cust_industry"
name="baseDictIndustry.dict_id">
<option value="">-请选择-</option>
</select></td>
</TR>
<TR>
<td>固定电话 :</td>
<td>
<s:textfield cssClass="textbox" cssStyle="WIDTH:180px" maxlength="50"
name="cust_phone" value="%{model.cust_phone}" /></td>
<td>移动电话 :</td>
<td>
<s:textfield cssClass="textbox" cssStyle="WIDTH:180px" maxlength="50"
name="cust_mobile" value="%{model.cust_mobile}" /></td>
</TR>
<TR>
<td>客户资质:</td>
<td colspan="3"><INPUT type="file" name="upload"></td>
</TR>
<tr>
<td rowspan=2><INPUT class=button id=sButton2 type=submit
value=" 修改 " name=sButton2></td>
</tr>
</TABLE>
隐藏属性
异步加载的数据回显
<script type="text/javascript">
$(function() {
//页面加载函数就会执行
//页面加载,异步查询字典数据
//加载客户来源
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "002"
}, function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_source").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据
$("#cust_source option[value='${model.baseDictSource.dict_id}']").prop("selected","selected");
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "006"
}, function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_level").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据
$("#cust_level option[value='${model.baseDictLevel.dict_id}']").prop("selected","selected");
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "001"
}, function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_industry").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据
$("#cust_industry option[value='${model.baseDictIndustry.dict_id}']").prop("selected","selected");
}, "json");
});
</script>
第四步:修改edit.jsp中的提交路径
第五步:编写Action中的update方法
/**
* 客户修改的方法
*
* @throws IOException
*/
public String update() throws IOException {
// 文件项是否已经选择:如果选择了,就删除原有文件,上传新文件。如果没有选,使用原有即可。
if (upload != null) {
// 设置文件上传路径
String path = "E:/upload";
// 已经选择了
// 删除原有文件
String cust_image = customer.getCust_image();
if (cust_image != null || !"".equals(cust_image)) {
File deletefile = new File(cust_image);
if (deletefile.exists()) {
deletefile.delete();
}
// 删除空目录
deletefile = new File(path);
removeDir(deletefile);
}
// 一个目录下存放的相同文件名:随机文件名
String uuidFileName = UploadUtils.getUuidFileName(uploadFileName);
// 一个目录下存放的文件过多:目录分离
String realPath = UploadUtils.getPath(uuidFileName);
// 创建目录
String url = path + realPath;
File file = new File(url);
if (!file.exists()) {
file.mkdirs();
}
// 文件上传
File dictFile = new File(url + "/" + uuidFileName);
FileUtils.copyFile(upload, dictFile);
customer.setCust_image(url + "/" + uuidFileName);
}
customerService.update(customer);
return "updateSuccess";
}
第六步:编写Service
@Override
// 业务层修改客户的方法
public void update(Customer customer) {
customerDao.update(customer);
}
第七步:编写DAO
@Override
public void update(Customer customer) {
this.getHibernateTemplate().update(customer);
}
第八步:结果页面
<result name="updateSuccess" type="redirectAction">customer_findAll.action</result>
四、客户管理-条件查询客户
1、客户管理条件查询
第一步:在列表页面准备条件
表单元素
异步加载
<script type="text/javascript">
$(function() {
//页面加载函数就会执行
//页面加载,异步查询字典数据
//加载客户来源
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "002"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_source").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "006"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_level").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "001"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_industry").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
}, "json");
});
</script>
第二步:改写Action中的findAll方法
/**
* 客户查找的方法
*/
public String findAll() {
// 接收参数:分页参数
// 最好使用DetachedCriteria对象(条件查询--带分页)
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
// 设置条件:(在web层设置条件)
if (customer.getCust_name() != null && !customer.getCust_name().trim().equals("")) {
// 输入名称:
detachedCriteria.add(Restrictions.like("cust_name", "%" + customer.getCust_name() + "%"));
}
if (customer.getBaseDictSource() != null) {
if (customer.getBaseDictSource().getDict_id() != null
&& !customer.getBaseDictSource().getDict_id().trim().equals("")) {
detachedCriteria.add(Restrictions.eq("baseDictSource.dict_id",
customer.getBaseDictSource().getDict_id()));
}
}
if (customer.getBaseDictLevel() != null) {
if (customer.getBaseDictLevel().getDict_id() != null
&& !customer.getBaseDictLevel().getDict_id().trim().equals("")) {
detachedCriteria.add(Restrictions.eq("baseDictLevel.dict_id",
customer.getBaseDictLevel().getDict_id()));
}
}
if (customer.getBaseDictIndustry() != null) {
if (customer.getBaseDictIndustry().getDict_id() != null
&& !customer.getBaseDictIndustry().getDict_id().trim().equals("")) {
detachedCriteria.add(Restrictions.eq("baseDictIndustry.dict_id",
customer.getBaseDictIndustry().getDict_id()));
}
}
// 调用分页查询
// System.out.println(pageSize);
PageBean<Customer> pageBean = customerService.findByPage(detachedCriteria, currPage, pageSize);
ActionContext.getContext().getValueStack().push(pageBean);
return "findAll";
}
第三步:在条件上回显数据
<script type="text/javascript">
$(function() {
//页面加载函数就会执行
//页面加载,异步查询字典数据
//加载客户来源
$ .post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "002"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_source").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据(回显数据)
$("#cust_source option[value='${model.baseDictSource.dict_id}']").prop("selected", "selected");
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "006"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_level").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据(回显数据)
$("#cust_level option[value='${model.baseDictLevel.dict_id}']").prop("selected", "selected");
}, "json");
$
.post(
"${pageContext.request.contextPath}/baseDict_findByTypeCode.action",
{
"dict_type_code" : "001"
},
function(data) {
//遍历json的数据:
$(data).each(
function(i, n) {
$("#cust_industry").append(
"<option value='"+n.dict_id+"'>"
+ n.dict_item_name
+ "</option>");
});
//使用EL获取值栈的数据(回显数据)
$("#cust_industry option[value='${model.baseDictIndustry.dict_id}']").prop("selected", "selected");
}, "json");
});
</script>
五、联系人管理-查询列表
1、联系人准备工作
第一步:创建表
CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`)
REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
第二步:创建实体和映射
* 联系人的实体
* 联系人映射
* 修改客户实体
* 修改客户映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!--建立类与表的映射 -->
<class name="crm.domain.Customer" table="cst_customer">
<!--建立类中属性与表中的主键对应 -->
<id name="cust_id" column="cust_id">
<generator class="native"></generator>
</id>
<!--建立类中的普通属性与表中的字段对应,非主键用property -->
<property name="cust_name" column="cust_name"></property>
<!-- <property name="cust_source" column="cust_source"></property> -->
<!-- <property name="cust_industry" column="cust_industry"></property> -->
<!-- <property name="cust_level" column="cust_level"></property> -->
<property name="cust_phone" column="cust_phone"></property>
<property name="cust_mobile" column="cust_mobile"></property>
<!--配置客户与字典的多对一的映射 -->
<many-to-one name="baseDictSource" class="crm.domain.BaseDict"
column="cust_source"></many-to-one>
<many-to-one name="baseDictIndustry" class="crm.domain.BaseDict"
column="cust_industry"></many-to-one>
<many-to-one name="baseDictLevel" class="crm.domain.BaseDict"
column="cust_level"></many-to-one>
<property name="cust_image" column="cust_image"></property>
<!-- 配置与联系人的关系映射 -->
<set name="linkMans">
<key column="lkm_cust_id" />
<one-to-many class="crm.domain.LinkMan" />
</set>
</class>
</hibernate-mapping>
第三步:创建相关类
* 创建Action
* 创建Service
* 创建DAO
第四步:完成相关配置
<!-- 将联系人交给Spring管理 -->
<!-- 配置DAO -->
<bean id="linkManDao" class="crm.dao.impl.LinkManDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置Service -->
<bean id="linkManService" class="crm.service.impl.LinkManServiceImpl">
<property name="linkManDao" ref="linkManDao"></property>
</bean>
<!--配置Action -->
<bean id="linkManAction" class="crm.web.action.LinkManAction"
scope="prototype">
<property name="linkManService" ref="linkManService"></property>
</bean>
2、查询联系人的列表
第一步:修改menu.jsp的链接
第二步:编写Action
public class LinkManAction extends ActionSupport implements ModelDriven<LinkMan> {
// 模型驱动使用的对象
private LinkMan linkMan = new LinkMan();
@Override
public LinkMan getModel() {
return linkMan;
}
// 注入联系人Service
private LinkManService linkManService;
public void setLinkManService(LinkManService linkManService) {
this.linkManService = linkManService;
}
// 分页参数
private Integer currPage = 1;
private Integer pageSize = 3;
public void setCurrPage(Integer currPage) {
if (currPage == null) {
currPage = 1;
}
this.currPage = currPage;
}
public void setPageSize(Integer pageSize) {
if (pageSize == null) {
pageSize = 3;
}
this.pageSize = pageSize;
}
/**
* 查询联系人列表的Action
*/
public String findAll() {
// 创建离线查询
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(LinkMan.class);
// 设置条件
// 调用业务层
PageBean<LinkMan> pageBean = linkManService.findAll(detachedCriteria, currPage, pageSize);
ActionContext.getContext().getValueStack().push(pageBean);
return "findAll";
}
第三步:编写Service
@Override
// 业务层分页查询联系人的方法
public PageBean<LinkMan> findAll(DetachedCriteria detachedCriteria, Integer currPage, Integer pageSize) {
PageBean<LinkMan> pageBean = new PageBean<>();
// 设置分页页数
pageBean.setCurrPage(currPage);
// 设置每页显示记录数
pageBean.setPageSize(pageSize);
// 设置总记录数
Integer totalCount = linkManDao.findCount(detachedCriteria);
pageBean.setTotalCount(totalCount);
// 设置总页数
double tc = totalCount;
Double num = Math.ceil(tc / pageSize);
pageBean.setTotalPage(num.intValue());
// 每页显示数据的集合
Integer begin = (currPage - 1) * pageSize;
List<LinkMan> list = linkManDao.findByPage(detachedCriteria, begin, pageSize);
pageBean.setList(list);
return pageBean;
}
第四步:编写DAO
@Override
// DAO中统计个数的方法
public Integer findCount(DetachedCriteria detachedCriteria) {
detachedCriteria.setProjection(Projections.rowCount());
List<Long> list = (List<Long>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
if (list.size() > 0) {
return list.get(0).intValue();
}
return null;
}
@Override
// DAO中的分页查询
public List<LinkMan> findByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize) {
detachedCriteria.setProjection(null);
return (List<LinkMan>) this.getHibernateTemplate().findByCriteria(detachedCriteria, begin, pageSize);
}
第五步:页面配置
<!--配置联系人的Action -->
<action name="linkman_*" class="linkManAction" method="{1}">
<result name="findAll">/jsp/linkman/list.jsp</result>
</action>
<s:iterator value="list">
<%-- <c:forEach items="${list }" var="linkman"> --%>
<TR style="FONT-WEIGHT: normal; FONT-STYLE: normal; BACKGROUND-COLOR: white; TEXT-DECORATION: none">
<TD><s:property value="lkm_name" /></TD>
<TD><s:property value="lkm_gender" /></TD>
<TD><s:property value="lkm_phone" /></TD>
<TD><s:property value="lkm_mobile" /></TD>
<TD><s:property value="lkm_email" /></TD>
<TD><s:property value="lkm_qq" /></TD>
<TD><s:property value="lkm_position" /></TD>
<TD><s:property value="customer.cust_name" /></TD>
<TD><a href="${pageContext.request.contextPath }/">修改</a>
<a
href="${pageContext.request.contextPath }/">删除</a>
</TD>
</TR>
<%-- </c:forEach> --%>
</s:iterator>
<TR>
<TD><SPAN id=pagelink>
<DIV
style="LINE-HEIGHT: 20px; HEIGHT: 20px; TEXT-ALIGN: right">
共[<B><s:property value="totalCount" /></B>]条记录,[<B><s:property
value="totalPage" /></B>]页 , 每页显示 <select name="pageSize"
onchange="to_page()">
<option value="3" <s:if test="pageSize==3">selected</s:if>>3</option>
<option value="5" <s:if test="pageSize==5">selected</s:if>>5</option>
<option value="10"<s:if test="pageSize==10">selected</s:if>>10</option>
</select> 条
<s:if test="currPage!=1">
[<A href="javascript:to_page(1)">首页</A>]
[<A href="javascript:to_page(<s:property value="currPage-1" />)">前一页</A>]
</s:if>
<B>
<s:iterator var="i" begin="1" end="totalPage">
<s:if test="#i==currPage">
s:property value="#i" />
</s:if>
<s:else>
<a href="javascript:to_page(<s:property value="#i"/>)"><s:propertyvalue="#i" /></a>
</s:else>
</s:iterator>
</B>
<s:if test="currPage!=totalPage">
[<A href="javascript:to_page(<s:property value="currPage+1" />)">后一页</A>]
[<A href="javascript:to_page(<s:property value="totalPage" />)">尾页</A>]
</s:if>
到 <input type="text" size="3" id="page" name="currPage" />
页 <input type="button" value="Go" onclick="to_page()" />
</DIV>
</SPAN>
</TD>
</TR>
六、联系人管理
1、保存联系人
1.1 修改menu.jsp的链接
<TR>
<TD class=menuSmall><A class=style2
href="${pageContext.request.contextPath }/linkman_saveUI.action" target=main>- 新增联系人</A></TD>
</TR>
1.2 编写Action中的saveUI的方法
// 注入客户管理的Service
private CustomerService customerService;
public void setCustomerService(CustomerService customerService) {
this.customerService = customerService;
}
/**
* 跳转到添加页面的方法saveUI
*
* @return
*/
public String saveUI() {
// 查询所有客户
List<Customer> list = customerService.findAll();
// 将list集合保存到值栈中
ActionContext.getContext().getValueStack().set("list", list);
return "saveUI";
}
<!--配置联系人的Action -->
<action name="linkman_*" class="linkManAction" method="{1}">
<result name="saveUI">/jsp/linkman/add.jsp</result>
<result name="saveSuccess" type="redirectAction">linkman_findAll.action</result>
</action>
1.3 编写Service
@Override
// 业务层保存联系人的方法
public void save(LinkMan linkMan) {
linkManDao.save(linkMan);
}
1.4 编写DAO
@Override
// DAO中保存联系人的方法
public void save(LinkMan linkMan) {
this.getHibernateTemplate().save(linkMan);
}
1.5 修改添加页面(改成struts2的标签)编写表单提交路径
<s:form id="form1" name="form1" action="linkman_save" namespace="/"
method="post" theme="simple">
<TABLE cellSpacing=0 cellPadding=5 border=0>
<tr>
<td>所属客户:</td>
<td colspan="3"><s:select list="list"
name="customer.cust_id" headerKey="" headerValue="-请选择-"
listKey="cust_id" listValue="cust_name" /></td>
</tr>
<TR>
<td>联系人名称:</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_name" /></td>
<td>联系人性别:</td>
<td><s:radio list="#{'1':'男','2':'女'}" name="lkm_gender" /></td>
</TR>
<TR>
<td>联系人办公电话 :</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_phone" /></td>
<td>联系人手机 :</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_mobile" /></td>
</TR>
<TR>
<td>联系人邮箱</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_email" /></td>
<td>联系人QQ:</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_qq" /></td>
</TR>
<TR>
<td>联系人职位:</td>
<td><s:textfield cssClass="textbox" id="sChannel2"
cssStyle="WIDTH: 180px" maxlength="50" name="lkm_position" /></td>
<td>联系人备注:</td>
<td><s:textarea name="lkm_memo" rows="2" cols="20" /></td>
</TR>
<tr>
<td rowspan=2><INPUT class=button id=sButton2 type=submit
value="保存 " name=sButton2></td>
</tr>
</TABLE>
1.6 编写Action的save方法
/**
* 保存客户的方法
*/
public String save() {
// 调用业务层保存联系人
linkManService.save(linkMan);
return "saveSuccess";
}
1.7 编写Service
@Override
// 业务层保存联系人的方法
public void save(LinkMan linkMan) {
linkManDao.save(linkMan);
}
1.8 编写DAO
@Override
// DAO中保存联系人的方法
public void save(LinkMan linkMan) {
this.getHibernateTemplate().save(linkMan);
}
2、修改联系人
2.1 修改列表页面上链接
<TD><a href="${pageContext.request.contextPath }/linkman_edit.action?lkm_id=<s:property value="lkm_id"/>">修改</a>
2.2 编写Action的edit方法
/**
* 跳转到编辑页面的方法
*/
public String edit() {
// 查询某个联系人,查询所有客户
// 查询所有客户:
List<Customer> list = customerService.findAll();
// 根据id查询联系人
linkMan = linkManService.findById(linkMan.getLkm_id());
// 将list和linkMan的对象带到页面上(list用set入值栈,linkMan可以用模型驱动)
ActionContext.getContext().getValueStack().set("list", list);
// 将对象的值也存入值栈中(方便自动回显)
ActionContext.getContext().getValueStack().push(linkMan);
return "editSuccess";
}
<!--配置联系人的Action -->
<action name="linkman_*" class="linkManAction" method="{1}">
<result name="editSuccess">/jsp/linkman/edit.jsp</result>
</action>
2.3 编写Service
@Override
// 业务层根据id查询联系人的方法
public LinkMan findById(Long lkm_id) {
return linkManDao.findById(lkm_id);
}
2.4 编写DAO
@Override
// DAO中根据id查询联系人的方法
public LinkMan findById(Long lkm_id) {
return this.getHibernateTemplate().get(LinkMan.class, lkm_id);
}
2.5 修改编辑页面提交的路径
<s:form id="form1" name="form1" action="linkman_update" namespace="/"
method="post" theme="simple">
<s:hidden name="lkm_id" />
.........
2.6 编写Action的update方法
/**
* 修改联系人的方法
*/
public String update() {
// 调用业务层
linkManService.update(linkMan);
return "updateSuccess";
}
2.7 编写Service
@Override
// 业务层修改联系人的方法
public void update(LinkMan linkMan) {
linkManDao.update(linkMan);
}
2.8 编写DAO
@Override
// DAO中修改联系人的方法
public void update(LinkMan linkMan) {
this.getHibernateTemplate().update(linkMan);
}
3、删除联系人
3.1 修改链接地址
<a href="${pageContext.request.contextPath }/linkman_delete.action?lkm_id=<s:property value="lkm_id"/>">删除</a>
3.2 编写Action中的delete方法
/**
* 删除联系人的方法
*/
public String delete() {
// 先查询,再删除
// 调用业务层
linkMan = linkManService.findById(linkMan.getLkm_id());
// 删除联系人
linkManService.delete(linkMan);
// 页面跳转
return "deleteSuccess";
}
3.3 编写Service
@Override
// 业务层根据id查询联系人的方法
public LinkMan findById(Long lkm_id) {
return linkManDao.findById(lkm_id);
}
3.4 编写DAO
@Override
// DAO中删除联系人方法
public void delete(LinkMan linkMan) {
this.getHibernateTemplate().delete(linkMan);
}
4、条件查询联系人
4.1 修改列表页面
<TR>
<TD>联系人名称:</TD>
<TD><s:textfield name="lkm_name" cssClass="textbox"cssStyle="WIDTH:80px" maxlength="50"
id="sChannel2" /></TD>
<TD>联系人性别:</TD>
<TD><s:select name="lkm_gender"list="#{'1':'男','2':'女' }" headerKey=""
headerValue="-请选择-"></s:select></TD>
<TD><INPUT class=button id=sButton2 type=submit value=" 筛选 " name=sButton2></TD>
</TR>
4.2 修改Action中findAll的方法
/**
* 查询联系人列表的Action
*/
public String findAll() {
// 创建离线查询
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(LinkMan.class);
// 设置条件
if (linkMan.getLkm_name() != null) {
if (!linkMan.getLkm_name().trim().equals("")) {
// 设置按名称查询的条件:
detachedCriteria.add(Restrictions.like("lkm_name", "%" + linkMan.getLkm_name() + "%"));
}
}
if (linkMan.getLkm_gender() != null) {
if (!linkMan.getLkm_gender().trim().equals("")) {
// 设置按照性别查询的条件
detachedCriteria.add(Restrictions.eq("lkm_gender", linkMan.getLkm_gender()));
}
}
// 调用业务层
PageBean<LinkMan> pageBean = linkManService.findAll(detachedCriteria, currPage, pageSize);
ActionContext.getContext().getValueStack().push(pageBean);
return "findAll";
}
5、解决与客户之间问题
5.1 级联删除的问题
- 级联删除,在客户删除的时候,先查询再删除
- 在客户的映射中进行配置
<!-- 配置与联系人的关系映射 -->
<set name="linkMans" cascade="delete">
<!--添加级联删除(删客户时,联系人也会删除)cascade="delete" -->
<key column="lkm_cust_id" />
<one-to-many class="crm.domain.LinkMan" />
</set>
5.2 修改客户的时候,联系人的客户的信息就丢失了
- 因为在修改客户的时候,没有查询联系人的集合,当点击修改,修改客户(修改客户所关联联系人)因为联系人的集合是空,所以将外键置为null。
<!-- 配置与联系人的关系映射 -->
<set name="linkMans" cascade="delete" inverse="true">
<!--添加级联删除(删客户时,联系人也会删除)cascade="delete" -->
<!--修改客户时联系人的客户信息丢失,放弃外键维护权(此时外键的修改只能在联系人操作当中) inverse="true" -->
<key column="lkm_cust_id" />
<one-to-many class="crm.domain.LinkMan" />
</set>
七、抽取通用的DAO
1、通用的DAO的抽取
1.1 抽取通用的增删改的操作
- 定义接口
/**
* 通用的DAO的接口
*
* @author Administrator
*
*/
public interface BaseDao<T> {
public void save(T t);
public void update(T t);
public void delete(T t);
}
- 定义实现类
/**
* 通用的DAO的实现类
*
* @author Administrator
*
* @param <T>
*/
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
public BaseDaoImpl() {
@Override
public void save(T t) {
this.getHibernateTemplate().save(t);
}
@Override
public void update(T t) {
this.getHibernateTemplate().update(t);
}
@Override
public void delete(T t) {
this.getHibernateTemplate().delete(t);
}
}
1.2 抽取一个查询一个的方法
- 在实现类中实现查询的方法
如果能把具体的类型的Class解决,查询所有,分页查询都可以解决!!!
1.3 解决方案一:在实现类的构造方法中传入一个Class
- 编写实现类
在父类中提供了有参数的构造方法,在子类中继承了父类,提供构造方法,在子类的构造中,调用父类的有参数的构造。 - 在客户的DAO的实现类中
- 在联系人的DAO的实现类中
- 有了以上这些内容,将查询的所有的方法,都抽取
- 接口
- 实现类
如果这样抽取完成以后,那么在编写DAO的时候如果里面都是一些CRUD的操作,在DAO中只需要提供构造方法即可。 - 客户的DAO
- 联系人的DAO
如果将通用的DAO编写的更好,连构造方法都不想要了!!!需要怎么做???
泛型反射
1.4 解决方案二:通过泛型的反射抽取通用的DAO
如果现在将DAO中的构造方法去掉,将父类的通用的DAO中提供无参数的构造即可,但是需要在无参数的构造中
需要获得具体类型的Class才可以-----涉及到泛型的反射了。
回顾一下泛型:
泛型 :通用的类型。
<> :念为 typeof
List<E> :E称为类型参数变量
ArrayList<Integer> :Integer称为是实际类型参数
ArrayList<Integer> :ArrayList<Integer>称为参数化类型
需要做的时候在父类的构造方法中获得子类继承父类上的参数化类型中的实际类型参数
泛型反射的步骤:
第一步:获得代表子类对象的Class
第二步:查看API
Type[] getGenericInterfaces(); :获得带有泛型的接口,可以实现多个接口。
Type getGenericSuperclass(); :获得带有泛型的父类,继承一个类。
第三步:获得带有泛型的父类
第四步:将带有泛型的父类的类型转成具体参数化的类型
第五步:通过参数化类型的方法获得实际类型参数
代码实现
/**
* 通用的DAO的实现类
*
* @author Administrator
*
* @param <T>
*/
public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao<T> {
private Class clazz;
// 提供构造方法:在构造方法中传入具体类型的class
/**
* 但是现在不想子类上有构造方法,必须在父类提供无参数的构造,在无参构造中获得具体类型的Class。
* 具体类型的Class是参数类型中实际类型参数。
*
* @param clazz
*/
public BaseDaoImpl() {
// 反射:第一步获得Class
Class clazz = this.getClass();// 正在被调用那个类的Class,CustomerDaoImpl或者LinkManDaoImpl
// 查看JDK的API
Type type = clazz.getGenericSuperclass();// 参数化类型:BaseDaoImpl<Customer>,BaseDaoImpl<LinkMan>
// 得到这个type就是一个参数化的类型,将type强转成参数化的类型
ParameterizedType pType = (ParameterizedType) type;
// 通过参数化类型获得实际类型参数:得到一个实际类型参数的数组?Map<String,Integer>
Type[] types = pType.getActualTypeArguments();
// 只获得第一个实际类型参数即可
this.clazz = (Class) types[0];// 得到Customer、LinkMan、User
}
@Override
public void save(T t) {
this.getHibernateTemplate().save(t);
}
@Override
public void update(T t) {
this.getHibernateTemplate().update(t);
}
@Override
public void delete(T t) {
this.getHibernateTemplate().delete(t);
}
@Override
public T findById(Serializable id) {
return (T) this.getHibernateTemplate().get(clazz, id);
}
@Override
// 查询所有的方法
public List<T> findAll() {
return (List<T>) this.getHibernateTemplate().find("from " + clazz.getSimpleName());
}
@Override
// 统计个数的方法
public Integer findCount(DetachedCriteria detachedCriteria) {
// 设置统计个数的条件
detachedCriteria.setProjection(Projections.rowCount());
List<Long> list = (List<Long>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
if (list.size() > 0) {
return list.get(0).intValue();
}
return null;
}
@Override
// 分页查询
public List<T> findByPage(DetachedCriteria detachedCriteria, Integer begin, Integer pageSize) {
detachedCriteria.setProjection(null);
return (List<T>) this.getHibernateTemplate().findByCriteria(detachedCriteria, begin, pageSize);
}
}