前后端分离中后端的实现

2,210 阅读4分钟

基于宠物商店项目的重构,将原先项目进行前后盾分离,文末附上项目代码

RESTful API的设计

API设计demo

springboot工程

ORM我们用到mybatis-plus,引入各种依赖,对配置文件进行配置,没什么特别之处,此处便省略。

新建实体类

image.png

实体类注解配置

详情请参照mybaits-plus官网教程

项目数据库表:

image.png

@TableName(value = "category")
public class Category {

    @TableId(value = "catid", type = IdType.INPUT)
    private String categoryId;
    @TableField(value = "name")
    private String name;
    @TableField(value = "descn")
    private String description;

}

编写Mapper类

继承BaseMapper

public interface CategoryMapper extends BaseMapper<Category> {
    
}

启动类添加@MapperScan注解

编写测试类,进行测试

6
[Category(categoryId=, name=, description=<image src='/images/birds_icon.gif'><font size='5' color='blue'> Birds</font>), Category(categoryId=BIRDS, name=Birds, description=<image src="./images/birds_icon.gif"><font size="5" color="blue"> Birds</font>), Category(categoryId=CATS, name=Cats, description=<image src="/images/cats_icon.gif"><font size="5" color="blue"> Cats</font>), Category(categoryId=DOGS, name=Dogs, description=<image src="/images/dogs_icon.gif"><font size="5" color="blue"> Dogs</font>), Category(categoryId=FISH, name=Fish, description=<image src="/images/fish_icon.gif"><font size="5" color="blue"> Fish</font>), Category(categoryId=REPTILES, name=Reptiles, description=<image src="/images/reptiles_icon.gif"><font size="5" color="blue"> Reptiles</font>)]

image.png

成功

通用响应的设计

此处的controller以及service层实现我就不再赘述了

编写CommonResponse类,此类的设计依据原先RESTful API的设计实现

public class CommonResponse<T> implements Serializable {

    private int status;
    private String msg;
    private T data;


    public CommonResponse(int status){
        this.status=status;
    }
    public CommonResponse(int status,String msg){
        this.status = status;
        this.msg=msg;

    }
    public CommonResponse(int status,String msg,T data){
        this.status = status;
        this.msg=msg;
        this.data=data;
    }
    public CommonResponse(int status,T data){
        this.status = status;
        this.data=data;
    }
}

这是原先的设计,但这样设计有一个问题,在传参时传整形与字符串型的话只能调用第二个构造函数。

测试如下:

image.png

改进如下:

public class CommonResponse<T> implements Serializable {

    private int status;
    private String msg;
    private T data;


    private CommonResponse(int status){
        this.status=status;
    }
    private CommonResponse(int status,String msg){
        this.status = status;
        this.msg=msg;

    }
    private CommonResponse(int status,String msg,T data){
        this.status = status;
        this.msg=msg;
        this.data=data;
    }
    private CommonResponse(int status,T data){
        this.status = status;
        this.data=data;
    }

    public static <T> CommonResponse<T> creatForSuccess(){
        return new CommonResponse<T>(0);
    }
    public static <T> CommonResponse<T> creatForSuccess(T date){
        return new CommonResponse<T>(0,date);
    }
    
     public static <T> CommonResponse<T> creatForSuccess(String msg,T date){
        return new CommonResponse<T>(0,msg,date);
    }
    public static <T> CommonResponse<T> creatForSuccessMessage(String msg){
        return new CommonResponse<T>(0,msg);
    }
   
    public static <T> CommonResponse<T> creatForError(String msg){
        return new CommonResponse<T>(1,msg);
    }

}

将构造方法设置为私有,通过下面的静态方法进行赋值

这样通用响应就设计完成了

public class CatalogServiceImpl implements CatalogService {

    @Autowired
    private CategoryMapper categoryMapper;

    @Override
    public CommonResponse<List<Category>> getCategoryList() {
        List<Category> categoryList = categoryMapper.selectList(null);
        if (categoryList.isEmpty()){
            return CommonResponse.creatForSuccessMessage("没有找到种类信息");//没查到但此时的状态仍然为成功的
        }
        else{
            return CommonResponse.creatForSuccess(categoryList);
        }
    }
}

上述代码即为service层,最后运行时出现了下面的报错:

No converter found for return value of type: class com.xiesuper.common.CommonResponse] 

调试时数据都已经得到,但在postman上访问时就出现上述报错,最后发现CommonResponse类中没添加@Getter注解(引入了lambook依赖)。

最后得到的JSON数据如下:

