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,备份出来。

需求分析:

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

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

【回顾】

文件下载的要求:

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

开发步骤:

  1. 客户端页码代码

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

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

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

  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对象的获取抽取出来一个单独的方法,可简化代码。

  1. 定区管理—定区添加

需求分析:

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

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

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

目标:

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

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

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

开发步骤:

  1. 客户端页码

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

  1. 服务端代码

StaffAction:

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

@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)显示

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

  1. 客户端代码

  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:

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

//保存定区

$("save").click(){

//表单校验

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

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

}

};

2)服务端代码:

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

【思考】

多个分区如何封装?

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

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

相当于页面上有:很多checkbox

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

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

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

解决方案:

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

  1. 服务端代码

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

Subarea:

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

@Transient

public String getSubareaId(){

return id;

}

测试:

  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的快照问题】

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

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

定区编码:T_BC_DECIDEDZONE

所属单位:T_BC_STAFF

是否关联分区:T_BC_SUBAREA

回顾条件分页列表查询:

  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

jquery-extends-customization.js

/**

* 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");

});

  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>{

}

测试:

添加多条数据测试列表。

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

目标:

  1. 需求分析

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

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

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

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

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

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

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

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

客户信息---CRM系统

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

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

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

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

方案:

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

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

功能分析:

  • 左面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

基础架构;

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

传统行业和互联网行业:

技术:CXF与Spring整合。

参考Spring的规范:

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

    1. SSH基础环境搭建

技术架构:

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

新建Maven项目:

引入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等)

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>

新建实体类:

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:

  1. CXF环境的搭建

关于CXF:

引入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最好参考官方的文档:

规划java开发包:

回顾: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'}

*opertype:101,代表获取所有用户,102,代表获取没有定区的用户,103,代表获取有定区的用户

*/

public String operateCustomer(String param) {

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

}

}

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

配置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:

  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:

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

本课程使用Gson-google

  1. Json解析-GSON

  1. GSON的下载安装

https://github.com/google/gson

通过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() //自动格式化换行

//     .setVersion(1.0) //需要结合注解使用,有的字段在1。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:

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实现远程调用。

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

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

转载于:https://www.cnblogs.com/beyondcj/p/6271234.html

BOS物流管理系统-第五天相关推荐

  1. BOS物流管理系统-第一天

    BOS物流管理系统-第一天-系统分析.环境搭建.前端框架 BoBo老师 整体项目内容目标: 对项目概述的一些理解 亮点技术的学习 注意学习方式:优先完成当天代码. 其他内容. 最终: 学到新的技术,会 ...

  2. 基于SSM实现的物流管理系统(完美运行,包含数据库源代码,可远程调试)

    文章目录 前言 一.技术实现 二.系统功能 三.系统登陆界面 四.管理员界面 五.员工界面 六.客户界面 总结 前言 本项目是一套基于SSM实现的物流管理系统 或 物流配送系统 或 快递物流系统 或 ...

  3. 基于ssm快递物流管理系统源码获取(java毕业设计)

    基于ssm快递物流管理系统 快递物流是基于ssm框架,java语言和mysql数据库开发,系统的角色主要分为用户和管理员两个,其中用户的主要功能是可以查看系统的新闻,公司介绍,能够在线下物流订单,能够 ...

  4. java宅急送下载_java宅急送BOS物流项目

    本套视频是宅急送BOS物流项目推出历时三年后推出的一套综合项目实战视频,该视频针对有SSH(Struts2+Spring+Hibernate)框架基础- 课程简介 [已更新] 隐藏内容,您需要满足以下 ...

  5. 基于 SpringBoot + Vue 的物流管理系统(附源码)

    国庆期间哪也没去,在家闲来无事,写了一个基于 SpringBoot + Vue 的物流管理系统,把源码分享给大家,在文章结尾处,自行获取即可~ 一.介绍 基于Java的物流管理系统. 二.软件架构 系 ...

  6. 【JAVA程序设计】基于SSM+VUE前后端分离的物流管理系统

    基于SSM+VUE前后端分离的物流管理系统 零.项目获取 一.项目简介 二.开发环境 三.项目技术 四.系统架构 五.运行截图 零.项目获取 获取方式(点击下载):是云猿实战 项目经过多人测试运行,可 ...

  7. 数据结构课程设计 物流管理系统

    1 问题描述 在当今的电子商务时代,全球物流产业有了新的发展趋势.现代物流服务的核心目标是在物流全过程中以最小的综合成本来满足顾客的需求. 在这个"互联网+"的时代,众多的学者研究 ...

  8. 物流信息管理系统MySQL设计,物流管理系统的SQL数据库设计(含代码)

    物流管理系统的SQL数据库设计(含代码) 物流管理信息系统的数据库设计班级xxx系统名称:物流管理信息系统一.需求分析物流管理系统是为制造商和零售商设计的管理系统数据库系统,目的是:1.实现上游制造商 ...

  9. 不错!基于Springboot 2.0 + LayUI开发的物流管理系统(已开源)

    缺物流管理系统项目经验的,推荐看看这个项目. 项目需求说明 首先,发货客户与快递公司签订货运合同(货运单),把货物交给快递公司来托运,并按照货运合同的付款方式付款.快递公司根据货物运输线路,为货物配车 ...

最新文章

  1. Windows 搭建ASP.NET Boilerplate项目开发环境
  2. python爬虫意思_python为什么叫爬虫
  3. Java 中的某些方法为什么要求传入一个 Class 对象
  4. KTV阶段项目告一段落,让我们张开双臂迎接新阶段吧!!
  5. 使用Spring Data MongoDB和Spring Boot进行数据聚合
  6. SAS详细的下载与安装流程
  7. 线性代数(9):线性正交
  8. 全球免费开放的电子图书馆
  9. ffmpeg转码保留时间戳
  10. 2022考研复习第二十三周
  11. AE渲染加快速度,解决导出视频太慢的问题
  12. 18048 自由落体
  13. c语言编译器怎么防止优化变量,volatile关键字的作用:防止变量被编译器优化
  14. scnaf使用格式化字符串%d接受小数的情况
  15. GPU Counter功能更新|支持Adreno、PowerVR芯片
  16. 技术团队里什么样的人会被清除?抢老板的工作干合适吗?
  17. VMware12安装图解
  18. 框架 --mybatis(ORM映射)-数据库技术
  19. Timer定时器详解
  20. 智能床、智能床垫、智能睡眠监测器的调研分析

热门文章

  1. 高校车辆管理系统/车辆管理系统的设计与实现
  2. 测井计算机使用的总线,测井数据处理开始步入工作站时代
  3. 毕业设计_校园志愿者系统平台的设计与实现
  4. 使用 C# 修改文件创建时间(图片也可修改)
  5. 马哥Linux学习笔记1-2OS及Linux基础知识
  6. 地铁供电系统原理图_地铁小百科——带你认识地铁供电系统
  7. Andorid.bp文件和Anorid.rc文件入门
  8. TLS协议分析 与 现代加密通信协议设计
  9. 「微信同声传译」小程序插件:快速实现语音转文字、文本翻译、语音合成等能力...
  10. 存储卡、逻辑加密卡和CPU卡的相关知识