BOS物流管理系统-第五天

时间:2022-04-26 23:53:15

BOS物流管理系统-第五天-定区管理-WebServcie远程调用

主要内容:

  1. 分区设置—导出(分区条件查询后的结果导出为Excel—POI生成Excel和文件下载)
  2. 定区管理---定区添加(定区关联分区和取派员,easyUi相关的注意的地方)
  3. 定区管理-分页条件查询(复习—form表单json转换,Spring Data Specification )
  4. 定区管理—定区关联客户(模拟系统间:bos和crm(Customer Relational Managerment)的远程调用—WebService CXF、Hessian\crm系统架构+SSH,json的解析:Gson技术)

学习目标:

  1. Excel文件的导出;
  2. 定区的管理的业务--复杂业务的编写
  3. 远程调用,接口数据的调用(搭环境)
  4. GSON的使用
  1. 分区设置—导出分区数据

目标:分区条件查询后的结果导出为Excel,备份出来。

BOS物流管理系统-第五天

需求分析:

导出的数据不仅是本页的,而是符合条件的全部数据。

因此:程序中list查询,需要带业务条件,但不能带分页条件!

【回顾】

文件下载的要求:

  • JavaWeb:
    • 客户端同步方式提交请求,不能使用Ajax方式,否则浏览器无法捕捉到下载,无法出现下载框。
    • 服务器需要向响应中放入文件的输出流,并且一般需要指定Content-Type(内容类型)和Content-Disposition(附件打开方式和附件的名字)。
  • Struts2:
    • 客户端同步方式提交请求。
    • 使用Stream类型的结果集,该结果集封装了一些操作,比如将输出流放入响应中,但也需要设置Content-Type和Content-Disposition。

开发步骤:

  1. 客户端页码代码

因为查询的form中的表单缓存了查询条件,因此,直接提交该表单,进行导出数据。

BOS物流管理系统-第五天

使用分区查询form提交给下载服务器端路径:

BOS物流管理系统-第五天

给导出按钮添加表单提交事件:

BOS物流管理系统-第五天

  1. 服务端代码

SubareaAction:

//导出分区文件

@Action(value="subarea_exportData")

public String exportData() throws IOException{

//获取查询条件Specification对象

Specification<Subarea> specification = getSubareaSpecification();

//调用业务层查询数据(无需分页)

List<Subarea> subareaList= subareaService.findSubareaListByspecification(specification);

//使用POI,将数据转换生成Excel(.xls格式)

//开发过程

//1.创建一个新的空白工作簿Excel

HSSFWorkbook hssfWorkbook = new HSSFWorkbook();

//2.在工作簿中创建一个新的工作表

HSSFSheet sheet = hssfWorkbook.createSheet();//可以匿名,也可以有名字

//3.在工作表中创建第一行,作为标题行

HSSFRow headRow = sheet.createRow(0);

//给标题行的每一格写入数据

headRow.createCell(0).setCellValue("分区编号");

headRow.createCell(1).setCellValue("区域编码");

headRow.createCell(2).setCellValue("关键字");

headRow.createCell(3).setCellValue("起始号");

headRow.createCell(4).setCellValue("结束号");

headRow.createCell(5).setCellValue("单双号");

headRow.createCell(6).setCellValue("位置信息");

//4.在工作表中写入其他数据行,每一个分区对应一行数据

for (Subarea subarea : subareaList) {

HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum()+1);//从第二行开始写

dataRow.createCell(0).setCellValue(subarea.getId());

dataRow.createCell(1).setCellValue(subarea.getRegion().getId());

dataRow.createCell(2).setCellValue(subarea.getAddresskey());

dataRow.createCell(3).setCellValue(subarea.getStartnum());

dataRow.createCell(4).setCellValue(subarea.getEndnum());

dataRow.createCell(5).setCellValue(subarea.getSingle().toString());//Character不识别

dataRow.createCell(6).setCellValue(subarea.getPosition());

}

//设置客户端浏览器用于识别附件的两个参数Content-Type和Content-Disposition

//文件名

String downFilename="分区数据.xls";

//获取文件的MIME类型:

String contentType=ServletActionContext.getServletContext().getMimeType(downFilename);

//将MIME类型放入响应

ServletActionContext.getResponse().setContentType(contentType);

//浏览器类型

String agent = ServletActionContext.getRequest().getHeader("user-agent");

//附件名编码,解决中文乱码问题

downFilename = FileUtils.encodeDownloadFilename(downFilename, agent);

//获取附件的名字和下载方式

String contentDisposition="attachment;filename="+downFilename;

//将附件名字和下载方式放入响应头信息中

