文件系统微服务器,SpringCloud微服务架构实战库存管理与分布式文件系统

库存管理与分布式文件系统

在电商平台的库存管理系统设计中,将涉及商品和本地图库的管理,这里我们将使用另一种数据开发框架 MyBatis进行数据库访问方面的设计,还将实现与分布式文件系统的对接使用。

本章实例的项目工程是一个商品微服务项目goods-microservice,可以从本文提供的源代码中下载,或者在IDEA中通过Git检出:检出代码后,请获取本实例使用的分支V2.1。本项目包含以下几个模块:

goods-object:公共对象设计。

goods-restapi:库存管理微服务API应用。goods-web:库存管理PC端Web应用。

190eebef391c2da15dce5269fb25c4c8.png

本篇文章要介绍的内容基于MyBatis的数据库开发,

数据库服务组装,

单元测试,

库存微服务接口开发,

库存管理的Web应用开发,

Web应用项目热部署设置基于MyBatis的数据库开发

有关数据库开发的整个过程是在模块 goods-restapi 中实现的,在这个模块中,我们将使用MyBatis开发框架实现数据库的访问设计。其中,有关数据源的配置及其相关监控与第6章的设计相同,不再说明。

使用经过组装的MyBatis 组件

这里我们将在使用MyBatis组件的基础上,再使用一个经过高级封装设计的MyBatis组件。使用这一组件不但能简化一些基本的查询设计,还能提升程序的性能。在项目对象模型中引入相关组件的依赖,代码如下所示:org.mybatis.spring.bootmybatis-spring-boot-starter1.3.1

tk.mybatismapper-spring-boot-starter2.0.4com.github.pagehelperKpagehelper-spring-boot-starter1.2.3

其中,有关tk.mybatis 的设计,如果读者感兴趣,可以登录GitHub官网,搜索Mybatis进行更进一步的了解。

数据对象及其表结构定义

在库存管理中,我们将主要创建一个商品对象Goods,它的定义如下所示:@Table (name = "tgoods")

@ Data

public class Goods {

//商品编号

@Id

@GeneratedValue(strategy =- GenerationType. IDENTITY)private Long id;

//商家编号

private Long merchantid;//主类编号

private Long sortsid;//子类编号

private Long subsid;//商品名称

private string name;//商品内容

private string contents;

//商品图片

private String photo;//价格

private Double price;//购买数量

private Integer buynum;//库存数量

private Integer reserve;//操作员

private String operator;//创建时间

@DateTimeFormat (pattern= "yyyy-MM-dd HH:mm : ss")private Date created;

}

在上面的代码中,主要解释下面几个内容:(1)注解@Table关联了数据库的表格t _goods。

(2)注解@Data使用了Lombok 工具,它会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode和 toString 等方法。

(3)注解@Id和注解@GeneratedValue将在数据创建或编辑时为对象取得数据库ID的值。

(4)注解@DateTimeFormat使用了日期格式化,以保证在数据存取中使用正确的日期格式。

商品对象在数据库中对应的表格为t_goods,这个表结构的定义如下所示:CREATE TABLE 'tgoods'(

'id' bigint (20) NOT NULL AUTO INCREMENT,

' contents' varchar(255)COLLATE utf8 bin DEFAULT NULL,'created' timestamp NOT NULL DEFAULT CURRENT TIMESTAMP,'merchantid' bigint (20) DEFAULT NULL,

'name'varchar(255)COLLATE utf8 bin DEFAULT NULL,

'operator' varchar(255) COLLATE utf8_bin DEFAULT NULL,'photo'varchar(255)COLLATE utf8_bin DEFAULT NULL,'price'double DEFAULT NULL,

'reserve' int(11)DEFAULT NULL,'sortsid' bigint(20) DEFAULT NULL,'subsid' bigint (20) DEFAULT NULL,' buynum' int (11) DEFAULT NULL,PRIMARY KEY ('id')

}

ENGINE=InnoDB AUTO INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8 bin;

在这个表结构定义中,我们设定商品编号由数据库实现自动生成,同时商品的创建时间默认使用当前时间戳自动赋值。

Mapper 与SQL定制

有了数据对象和表格之后,我们就可以进行 Mapper 设计及其相关的数据库查询定制了。针对商品管理的数据库查询操作来说,我们可以设计一个数据访问接口GoodsMapper,代码如所示:@Repository

public interface GoodsMapper extends MyMapper{

ListgetPage (@Param ( "map")Mapmap);

}