{
   "status": 0,
   "msg": null,
   "data": [
       {
           "categoryId": "BIRDS",
           "name": "Birds",
           "description": "<image src=\"./images/birds_icon.gif\"><font size=\"5\" color=\"blue\"> Birds</font>"
       },
       {
           "categoryId": "CATS",
           "name": "Cats",
           "description": "<image src=\"/images/cats_icon.gif\"><font size=\"5\" color=\"blue\"> Cats</font>"
       },
       {
           "categoryId": "DOGS",
           "name": "Dogs",
           "description": "<image src=\"/images/dogs_icon.gif\"><font size=\"5\" color=\"blue\"> Dogs</font>"
       },
       {
           "categoryId": "FISH",
           "name": "Fish",
           "description": "<image src=\"/images/fish_icon.gif\"><font size=\"5\" color=\"blue\"> Fish</font>"
       },
       {
           "categoryId": "REPTILES",
           "name": "Reptiles",
           "description": "<image src=\"/images/reptiles_icon.gif\"><font size=\"5\" color=\"blue\"> Reptiles</font>"
       }
   ]
}

此处我们没有对msg赋值,但最终得到了msg的数据,解决方法:在CommonResponse类上添加注解

//Json序列化时空的数据不会被包含
@JsonInclude(JsonInclude.Include.NON_NULL)

实体类中的信息不是来自于一张表该周么办??

我们看原来宠物商店Item类中变量:

private String itemId;
  private String productId;
  private BigDecimal listPrice;
  private BigDecimal unitCost;
  private int supplierId;
  private String status;
  private String attribute1;
  private String attribute2;
  private String attribute3;
  private String attribute4;
  private String attribute5;
  private Product product;
  private int quantity;

其信息来自于三个表:

item表: image.png

inventory表: image.png product表: image.png

其中一个解决方法就是SQL语句的连接查询,但mybatis-plus是不推荐这种用法的

下面我们来解决这种问题:

首先我们需要学习一下VO,BO,DTO,DO几个对象的概念: 浅析VO,BO,DTO,DO

VO即view object 也就是我们页面中要展示的对象,此时查询Item信息我们便用到vo 这里以查询某个productId下的所有Items为例

新建Item,Product,ItemInventory实体类,以及Itemmapper,Productmapper,ItemInventoryMapper具体代码就不再这展示了。 新建ItemVo:

image.png

对ItemVo信息进行填充:

@Data
public class ItemVo {
    //item表中字段
    private String itemId;
    private String productId;
    private BigDecimal listPrice;
    private BigDecimal unitCost;
    private int supplierId;
    private String status;
    private String attribute1;
    private String attribute2;
    private String attribute3;
    private String attribute4;
    private String attribute5;


    //item所属product属性
    private String categoryId;
    private String name;
    private String description;

    //item的库存
    private int quantity;

}

下面我们理一下思路,首先,我们要根据productId获取到数据库中Item表中的items,然后获得product信息,然后获得库存信息,那么问题来了,我们如何根据productId获取到items呢?

此时我们便用到mybatis-plus中条件构造器

条件构造器

关键代码实现如下


QueryWrapper<Item> queryWrapper = new QueryWrapper<>();//条件构造器
 queryWrapper.eq("productId",productId);//参数为字段名与传入的value值
 List<Item> itemList = itemMapper.selectList(queryWrapper);

然后就是将分别查到的数据填充到ItemVo中便可:实现代码如下:

 @Override
    public CommonResponse<List<ItemVo>> getItemsByProductId(String productId) {

        QueryWrapper<Item> queryWrapper = new QueryWrapper<>();//条件构造器
        queryWrapper.eq("productId",productId);//参数为字段名与传入的value值
        List<Item> itemList = itemMapper.selectList(queryWrapper);

        if(itemList.isEmpty()){
            return CommonResponse.creatForSuccessMessage("对应ProductId没有items信息");
        }
        Product product =productMapper.selectById(productId);

        List<ItemVo> itemVoList = new ArrayList<>();
        for(Item item:itemList){
            ItemVo itemVo = itemToItemVo(item,product);
            itemVoList.add(itemVo);
        }


        return CommonResponse.creatForSuccess(itemVoList);


    }

    private ItemVo itemToItemVo(Item item,Product product){
        ItemVo itemVo =new ItemVo();
        itemVo.setItemId(item.getItemId());
        itemVo.setProductId(item.getProductId());
        itemVo.setUnitCost(item.getUnitCost());
        itemVo.setListPrice(item.getListPrice());
        itemVo.setSupplierId(item.getSupplierId());
        itemVo.setStatus(item.getStatus());
        item.setAttribute1(item.getAttribute1());
        item.setAttribute2(item.getAttribute2());
        item.setAttribute3(item.getAttribute3());
        item.setAttribute4(item.getAttribute4());
        item.setAttribute5(item.getAttribute5());

        itemVo.setDescription(product.getDescription());
        itemVo.setCategoryId(product.getCategoryId());
        itemVo.setName(product.getName());

        ItemInventory itemInventory = itemInventoryMapper.selectById(item.getItemId());
        itemVo.setQuantity(itemInventory.getQuantity());

        return itemVo;

    }

项目源码链接