ServletActionContext.getResponse().setHeader("Content-Disposition", contentDisposition);

//将Excel的文件流写入到客户端响应中

hssfWorkbook.write(ServletActionContext.getResponse().getOutputStream());

return NONE;

}

SubareaService:

/**

* 根据条件查询分区列表

* @param specification

* @return

*/

public List<Subarea> findSubareaListByspecification(Specification<Subarea> specification);

SubareaServiceImpl:

public List<Subarea> findSubareaListByspecification(Specification<Subarea> specification) {

return subareaDAO.findAll(specification);

}

提示:

可将Specification对象的获取抽取出来一个单独的方法,可简化代码。

BOS物流管理系统-第五天

BOS物流管理系统-第五天

BOS物流管理系统-第五天

  1. 定区管理—定区添加

需求分析:

了解什么是定区?取派员固定的配送区域,该区域需要配置管理。它是动态可调整的。

定区是分区的集合,实际业务中,定区需要指定多名取派员负责,(需开发取派员排班功能、收派时间管理)。

这里简化业务,一个定区关联了一个取派员。

BOS物流管理系统-第五天

目标:

BOS物流管理系统-第五天

定区添加,主要是关联 取派员和分区。即定区是由哪个取派员负责的,该定区包含哪几个分区。

  1. 取派员下来列表的数据填充

目标:显示取派员的下拉列表:

BOS物流管理系统-第五天

开发步骤:

  1. 客户端页码

BOS物流管理系统-第五天

注意:从业务角度上说,要显示的快递员必须是未作废的、正常的。

  1. 服务端代码

StaffAction:

BOS物流管理系统-第五天

//异步请求没有删除标记的派送员列表

@Action(value="staff_listNoDelAjax")

public String listNoDelAjax(){

//调用业务层

List<Staff> staffList= staffService.findStaffListForNoDel();

//压入栈顶

ActionContext.getContext().getValueStack().push(staffList);

return JSON;

}

StaffService:

/**

* 查询所有没有作废的员工

* @return

*/

public List<Staff> findStaffListForNoDel();

StaffServiceImpl:

public List<Staff> findStaffListForNoDel() {

//调用dao层查询,查询未作废的派送员

return staffDAO.findByDeltag('0');

}

StaffDAO:

//根据删除标志来查询派送员

//使用了自动参数查找的查询策略,即根据删除标记来查询派送员列表

List<Staff> findByDeltag(Character deltag);

  1. 未分配的分区列表的表格(Datagrid)显示

目标:在添加页面显示未分配的分区列表,用户关联分区。

BOS物流管理系统-第五天

  1. 客户端代码

BOS物流管理系统-第五天

  1. 服务端代码

SubareaAction:

//查询所有没有定区关联的分区列表

@Action(value="subarea_listNoDecidedZone")

public String listNoDecidedZone(){

//调用业务层查询

List<Subarea> subareaList= subareaService.findSubareaListForNoDecidedZone();

//压入栈顶

ActionContext.getContext().getValueStack().push(subareaList);

return JSON;

}

SubareaService:

/**

* 查询出所有没有被定区关联的分区

* @return

*/

public List<Subarea> findSubareaListForNoDecidedZone();

SubareaServiceImpl:

public List<Subarea> findSubareaListForNoDecidedZone() {

//调用dao层

return subareaDAO.findByDecidedZoneIsNull();

//        return subareaDAO.findSubareaListForNoDecidedZone();

}

SubareaDAO:

//查询定区是空的分区:方法一:使用Spring Data JPA的属性表达式

List<Subarea> findByDecidedZoneIsNull();

//查询定区是空的分区:方法二:直接写语句

@Query("from Subarea where decidedZone is null")//注意面向对象的写法

List<Subarea> findSubareaListForNoDecidedZone();

  1. 定区添加的实现

目标:保存定区数据,在分区表中使用外键关联定区。

  1. 解决请求中定区编号和分区编号的参数名称冲突的问题

1)客户端代码

decidedzone.jsp:

BOS物流管理系统-第五天

BOS物流管理系统-第五天

点击添加form中save按钮,提交form (验证功能)

//保存定区

$("save").click(){

//表单校验

if($("#decidedZoneForm").form("validate")){

$("#decidedZoneForm").submit();//提交表单保存数据

}

};

2)服务端代码:

PO采用手动指定id的方式:

BOS物流管理系统-第五天

【思考】

多个分区如何封装?

分区的datagrid勾选后,在表单中提交什么?

问题: 分区datagrid 勾选 分区编号,会不会随form 提交 ???

BOS物流管理系统-第五天

相当于页面上有:很多checkbox

BOS物流管理系统-第五天

