-->

谈SSM的Service层两种实现结构

概述

最近由于入职大厂,所以文章也少了,并不是自己懈怠了,而是大厂的保密措施不允许我上班写文章了,更何况还有无尽的加班。。。。。。。。。唉,所幸现在习惯了好多,现在觉着该记录一下知识了。目前市场上,要实现Java项目主要有Maven和Gradle两种框架,其中Gradle是新兴势力,Maven是老牌势力,其实我更加喜欢用Gradle,因为更加便捷,无奈,现在市面上Maven仍然是主流,因为稳定,更因为许多老项目都是Maven搭建的。。。。。。今天这篇文章主要是介绍两种实现springboot的方式,以防以后忘记

第一种:Domain(entity)-Mapper-Service-Controller

这种方式依赖于mybatis-plus的BaseMapper和ServiceImpl<Mapper,entity>实现,效果就是没有了Service接口类,这种方式是我的老师教给我的方式,我也认为是最简单的方式,也是我的最爱,不过却是不常见的
例如:

实体类层:Domain(entity)

package com.example.pmxt.domain;

import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
@TableName("sys_region")
public class Region {

    @NotNull(message = "id不能为空")
//    @NotBlank(message = "id不能为空")
//    @Length(min = 6,max = 6,message = "长度必须为6")
    @ExcelProperty("编号")
    private Integer id;

    @NotNull(message = "名称不能为空")
    @NotBlank(message = "名称不能为空")
    @ExcelProperty("名称")
    private String name;

    @NotNull(message = "父类id不能为空")
//    @NotBlank(message = "父类id不能为空")
//    @Length(min = 6,max = 6,message = "长度必须为6")
    @ExcelProperty("父类id")
    private Integer parentId;

    @ExcelProperty("备注")
    private String remark;

}

这一层在两种方式中并无区别,就是建立实体类,方便管理。

Mapper层

这一层主要有两种实现方式:注解和写<这种标签,注解比较多,因为简便,标签的话,应该是老程序员专属吧,注解如下:

package com.example.pmxt.modules.region;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.pmxt.domain.Region;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface RegionMapper extends BaseMapper<Region> {

//    @Select("SELECT sys_region.id,sys_region.name,sys_region.parent_id from sys_region,busi_bd.szd where sys_region.id = busi_bd.szd union SELECT sys_region.id,sys_region.name,sys_region.parent_id from sys_region,busi_bd where sys_region.id = concat(left(busi_bd.szd,4),'00') union SELECT sys_region.id,sys_region.name,sys_region.parent_id from sys_region,busi_bd where sys_region.id = concat(left(busi_bd.szd,2),'0000');")
//    List<Region> selectbybd();

    @Select("select * from sys_region where name = #{name}")
    Region getByname(String name);

    @Select("select parent_id from sys_region where name = #{name};")
    Integer getparentIdByName(String name);

    @Select("select id from sys_region where name = #{name};")
    Integer getIdByName(String name);

    @Select("select * from sys_region where concat(id) like concat(left(concat(#{id}),2),'%');")
    List<Region> getByProvince(Integer id);

    @Select("select * from sys_region where concat(id) like concat(left(concat(#{id}),4),'%');")
    List<Region> getByCity(Integer id);

    @Select("select * from sys_region where id = #{id}")
    List<Region> getByCounty(Integer id);


}

注意BaseMapper来自于Mybatis-plus导包

主要就是注解+SQL语句后,下面跟个方法名

Service层

这块是两种方式差别最大的一个,这种方式只需要一个Service类就好了,但是需要一个工具类BaseService。

BaseService:

package com.example.pmxt.common;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

public class BaseService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {
}

所有的Service都可以继承这个BaseService,这样就不用写ServiceImpl了,只需要卸载Service中就行了

Service:

package com.example.pmxt.modules.region;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.example.pmxt.common.BaseService;
import com.example.pmxt.domain.Region;
import lombok.SneakyThrows;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Service
@Transactional
public class RegionService extends BaseService<RegionMapper,Region> {

    private final RegionMapper mapper;

    @Autowired
    public RegionService(RegionMapper mapper) {
        this.mapper = mapper;
    }


    @SneakyThrows
    public boolean importFileToDB(boolean deleteHistory, MultipartFile file) {
    //是否删除数据库表中历史数据
        if (deleteHistory) {
            this.remove(null);
        }
    //读取excel表格中数据
        EasyExcel.read(file.getInputStream(), Region.class, new ReadListener<Region>() {
            //定义一个常量BATCH_SIZE与一个列表ls
            private static final int BATCH_SIZE = 1000;
            private List<Region> ls = Lists.newArrayList();
            //重写invoke方法
            @Override
            public void invoke(Region data, AnalysisContext context) {
                //System.err.println(data);
                //列表里添加Flowfee类的数据
                ls.add(data);
                //每1000条放到数据库里面
                if (ls.size() >= BATCH_SIZE) {
                    RegionService.this.saveBatch(ls);
                    ls.clear();
                }
            }
            //重写doAfterAllAnalysed方法,处理剩下的数据,例如1050条,其中1000条在上面已经放到数据库,剩下的50条下面处理
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                RegionService.this.saveBatch(ls);
            }
        }).sheet().doRead();
        return true;
    }

    public Integer getparentIdByName(String name){
        return mapper.getparentIdByName(name);
    }
    public Integer getIdByName(String name){
        return mapper.getIdByName(name);
    }

    public List<Region> gettable(String name){
        if (mapper.getByname(name)==null){
            return null;
        }
        Integer id = mapper.getIdByName(name);
        if ("0000".equals(String.valueOf(id).substring(2))){
            return mapper.getByProvince(id);
        }else if ("00".equals(String.valueOf(id).substring(4))){
            return mapper.getByCity(id);
        }else {
            return mapper.getByCounty(id);
        }
    }
}