在这个接口设计中,我们只声明了一个getPage 的方法,用来取得商品的列表数据,但是有关数据的增删改查的基本操作,则没有做任何设计。这是因为在代码中我们继承MyMapper,它已经为我们实现了数据访问的基本操作。对应上面的 GoodsMapper 接口定义,我们设计一个 GoodsMapper.xml来定制相关的 SOL查询设计,代码如下所示:

SELECT g.* FROMt goods g WHERE1=1

AND g.name LIKE CONCAT('%',#{map.name}, '8')AND g.merchantid=#{map.merchantid}AND g.sortsid=#{map.sortsid]AND g.subsid=#{map.subsid}AND g.created >=#{map.created}

从上面的SQL查询设计中,我们实现了几个动态条件的查询设计,即可以根据传输的参数,通过商品名称、商家编号和商品创建时间等条件进行查询。数据库服务组装

下面封装一个商品数据的服务类GoodsService,代码如下所示:@service

CTransactional

public class GoodsService {

CAutowired

private GoodsMapper goodsMapper;

public String insert (Goods goods){

try {

goodsMapper.insert(goods);

return goods.getId().toString();}catch(Exception e){

e.printStackTrace();return e.getMessage();

public String update(Goods goods){

try {

goodsMapper. updateByPrimaryKey (goods) ;return goods.getId() .toString();

}catch(Exception e){

e.printStackTrace();return e.getMessage();

}

}

public String delete(Long id){

try{

goodsMapper.deleteByPrimaryKey (id);return id.toString();

}catch(Exception e){

e.printStackTrace();return e.getMessage();

public Goods getById (Long id){

try {

return goodsMapper.selectByPrimaryKey(id);}catch (Exception e){

e.printStackTrace();return null;

public PageInfogetPage (GoodsQ0 goodsQ0)throws Exception t

try {

Mapmap = MapToBeanUtil.transBean2Map(goodsQo);

if(!StringUtils.isEmpty(goodsQo.getName()){

map.put( "name" , goodsQo.getName());

}

if(!StringUtils.isEmpty(goodsQo.getMerchantid())){

map.put ("merchantid",goodsQo.getMerchantid());

}

if(!StringUtils.isEmpty(goodsQo.getSortsid())){

map.put("sortsid",goodsQo.getSortsid());

if(!StringUtils.isEmpty(goodsQo.getSubsid())){

map .put("subsid",goodsQo.getSubsid());

}

//把日期转为字符串

if (!StringUtils.isEmpty(goodsQo.getCreated())){

map. put("created",

CommonUtils.formatDateTime (goodsQo.getCreated()));

}

PageHelper.startPage (goodsQo.getPage(), goodsQo.getSize());Listlist = goodsMapper.getPage (map);

return new PageInfo (list);

}catch(Exception e){

e.printStackTrace();return null;

}

}

}

这是一个完整的代码,从中我们可以看到一些数据查询的基本操作,主要是通过调用接口GoodsMapper 的父接口来实现的,这是 tk.mybatis组件提供的功能。其中,分页设计的调用和实现是我们自定义的设计。注意,在参数传输中,日期数据必须进行格式化转换。单元测试

在完成数据服务的一系列设计后,我们就可以使用GoodsService进行单元测试了。下面的代码是一个插入商品数据的测试用例:org.mybatis.spring.bootmybatis-spring-boot-starter1.3.1

tk.mybatismapper-spring-boot-starter2.0.4com.github.pagehelperKpagehelper-spring-boot-starter1.2.3

参照这个测试用例,我们还可以对商品数据服务的其他操作进行测试。有关这部分的内容,可以参考源代码的设计。库存微服务接口开发

在模块 goods-restapi完成了商品数据服务的开发之后,我们就可以在这个模块中进行微服务接口的开发了。

在主程序中支持MyBatis

由于使用了MyBatis组件,在主程序GoodsRestApiApplication中必须增加对Mapper的扫描设置,代码如下所示:@Table (name = "tgoods")

@ Data

public class Goods {

//商品编号

@Id

@GeneratedValue(strategy =- GenerationType. IDENTITY)private Long id;

//商家编号

private Long merchantid;//主类编号

private Long sortsid;//子类编号

private Long subsid;//商品名称

private string name;//商品内容

private string contents;

//商品图片

private String photo;//价格

private Double price;//购买数量

private Integer buynum;//库存数量

private Integer reserve;//操作员

private String operator;//创建时间

@DateTimeFormat (pattern= "yyyy-MM-dd HH:mm : ss")private Date created;

}

基于REST协议的控制器设计

REST 的接口开发可以由Spring MVC的控制器实现,商品接口控制器GoodsRestController的部分实现代码如下所示:@RestController

@RequestMapping( " /goods")

public class GoodsRestController {

private static Logger logger =

LoggerFactory.getLogger(GoodsRestController.class);

@Autowired

private GoodsService goodsservice;

@GetMapping (value="/{id] ")

public String fnidById(@PathVariable Long id){

Goods goods =goodsService.getById(id);

return new Gson( .toJson (goods) ;

cGetMapping()

public string findAll(Integer index,Integer size,Long merchantid,

String name, Long sortsid, Long subsid,

String created) {

try{

GoodsQo goodsQ0 =new GoodsQ0();

if(CommonUtils.isNotNul1(index)){

goodsQo.setPage (index);

}

if(CommonUtils.isNotNull(size)){

goodsQo.setSize(size);

if(CommonUtils.isNotNull (merchantid)){

goodsQo.setMerchantid(merchantid);

}

if(CommonUtils.isNotNull(name)){

goodsQo.setName (name);

}

if(CommonUtils.isNotNull(sortsid)){

goodsQo.setSortsid(sortsid);

}

if(CommonUtils.isNotNull(subsid)){

goodsQo.setSubsid (subsid);

if(CommonUtils.isNotNull(created)){

SimpleDateFormat sdf = new SimpleDateFormat ("yyvvy-MM-dd

HH:mm:ss");

goodsQo .setCreated (sdf.parse(created));

PageInfopage= goodsService.getPage(goodsQ0);

return new Gson( .toJson (page);

]catch(Exception e){

e.printStackTrace();

)

return null;

@PostMapping()

public String save(@RequestBody GoodsQo goodsQo) throws Exceptiont

Goods goods = new Goods();

BeanUtils.copyProperties(goodsQo,goods);goods.setCreated(new Date());

String response = goodsService.insert(goods);

logger.info("新增=”+ response);

return response;

}

...

}

从上面代码中可以看出,我们通过商品数据服务GoodsService可以对外提供有关商品数据访问的各种接口。需要注意的是,上面分页查询使用了GET方法,所以参数的传输不能直接使用查询对象,必须每个参数单独指定。不过,我们在进行接口调用设计时,可以使用查询对象进行转换,具体可以参考7.5节。库存管理的Web应用开发

有关库存管理的Web应用的微服务开发,主要是通过调用商品管理的微服务接口实现的,这里面的设计和实现方法与第6章中类目管理的设计和实现方法十分相似,所以不再做全面的详细说明,只针对某些不同点进行简要的介绍。

库存管理的Web应用的微服务设计是在模块goods-web中实现的,这是一个独立的微服务应用,可以单独使用一个项目工程来设计。

公共对象的依赖引用

当我们管理商品时,必将用到商品的类目设定,所以在模块 goods-web中,为了方便使用相关的查询对象,必须有其相关的依赖引用,代码如下所示:CREATE TABLE 'tgoods'(

'id' bigint (20) NOT NULL AUTO INCREMENT,

' contents' varchar(255)COLLATE utf8 bin DEFAULT NULL,'created' timestamp NOT NULL DEFAULT CURRENT TIMESTAMP,'merchantid' bigint (20) DEFAULT NULL,

'name'varchar(255)COLLATE utf8 bin DEFAULT NULL,

'operator' varchar(255) COLLATE utf8_bin DEFAULT NULL,'photo'varchar(255)COLLATE utf8_bin DEFAULT NULL,'price'double DEFAULT NULL,

'reserve' int(11)DEFAULT NULL,'sortsid' bigint(20) DEFAULT NULL,'subsid' bigint (20) DEFAULT NULL,' buynum' int (11) DEFAULT NULL,PRIMARY KEY ('id')

}

ENGINE=InnoDB AUTO INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8 bin;

从上面的代码中可以看出,这里同时引用了商品和类目的查询对象依赖。其中,为了减少—些组件的重复引用,使用了排除设置。

商品分页数据调用设计

在商品应用管理中,对微服务接口的调用,同样是使用FeignClient 来设计的。这里不但涉及商品数据服务微服务接口的调用,还涉及类目数据服务微服务接口的调用。因为这里涉及查询对象的参数转换方法,所以针对微服务接口的调用和 FeignClient 的使用这部分内容,需要进一步说明。

在商品管理调用的客户端设计GoodsClient 中,分页数据查询部分的代码如下所示:@Repository

public interface GoodsMapper extends MyMapper{

ListgetPage (@Param ( "map")Mapmap);

}

在这个设计中,参数传输是按照微服务接口提供方提供的方法进行设计的,所以每个参数都必须按原接口的属性设置。

现在,我们来看看服务类GoodsRestService的设计,代码如下所示:对应上面的 GoodsMapper 接口定义,我们设计一个 GoodsMapper.xml来定制相关的 SOL查询设计,代码如下所示:

SELECT g.* FROMt goods g WHERE1=1

AND g.name LIKE CONCAT('%',#{map.name}, '8')AND g.merchantid=#{map.merchantid}AND g.sortsid=#{map.sortsid]AND g.subsid=#{map.subsid}AND g.created >=#{map.created}

在这里,我们为调用方提供了使用查询对象GoodsQo进行参数传输的方法,只有在程序中针对GoodsClient进行调用时,才从查询对象中取出每一个需要的参数。Web应用项目热部署设置

在Web应用开发过程中,我们经常需要对操作界面进行调整,如果需要频繁地重启应用,则不仅耗时耗力,还影响开发的效率和进度。因此,下面介绍如何在 IDEA 中进行热部署设置。

首先,在 Web模块的项目对象模型中增加热部署组件引用和构建工程的插件配置,代码如下所示:@service

CTransactional

public class GoodsService {

CAutowired

private GoodsMapper goodsMapper;

public String insert (Goods goods){

try {

goodsMapper.insert(goods);

return goods.getId().toString();}catch(Exception e){

e.printStackTrace();return e.getMessage();

public String update(Goods goods){

try {

goodsMapper. updateByPrimaryKey (goods) ;return goods.getId() .toString();

}catch(Exception e){

e.printStackTrace();return e.getMessage();

}

}

public String delete(Long id){

try{

goodsMapper.deleteByPrimaryKey (id);return id.toString();

}catch(Exception e){

e.printStackTrace();return e.getMessage();

public Goods getById (Long id){

try {

return goodsMapper.selectByPrimaryKey(id);}catch (Exception e){

e.printStackTrace();return null;

public PageInfogetPage (GoodsQ0 goodsQ0)throws Exception t

try {

Mapmap = MapToBeanUtil.transBean2Map(goodsQo);

if(!StringUtils.isEmpty(goodsQo.getName()){

map.put( "name" , goodsQo.getName());

}

if(!StringUtils.isEmpty(goodsQo.getMerchantid())){

map.put ("merchantid",goodsQo.getMerchantid());

}

if(!StringUtils.isEmpty(goodsQo.getSortsid())){

map.put("sortsid",goodsQo.getSortsid());

if(!StringUtils.isEmpty(goodsQo.getSubsid())){

map .put("subsid",goodsQo.getSubsid());

}

//把日期转为字符串

if (!StringUtils.isEmpty(goodsQo.getCreated())){

map. put("created",

CommonUtils.formatDateTime (goodsQo.getCreated()));

}

PageHelper.startPage (goodsQo.getPage(), goodsQo.getSize());Listlist = goodsMapper.getPage (map);

return new PageInfo (list);

}catch(Exception e){

e.printStackTrace();return null;

}

}

}

然后,在 IDEA 中,使用组合键“Shift+Ctrl+Alt+/”打开“Maintenance”对话框,选择“Registry…选项”,如图7-1所示。

cefa4c44fd949f47913ce19ff2c3dff4.png

在打开的“Registry”配置中,勾选“compiler.automake.allow.when.app.running”选项,如图7-2所示。对于这一步骤,只要设置过一次即可,不用对每一个项目都进行设置。

67dc2da98fdceaf9dcf88e77e116f7a7.png

接着,在IDEA 设置窗口中,选择“Complier”选项,勾选其下面的“Build project automatically"选项,如图7-3所示。这一设置必须针对每一个项目进行设定。

e62650501e1b50531fa18fa1fc04535e.png

这样,就可以打开浏览器的开发者工具窗口(例如 Chrome),勾选“Network”菜单下面的“Disable cache”选项,如图7-4所示。

6785246c225f8a10890a52ad2c65fa82.png

另外,为了不让每次修改一个Class就触发应用重启,我们可以在配置文件“application.yml"中增加如下所示的配置项:spring.devtools.restart.enabled: falsespring.devtools.livereload.enabled: false

完成上面这些配置之后,当我们修改页面设计时,程序就会进行自动更新了。在修改类文件时,需要手动重启一下,这样可以避免每修改一行代码,就自动触发应用重启。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值