发现提交的是表单复选框的字段。

但请求参数中定区编号和分区编号的参数名称会发生冲突:

分区编号提交时 也为id 和定区编号冲突

解决方案:

1)修改datagrid的复选框的field的名字:

BOS物流管理系统-第五天

  1. 服务端代码

修改服务器返回json,含有subareaId , 修改Subarea

Subarea:

//添加含有subareaId的getter方法,为了前端json显示该参数

@Transient

public String getSubareaId(){

return id;

}

测试:

BOS物流管理系统-第五天

  1. 添加定区的服务端代码实现

服务端代码如下:

DecidedZoneAction:

//定区的Action

@ParentPackage("basic-bos")

@Namespace("/")

@Controller

@Scope("prototype")

public class DecidedZoneAction extends BaseAction<DecidedZone>{

//属性驱动接受关联的分区

private String[] subareaId;

public void setSubareaId(String[] subareaId) {

this.subareaId = subareaId;

}

//注入service

@Autowired

private DecideZoneService decideZoneService;

@Action(value="decideZone_save",results={@Result(name=SUCCESS,location="/WEB-INF/pages/base/decidedzone.jsp")})

public String save(){

//调用业务层保存定区

decideZoneService.saveDecideZone(model,subareaId);

return SUCCESS;

}

}

DecidedZoneService:

//定区业务层接口

public interface DecideZoneService {

/**

* 保存定区,并关联分区

* @param decidedZone

* @param subareaIdArray

*/

public void saveDecideZone(DecidedZone decidedZone, String[] subareaIdArray);

}

DecidedZoneServiceImpl:

//定区业务层实现类

@Service

@Transactional

public class DecideZoneServiceImpl implements DecideZoneService{

//注入dao

@Autowired

private DecideZoneDAO decideZoneDAO;

@Autowired

private SubareaDAO subareaDAO;

public void saveDecideZone(DecidedZone decidedZone, String[] subareaIdArray) {

//保存定区(可能bug)

decideZoneDAO.save(decidedZone);

//        decidedZoneDAO.saveAndFlush(decidedZone);//flush,刷出,

if(subareaIdArray!=null){//是否关联了分区

//分区关联定区(更新分区)(快照?全属性更新?)

for (String subareaId: subareaId) {

//1。快照更新(先查出来,再设置属性)--critieral

//                Subarea subarea = subareaDAO.findOne(subareaId);

//                subarea.setDecidedZone(decidedZone);

//2.直接发出更新语句

subareaDAO.updateForDecidedZone(subareaId,decidedZone);

}

}

}

}

DecidedZoneDAO:

//定区dao

public interface DecideZoneDAO extends JpaRepository<DecidedZone, String>{

}

SubareaDAO:

/**

* 更新分区的定区外键字段

* @param subareaId

* @param decidedZone

*/

@Modifying

@Query("update Subarea set decidedZone =?2 where id = ?1")

public void updateForDecidedZone(String subareaId, DecidedZone decidedZone);

【hibernate的快照问题】

BOS物流管理系统-第五天

  1. 定区管理—分页条件查询

目标:分页条件查询(和分区列表查询一样,多表关联查询)

BOS物流管理系统-第五天

定区编码:T_BC_DECIDEDZONE

所属单位:T_BC_STAFF

是否关联分区:T_BC_SUBAREA

BOS物流管理系统-第五天