Controller层

这一层是用于给前端写接口的类,两种方法基本一致

package com.example.pmxt.modules.region;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.example.pmxt.common.ReturnResult;
import com.example.pmxt.domain.Region;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@Validated
@RequestMapping("/regions")
public class RegionController {

    private final RegionService service;

    @Autowired
    public RegionController(RegionService service) {
        this.service = service;
    }

    @GetMapping()
    public ReturnResult getAllRegion(){
        //这里用的MyBatis Plus
        List<Region> dtos = service.list();
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("id");
        // 最大递归深度
        treeNodeConfig.setDeep(4);
        //转换器
        List<Tree<Integer>> treeNodes = TreeUtil.build(dtos, 0, treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId());
                    tree.setParentId(treeNode.getParentId());
                    tree.setName(treeNode.getName());
                });
        return ReturnResult.buildSuccessResult(treeNodes);
    }

    @GetMapping("/name")
    public ReturnResult getByName(@NotNull(message = "parentid不能为空") String name){
        Integer parentid = service.getparentIdByName(name);
        //这里用的MyBatis Plus
        List<Region> dtos = service.gettable(name);
        if (dtos == null){
            return ReturnResult.buildFailureResult("该地区不存在");
        }
        //配置
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("id");
        // 最大递归深度
        treeNodeConfig.setDeep(3);
        //转换器
        List<Tree<Integer>> treeNodes = TreeUtil.build(dtos, parentid, treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId());
                    tree.setParentId(treeNode.getParentId());
                    tree.setName(treeNode.getName());
                });
        return ReturnResult.buildSuccessResult(treeNodes);

    }

//    @GetMapping("/selectbybd")
//    public ReturnResult selectbybd(){
//        return ReturnResult.buildSuccessResult(service.selectbybd());
//    }

    @PostMapping()
    public ReturnResult addRegion(@RequestBody @Validated Region region){
        if (region == null){
            return ReturnResult.buildFailureResult("行政区划不能为空");
        }
            return ReturnResult.buildSuccessResult(service.save(region));

    }

    @DeleteMapping("/{id}")
    public ReturnResult deleteRegionById(@PathVariable @NotNull(message = "id不能为空") Integer id){
        if(id == null){
            return ReturnResult.buildFailureResult("id不能为空");
        }
        Region r = service.getById(id);
        if (r != null){
            return ReturnResult.buildSuccessResult(service.removeById(id));
        }else {
            return ReturnResult.buildFailureResult("id不存在");
        }
    }

    @PutMapping()
    public ReturnResult updateRegion(@RequestBody Region region){
        if (region == null){
            return ReturnResult.buildFailureResult("行政区划不能为空");
        }
        Region r = service.getById(region.getId());
        if (r != null) {
            return ReturnResult.buildSuccessResult(service.updateById(region));
        }else {
            return ReturnResult.buildFailureResult("id不存在");
        }
    }


    //@PostMapping定义IP地址映射方法与位置,@ApiOperation是swagger测试注解
    @PostMapping("/uploadregion")
    public ReturnResult excelToDatabase( @RequestParam boolean deleteHistory,
                                         @RequestPart("file") MultipartFile file) {
        if(service.importFileToDB(deleteHistory, file)){
            return ReturnResult.buildSuccessResult("添加成功",null);
        }else {
            return ReturnResult.buildFailureResult("添加失败",null);
        }
    }


    @DeleteMapping("deletes")
    public ReturnResult deleteregions(Integer [] ids){
        if (ids.length == 0){
            return ReturnResult.buildFailureResult("行政区划不能为空");
        }else {
            return ReturnResult.buildSuccessResult(service.removeBatchByIds(Arrays.asList(ids)));
        }
    }
}

第二种:Domain(entity)-Mapper-Service-ServiceImpl-Controller

区别:Service层在上面是没有接口的,就是一个纯Service类,而在这中方式里,Service层分为了impl和Service接口,仔细观察可以看出,上面第一种方法中的Service类,就是第二种种的ServiceImpl

那么为什么Service层要写接口,像上面一样不写不是可以少写一个文件吗?

原因:在面向对象的软件设计原则中,有一个依赖倒置的软件设计原则,要求高层模块不能直接依赖于低层模块,应该依赖于抽象;在代码中Controller层为高层模块,Service为低层模块,所以Controller不能直接依赖于Service类,而要依赖于抽象的Service接口

那么,问题又来了,一定要满足面向对象的软件设计原则吗?

答案:由于我们通常写的代码不需要进行额外拓展,所以不写Service接口也是没问题的,如果需要额外拓展,那就需要遵守软件设计原则了(写Service接口,大厂基本上都得写。。。)

这种方法主要就差在Service层,其余层可以参考上面

Service层

Service:

public interface UserSerivce extends IService<User> {
}

这种方法Service要先继承IService工具类,实际上ServiceImpl已将包含了IService中的所有方法

ServiceImpl:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserSerivce {

}

这就需要多写一个Impl类,实际上两者原理一样,第二种多了个ServiceImpl<UserMapper, User> 实现 UserSerivce接口,而UserSerivce接口又要继承IService,比较麻烦,不喜欢。。。。。,但是无奈,这是主流,尤其大厂还有代码检视,第一种会被认为不对,但我个人觉得能实现功能就是好方法,你认为呢?

最后说明这两种方法应该在Maven和Gradle框架中都可以使用,至少Maven是都可以的,因为我试过了,,Gradle中我用过第一种,第二种还没用过,不过既然原理一致,应该都可以使用;

这篇文章只当自己对喜爱方法的纪念吧。。。。

posted @   ꧁ʚ星月天空ɞ꧂  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示