回顾条件分页列表查询:

  1. 前端:调用easyui的datagrid的load方法,参数就是条件(条件缓存到了dg);使用了jq的扩展方法,获取到了表单的所有参数的值,"拼接"为json对象,传递给参数。
  2. 后端:接收前端参数(分页条件+业务条件)

    分页条件:page当前页码,rows:最大记录数—baseAction。

    业务条件:模型驱动封装model(如果model封装不了,使用属性驱动或者直接getParameter())

    编码:先拼接分页条件对象+规范条件对象-----》findAll(,);

    Pageable pageable=new PageRequest (page-1,rows);

    Specification spec = new Specification(){

    拼接条件-类似于criteria

    CriteriaBuilder:构建条件的工具:cb

    Predicate:where后面的条件对象(相当于name=?)

    可以有N个,这些条件可以and 、or,条件随意组合,无需考虑语句怎么写。

    root根对象,主查询对象

    root.join(关联属性,连接方式)子查询对象

    最后:查询:Page pageResponse=dao.findAll(pageable, spec);

开发步骤:

  1. 前端代码

修改表单

decidedzone.jsp

BOS物流管理系统-第五天

jquery-extends-customization.js

BOS物流管理系统-第五天

/**

* jquery自定义扩展

*/

//注册serializeJson方法:可将form参数转换为json对象

$.fn.serializeJson=function(){

var serializeObj={};

var array=this.serializeArray();

var str=this.serialize();

$(array).each(function(){

if(serializeObj[this.name]){

if($.isArray(serializeObj[this.name])){

serializeObj[this.name].push(this.value);

}else{

serializeObj[this.name]=[serializeObj[this.name],this.value];

}

}else{

serializeObj[this.name]=this.value;

}

});

return serializeObj;

};

decidedzone.jsp:

<!-- 导入jquery自定义扩展 -->

<script type="text/javascript"

src="${pageContext.request.contextPath }/js/jquery-extends-customization.js"></script>

点击查询按钮时,将Form表单数据转换为json,加载(load)到Datagrid的中缓存起来,自动发起新的请求。

//查询按钮事件

$("#btn").click(function(){

//alert("执行查询...");

//form表单数据转换为json

var params= $("#searchForm").serializeJson();

//调用datagrid的load方法,将参数传进去

$("#grid").datagrid("load",params);

//关闭查询窗口

$('#searchWindow').window("close");

});

BOS物流管理系统-第五天

  1. 服务器端代码(Spring Data JPA Specification):

DecidedZoneAction:

//属性驱动获取是否关联了分区

private String hasSubarea;

public void setHasSubarea(String hasSubarea) {

this.hasSubarea = hasSubarea;

}

//列表分页查询定区

@Action("decidedZone_listpage")

public String listpage(){

//分页数据

Pageable pageable = new PageRequest(page-1, rows);

//业务条件

Specification<DecidedZone> specification = new Specification<DecidedZone>() {

public Predicate toPredicate(Root<DecidedZone> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

//条件表达式集合

Predicate predicateAnd = cb.conjunction();//and//交集

Predicate predicateOr = cb.disjunction();//or//并集

//定区编码条件-当前表

if(StringUtils.isNotBlank(model.getId())){

predicateAnd.getExpressions().add(cb.equal(root.get("id").as(String.class), model.getId()));

}

//所属单位

if(model.getStaff()!=null){

//多表关联

//+++++关联员工表:多对一

Join<DecidedZone, Staff> staffJoin = root.join(root.getModel().getSingularAttribute("staff", Staff.class), JoinType.LEFT);

//根据取派员的单位作为条件

if(StringUtils.isNotBlank(model.getStaff().getStation())){

predicateAnd.getExpressions().add(

cb.like(staffJoin.get("station").as(String.class), "%"+model.getStaff().getStation()+"%")

);

}

}

//+++++关联分区表:一对多

//是否关联分区

if(StringUtils.isNotBlank(hasSubarea)){

//因为面向对象,只需要看看其关联集合是否为空即可。这里无需显式使用多表关联,

if(hasSubarea.equals("1")){

//关联了分区

predicateAnd.getExpressions().add(

cb.isNotEmpty(root.get("subareas").as(Set.class))

);

}else{

//没有关联分区

predicateAnd.getExpressions().add(

cb.isEmpty(root.get("subareas").as(Set.class))

);

}

}

return predicateAnd;

}

};

//调用业务层查询

Page<DecidedZone> page= decideZoneService.findDecideZoneListPage(pageable,specification);

//转换为datagrid所需要的格式

Map<String, Object> resultMap = new HashMap<String, Object>();

resultMap.put("total", page.getTotalElements());

resultMap.put("rows", page.getContent());

//压入栈顶

ActionContext.getContext().getValueStack().push(resultMap);

return JSON;

}

DecideZoneService:

/**

* 分页查询定区列表

* @param pageable

* @param specification

* @return

*/

public Page<DecidedZone> findDecideZoneListPage(Pageable pageable, Specification<DecidedZone> specification);

DecideZoneServiceImpl:

public Page<DecidedZone> findDecideZoneListPage(Pageable pageable, Specification<DecidedZone> specification) {

return decideZoneDAO.findAll(specification, pageable);

}

DecideZoneDAO:

//定区dao

public interface DecideZoneDAO extends JpaRepository<DecidedZone, String>,JpaSpecificationExecutor<DecidedZone>{

}

测试:

添加多条数据测试列表。

BOS物流管理系统-第五天

  1. 定区管理—定区关联客户

目标:

BOS物流管理系统-第五天

  1. 需求分析

物流系统中划分了定区,不同的取派员被分配管理着不同的定区,每个定区中又包含多个不同的分区;

客户居住在某个区域的某个分区中,而分区又属于定区,因此,当客户下单后,会自动关联分配对应的取派员(通过定区识别),从而实现自动派单。

下面,我们做的功能就是手动将客户和定区进行关联,以方便系统后期的查询和管理、自动下单等。

BOS物流管理系统-第五天

这里有个特别的业务技术点就是,远程数据交互。

BOS物流管理系统-第五天

现在系统的很多模块都有相关接口的描述,即远程系统交互接口。不同的数据存放到不同的系统中,以实现SOA。

ERP:企业全套解决方案。包含了N多的子系统或子模块。

CRM:(Customer Relation Managerment):客户关系管理:客户信息。

我们的系统子系统交互关系:

客户信息---CRM系统

定区信息---BOS系统(后台管理系统-核心业务管理系统)

BOS系统必须远程访问CRM系统,获取客户信息,然后关联定区。

在CRM中建立一张表,模拟一张客户表,用来存放客户信息。客户一般来自于电话、互联网平台,客服(坐席)-BOS—也可以添加到crm中

BOS读取crm表中的客户数据。

方案:

传统方案:数据库连接(开个用户直连-一个库,Oracle的DBlink-两个库)--跨数据库和跨域访问。

现在的方案:接口技术连接。----SOA思想

BOS物流管理系统-第五天

功能分析:

BOS物流管理系统-第五天

  • 左面select 加载所有未关联定区的客户
  • 右面select 加载当前选中定区,已经关联的客户
  • 将移动到右面 select中所有客户,关联到当前选中的定区上
  1. 技术选型:接口技术

常见远程调用技术:

WebService(传统的—大Web Service)

SOCKET

RMI

Hessian

http-restful

上述技术统称 Web Service

各种远程调用技术效率对比:

SOCKET》 RMI》Hessian》http-restful》WebService

WebService

优点:早期非常流行,大公司的弄的,标准的,很容易跨平台,跨语言。设计的时候在网络上传输。

缺点:额外使用soap协议(xml包装-消息太大---解析成本),效率不高,大公司绑架,在开源的时代,很多人抵制它。Webservice没有集群的支持。--集群化思想。

--阿里 duble—支持集群

Hessian:

优点:二进制传输,效率超高

缺点:跨平台还是有点小问题。

选型:

如果跨平台跨语言,不太追求效率,可以使用webservice。

如果跨平台跨语言,追求效率,http-restful.

同平台(都是java),追求效率:Hessian

基础架构;

BOS物流管理系统-第五天

我们今天:子系统间的交互也用Webservice。

传统行业和互联网行业:

技术:CXF与Spring整合。

参考Spring的规范:

BOS物流管理系统-第五天

  1. CRM系统的CXF服务端构建开发

    1. SSH基础环境搭建

技术架构:

Spring + Hibernate(spring和hibernate直接整合)+CXF+Oracle+gson

新建Maven项目:

BOS物流管理系统-第五天

引入Maven坐标:

Spring、Hibernate、数据库和连接池、日志、Servlet、JSP、junit、编译版本覆盖、tomcat端口覆盖8888:

Pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>cn.itcast.projects</groupId>

<artifactId>mavencrm</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<name>mavencrm</name>

<description>物流的客服子系统</description>

<properties>

<spring.version>3.2.12.RELEASE</spring.version>

<hibernate.version>3.6.10.Final</hibernate.version>

<slf4j.version>1.7.5</slf4j.version>

<c3p0.version>0.9.1.2</c3p0.version>

<oracle.version>10.2.0.4.0</oracle.version>

<servlet.version>2.5</servlet.version>

<jsp.version>2.0</jsp.version>

<junit.version>4.11</junit.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aspects</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-core</artifactId>

<version>${hibernate.version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>c3p0</groupId>

<artifactId>c3p0</artifactId>

<version>${c3p0.version}</version>

</dependency>

<dependency>

<groupId>com.oracle</groupId>

<artifactId>ojdbc14</artifactId>

<version>${oracle.version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>servlet-api</artifactId>

<version>${servlet.version}</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jsp-api</artifactId>

<version>${jsp.version}</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>javassist</groupId>

<artifactId>javassist</artifactId>

<version>3.12.0.GA</version>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>tomcat-maven-plugin</artifactId>

<version>1.1</version>

<configuration>

<port>8888</port>

</configuration>

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-resources-plugin</artifactId>

<version>2.6</version>

<configuration>

<encoding>UTF-8</encoding>

</configuration>

</plugin>

<!-- 编译的jdk版本 -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.1</version>

<configuration>

<source>1.7</source>

<target>1.7</target>

</configuration>

</plugin>

</plugins>

</build>

</project>

配置Spring整合Hibernate(几个配置文件:applicationContext.xml、db.properties,web.xml,log4j.properties等)

BOS物流管理系统-第五天

db.properties:

jdbc.driverClass=oracle.jdbc.driver.OracleDriver

jdbc.url=jdbc:oracle:thin:@localhost:1521:xe

jdbc.username=CRM25

jdbc.password=CRM25

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- 引入外部的属性配置文件     -->

<context:property-placeholder location="classpath:db.properties"/>

<!-- 连接池 -->

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

<property name="driverClass" value="${jdbc.driverClass}"/>

<property name="jdbcUrl" value="${jdbc.url}"/>

<property name="user" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

</bean>

<!-- spring来整合hibernate

AnnotationSessionFactoryBean:spring提供的专门来整合hibernate注解

-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

<!-- 注入数据源 -->

<property name="dataSource" ref="dataSource"/>

<!-- hibernate一般属性 -->

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>

<prop key="hibernate.hbm2ddl.auto">update</prop>

<prop key="hibernate.show_sql">true</prop>

<prop key="hibernate.format_sql">true </prop>

</props>

</property>

<!-- 映射的类

packagesToScan:扫描哪个包,会将脑门上带@Entity的类,注册为实体类

-->

<property name="packagesToScan">

<list>

<value>cn.itcast.crm.domain</value>

</list>

</property>

</bean>

<!-- 事务管理器 -->

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory" ref="sessionFactory" />

</bean>

<!-- 注解驱动 -->

<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- 开启spring的bean组件扫描(默认也会开启注解功能) -->

<context:component-scan base-package="cn.itcast.crm.service,cn.itcast.crm.dao"/>

</beans>

web.xml:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<display-name>宅急送客服管理系统</display-name>

<!-- spring配置文件位置 -->

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:applicationContext.xml</param-value>

</context-param>

<!-- spring核心监听器 -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<!-- 欢迎页面 -->

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

新建实体类:

BOS物流管理系统-第五天

Customer:

@Entity

@Table(name="t_customer",schema="CRM25")

public class Customer {

@Id

@GeneratedValue

private Integer id;//OID属性

private String name;//客户名称

private String residence;//住所

private String telephone;//联系电话

private String decidedZoneId;//定区编号(客户和定区关联的字段)

}

测试上面的配置,启动服务,自动建表:

需要先建立用户CRM25:

BOS物流管理系统-第五天

  1. CXF环境的搭建

关于CXF:

BOS物流管理系统-第五天

BOS物流管理系统-第五天

引入Maven坐标:(javasist(单独导入,如果需要的)、CXF(官方))

Pom.xml:

<properties>

<cxf.version>3.1.5</cxf.version>

</properties>

<dependencies>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-frontend-jaxws</artifactId>

<version>${cxf.version}</version>

</dependency>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-rt-transports-http</artifactId>

<version>${cxf.version}</version>

</dependency>

</dependencies>

</project>

引入cxf的jar最好参考官方的文档:

BOS物流管理系统-第五天

规划java开发包:

BOS物流管理系统-第五天

回顾:Webservice的服务端开发:

第一步:编写SEI:接口和实现类

第二步:加注解

第三步:发布:使用spring整合的发布。

编写SEI(获取定区未关联和已经关联的数据、客户关联定区):

CustomerService:

//SEI:客户的业务接口

@WebService(

name="CustomerService",

serviceName="CRMService",

portName="CRMServicePort",

targetNamespace="http://ws.itcast.cn/"

)

@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)//soap1.2

public interface CustomerService {

@WebMethod(operationName="operateCustomer")

public @WebResult(name="result") String operateCustomer(@WebParam(name="param")String param);

}

CustomerServiceImpl:

//SEI:客户的业务实现类

@Service("customerService")

@Transactional

public class CustomerServiceImpl implements CustomerService {

//注入DAO

@Autowired

private GenericDAO<Customer, Integer> customerDAO;

@Override

/*参数定义规则:

*{'opertype':'101'}

,代表获取没有定区的用户,103,代表获取有定区的用户

*/

public String operateCustomer(String param) {

return "{'resultstatus':1,'data':[{},{}]}";

}

}

通用DAO:(略,参考Spring课程)

BOS物流管理系统-第五天

配置Spring,发布服务。

引入名称空间:

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd

    http://www.springframework.org/schema/tx

    http://www.springframework.org/schema/tx/spring-tx.xsd

    http://cxf.apache.org/jaxws

    http://cxf.apache.org/schemas/jaxws.xsd">

CXF服务端配置:

<!-- 配置CXF服务 -->

<!-- Import Apache CXF Bean Definition -->

<import resource="classpath:META-INF/cxf/cxf.xml"/>

<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>

<!-- 配置我们自己的ws服务 -->

<jaxws:server id="customerWebService" address="/CustomerWS" serviceClass="cn.itcast.crm.service.CustomerService">

<jaxws:serviceBean>

<ref bean="customerService"/>

</jaxws:serviceBean>

<!-- 配置日志拦截器 -->

<jaxws:inInterceptors>

<ref bean="loggingInInterceptor"/>

</jaxws:inInterceptors>

<jaxws:outInterceptors>

<ref bean="loggingOutInterceptor"/>

</jaxws:outInterceptors>

</jaxws:server>

<!-- 实例化日志拦截器 -->

<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>

<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>

CXF的核心控制器:

Web.xml

<!-- cxf的核心控制器 -->

<servlet>

<servlet-name>cxf</servlet-name>

<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>cxf</servlet-name>

<url-pattern>/ws/*</url-pattern>

</servlet-mapping>

  1. 接口联调

集团接口,各地市分期调试。

SOAPUI测试:

http://localhost:9999/mavencrm/ws/CustomerWS?wsdl

SOAP调试(服务端调试和双方初步接口联调):

工具SOAPUI:

BOS物流管理系统-第五天

  1. 接口的设计

Webservice支持复杂数据类型的传输,如list ,Customer。

但:

如果使用了复杂数据类型,可能失去跨平台的特性了。因为如customer这种数据类型,是跟语言有关的。

解决方案:

传字符串就行了。因为不同平台的语言,字符串的设计,都差不多。

问题又来了,用字符串能传递复杂数据类型么?

传递一张二维表的数据。

解决方案:

可以使用一定格式的字符串来传递。

以前流行XML格式,来封装业务数据。缺点:编写复杂,解析麻烦,内容过大。

现在流行JSON格式!!!!!!!json任何平台都能识别和解析。

现在理念:用JSON来描述任意的对象。

如何开发(步骤):

  1. 双方要协商,要传哪些数据(请求和响应)
  2. 设计json格式
  3. 开发接口
  4. 联调
  5. 上线发布。

设计json的格式:(约定—双方)

请求的数据:

{'opertype':'101','param':{'参数1':'参数1的值',……………}}

opertype':操作类型:你要做什么,比如,你要查询未关联的客户列表,双方约定101,如果是查询已经关联的客户列表:102,

如果某个业务需要传参,那么param该参数就有效了。比如,根据客户编号,查询客户信息:{'opertype':'301','param':{'参数1':'参数1的值',……………}}

响应的数据:

{'status':1,'data':[{},{},{}]}

Status:状态码,作用告诉客户端,你的请求的情况,1代表正常,向下解析数据了,如果0,说明未知异常,如果2,您的参数格式不正确。

使用json来传递数据有什么好处?

接口业务的开发重心发生变化,以前是一个业务开发一个接口,工作量在接口上;

现在N个业务用一个接口,工作量转移到解析json上面了。

扩展:

xml和java对象的 相互转换xStream:

BOS物流管理系统-第五天

json的序列化(封装)和反序列化(解析-java对象):

BOS物流管理系统-第五天

本课程使用Gson-google

  1. Json解析-GSON

  1. GSON的下载安装

https://github.com/google/gson

BOS物流管理系统-第五天

通过Maven坐标引入:

<gson.version>2.6.1</gson.version>

<dependency>

<groupId>com.google.code.gson</groupId>

<artifactId>gson</artifactId>

<version>${gson.version}</version>

</dependency>

Gson是google的一个开源、功能强大的json的序列化和反序列化的工具。

序列化:java----》json

反序列化:json---》java

  1. Gson的基本使用(了解)

//Gson instances are Thread-safe so you can reuse them freely across multiple threads.

Gson gson= new Gson();

//测试一下gson

//简单数据类型的序列化和反序列化

//map:json对象

//list:json数组

//-------json对象的操作

//序列化:map--->json

Map<String, Object> jsonMap=new HashMap<String,Object>();

jsonMap.put("username", "Rose");

jsonMap.put("age", 18);

String jsonObject = gson.toJson(jsonMap);

System.out.println(jsonObject);

//反序列化:json---》map

Map fanxuliehuaMap = gson.fromJson(jsonObject, Map.class);

System.out.println(fanxuliehuaMap);

//问题:大家以后在封装json的时候,尽量都使用字符串的值!(避免数字反序列化的时候自动变成double)

//-------json数组的操作

List<Customer> customerList = new ArrayList<>();

Customer customer1=new Customer();

customer1.setId(1001);

customer1.setName("Rose");

customerList.add(customer1);

Customer customer2=new Customer();

customer2.setId(1002);

customer2.setName("Jack");

customerList.add(customer2);

//序列化list---》json数组

String jsonArray = gson.toJson(customerList);

System.out.println(jsonArray);

//发现:java对象中为空的字段不会转换(可以控制)

//反序列化json数组---list

List fanxuliehuaList = gson.fromJson(jsonArray, List.class);

System.out.println(fanxuliehuaList);

//结果:序列化的时候,能自动将java对象转换为json字符串,但反序列化的时候,不能自动转换为java对象。

//默认所有的json对象都会自动转变换为map(com.google.gson.internal.LinkedTreeMap)

System.out.println(fanxuliehuaList.get(0).getClass());

//如果对于简单业务来说,你就用map和list来封装json就可以了。

//对于复杂的一些业务来说,使用map感觉不是很好,一般使用javabean来封装,但反序列化有问题。

  1. GsonUtils-GSON工具类

  1. 抽取GsonUtils

//gson的工具类

public class GsonUtils {

//线程安全的

private static final Gson GSON;

static{

GSON = new GsonBuilder()

.excludeFieldsWithoutExposeAnnotation()//打开Export注解,但打开了这个注解,副作用,要转换和不转换都要加注解

//     .serializeNulls() //是否序列化空值

.setDateFormat("yyyy-MM-dd HH:mm:ss")//序列化日期格式 "yyyy-MM-dd"

//     .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)//会把字段首字母大写

.setPrettyPrinting() //自动格式化换行

。0的版本的时候解析,但0。1版本不解析

.create();

}

//获取gson解析器

public static Gson getGson(){

return GSON;

}

//对象转换为json

public static String toJson(Object object){

return GSON.toJson(object);

}

//JSON转换为对象1--普通类型

public static <T> T fromJson(String json, Class<T> classOfT){

return GSON.fromJson(json, classOfT);

}

//JSON转换为对象-针对泛型的类型

public static <T> T fromJson(String json, Type typeOfT){

return GSON.fromJson(json, typeOfT);

}

}

提示:

TypeToken,它是gson提供的数据类型转换器,可以支持各种带泛型的类型数据的转换。

  1. 序列化和反序列化示例

【示例1】

1.普通类型(Customer)对象的序列化和反序列化。

//        Gson gson = GsonUtils.getGson();

//普通类型的

//序列化

Customer customer = new Customer();

customer.setId(1111);

customer.setName("Rose");

customer.setResidence("上海");

customer.setTelephone("1333333333");

String cJson = GsonUtils.toJson(customer);

System.out.println(cJson);

//反序列化:

Customer newcustomer = GsonUtils.fromJson(cJson, Customer.class);

System.out.println(newcustomer);

Customer:

BOS物流管理系统-第五天

public class Customer {

@Expose

private Integer id;//OID属性,使用的Oracle序列,

@Expose

private String name;//客户名称

@Expose

@SerializedName("address")

private String residence;//住所

@Expose

private String telephone;//联系电话

@Expose

private String decidedZoneId;//定区编号(客户和定区关联的字段)//外键关联到bos系统的定区的主键

提示:

Gson可以不需要注解来序列化和反序列化对象;但要使用更复杂功能,需要打开注解,那么没有注解的字段将不会被序列化和反序列化。

【示例2】

1.泛型对象(List<T>或Map<T1,T2>)的序列化和反序列化

//泛型的类型

//序列化(都没问题)

List<Customer> customerList = new ArrayList<>();

Customer customer1=new Customer();

customer1.setId(1001);

customer1.setName("Rose");

customerList.add(customer1);

Customer customer2=new Customer();

customer2.setId(1002);

customer2.setName("Jack");

customerList.add(customer2);

String listjson = GsonUtils.toJson(customerList);

System.out.println(listjson);

//反序列化

List list1 = GsonUtils.fromJson(listjson, List.class);

System.out.println(list1);

//泛型类型需要使用另外一个api

//         Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();

Type typeOfT = new TypeToken<List<Customer>>(){}.getType();

Object list2 = GsonUtils.fromJson(listjson, typeOfT);

System.out.println(list2);

  1. 重点和作业

  1. 导出Excel的文件(文件下载、POI)
  2. 定区添加(Datagrid的checked提交)
  3. 定区的分页条件列表查询(Spring Data JPA 的Specification)
  4. CRM系统提供客户信息操作的服务(CXF服务端技术,GSON工具技术)--基于SOA
  5. BOS系统从远程操作客户信息(CXF客户端技术,GSON技术、列表选择框)

课后:使用Hessian实现远程调用。

提示:建议团队作战,分析好需求,可以手动画页面流程图,页面技术如何实现,后台如何实现等。

额外:其他在课堂上未实现的功能,实现一下。