DDD架构和微服务初步实现

本次记录的是微服务的初步认识和DDD架构的初步实现和思路,在之前的发布里,对Javaweb进行了一次小总结,还有一些东西,不去详细理解说明了,下面开始我对微服务的理解。

什么是微服务?

在刚刚开始学习的时候我是懵逼的,微服务是什么,springcloud是什么,搜了一些相关文章发现全是官方语言,还是不太懂,在后面边敲边学的过程中,对我而言,我自己对微服务也有了一个起步的认知:springcloud(微服务)是多个springboot项目的集合管理

让我举个例子:在以前我们写一个项目的时候,比如图书管理系统,整个项目模块的任务,就是围绕图书的增删改查来操作,如果我们想实现用户管理,我们需要再次新建一个controller,两者是没有任何关联的,业务完全不同,可是却互相依赖,如果我们还有电影管理系统,当图书管理系统出现问题导致tomcat宕机,那整个网站都挂了

1.maven的依赖不清晰,比较混乱不容易管理

2.部署在一个服务器上压力大

3.无法实现业务解耦

我们可以提前理解一下,在类中直接引用的耦合度低,还是通过接口访问获取到资源低。一定是通过接口访问获取的耦合度低,后面会提到。

4.代码全在一个项目中,部署速度慢

5.协同开发困难

假如和小伙伴们开发一个管理系统,我来开发用户登录业务,你来开发图书的增删改查,我们是需要不停的拉取推送,拉取推送,效率很低,而且容易出错,使用微服务就可以完全解耦,我针对我的业务逻辑,你只关注你的业务,互不干扰

目前我对spring could的理解是:springboot项目集合 + 负载均衡,Feign调用,nacos,gateway网关等 组成了springcould。所以新知识很少,springcould没有那么神秘了。

DDD架构

下面的内容与微服务的关系不大,都是基于springboot的实现

什么是DDD架构,大家可以去自行搜索一下,太官方的我就不说了,我就说一下我对DDD的理解

在DDD之前我们需要知道MVC架构模式,相比大家都很熟悉了,controller调service层,service调dao层,controller暴露接口执行对应的业务,service执行业务逻辑,dao层操作数据库。

DDD也是类似的,只不过增添了一些规则和划分,变成了四层模式:

分层架构的一个重要原则是每层只能与位于其下方的层发生耦合,较低层绝不能直接访问较高层。
严格分层架构:
某层只能与位于其直接下方的层发生耦合
松散分层架构:
则允许某层与它的任意下方层发生耦合
我们在实际运用过程中多使用的是松散分层架构。

api(接口层):提供微服务之间的相互调用的接口

application(应用层):对用户传入的参数进行校验等处理与吞吐转换

domain(领域层):业务的主要逻辑

infra(基础设施层):数据库的交互

(偷懒了,自己不画图了哈哈哈)

差不多就这些,如果大家清楚mvc架构模式,应该能理解,只不过增加了一个微服务之间的调用,大家可以不去关注,我们后面才会用到。

下面我们开始进行微服务的第一步,先把整个骨架搭起来

一:骨架搭建

本次我实现的还是一个简单的增删改查,这次就先写查询了,都是差不多的,相信大家都是会写的,主要是说一下思路和一些工具

首先我们需要建立一个空项目,切记兄弟们,空项目,不要maven项目

紧接着我们需要建立一个maven模块,这个模块就是一个项目(我已经建好了)

我们可以这样理解,movie是一个网站,movie-catalogue是这个网站的一个功能,用来实现电影的增删改查,后面还会在movie下再建立模块,由一个一个的模块来组成一个项目。

下面我们继续搭建骨架,我们需要准备刚刚说的四个层模块,如下图:

application,domain,infra,api,作用如上文一样,而application-controller存放的是对应的controller,因为以后可能会添加mq和job定时任务,以后再说

这里我多增加了两个模块,一个是common层,一个是starter

common:项目的公共模块,比如说定义的枚举,用户的上下文,和一些工具类,都放在这里

starter:项目的启动模块,springboot总需要启动吧,没有任何的其他功能,只负责启动和配置文件的编写

二:建包

下面开始建包

application-controller中我们需要建立三个包

controller:处理用户请求和返回,对参数校验

convert:负责层与层的数据转换的

dto:封装前端请求与返回的数据

convert没有接触过,它是负责数据的转换,比如我们在执行完业务逻辑后返回数据,前端可能需要再一次的封装,举个例子:传入id查询电影信息,我们查询完电影信息后可以再封装一次,把当前请求的用户也返回出去,而不影响数据库的交互

common层:

entity:一些通用的实体,比如说封装的返回值和分页

enums:通用的枚举

domain层:

bo:业务执行的实体类

convent:与controller层一致,数据的转换,与基础层交互

service:业务接口与实现

infra层:

entity:数据库的映射实体类

mapper:数据库访问层

service:服务接口与实现

mapper:sql实现

starter层:

不多说了,spring boot的启动和配置文件

三:引入依赖

最恶心的就是这里了,也是开始最头疼的点,我们需要好多好多依赖,没什么技巧,大家只能慢慢顺下去,看到一个依赖就去查一下,知道自己想干什么,下面我来带大家引入一遍

 

不要搞错了,是父模块下的pom文件,这里规定了springboot和springcloud的版本

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.4.2</version>
            <type>pom</type>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.4.2</spring-boot.version>
    </properties>

starter: 

    <dependencies>
        <dependency>
            <groupId>com.yizhiliulianta</groupId>
            <artifactId>movie-catalogue-application-controller</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

引入controller层,因为DDD架构中上层引用下层,所以最上层一定是包含最下层的,所以之间引用最上层即可,大部分依赖我都写上注释了,大家有不知道的自行搜索吧,我就不去细说了

 infra:

    <dependencies>
        <!-- jdbcStarter-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.4.2</version>
        </dependency>
        <!-- druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!-- mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <!-- mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- 集合工具类-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
        <dependency>
            <groupId>com.yizhiliulianta</groupId>
            <artifactId>movie-catalogue-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

引入common是因为common中包含着通用的依赖,因为上层要依赖基础层,所以我们直接在基础层引入通用的依赖即可,当上层引入infra的时候,也就引入了common通用的依赖

domain:

    <dependencies>
        <!-- JSON 的实现-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.7</version>
        </dependency>
        <!-- 实现数据绑定和对象序列化-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.7</version>
        </dependency>
        <dependency>
            <groupId>com.yizhiliulianta</groupId>
            <artifactId>movie-catalogue-infra</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

领域层引入基础设施层,不多说了

common:

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!-- 数据映射-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.2.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.4.2.Final</version>
        </dependency>
        <!-- 日志-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>2.4.2</version>
        </dependency>
        <!--  json解析工具-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>
        <!-- 字符串工具-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
        <!-- 断言检查-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
    </dependencies>

通用依赖

controller:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.4.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-logging</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.yizhiliulianta</groupId>
            <artifactId>movie-catalogue-domain</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

禁用logging,防止日志冲突,一样的引用下层的domain

到这里依赖就引用完成了,让我们启动一下

MovieApplication类:

package com.yizhiliulianta.movie;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.yizhiliulianta")
@MapperScan("com.yizhiliulianta.**.mapper")
public class MovieApplication {
    public static void main(String[] args) {
        SpringApplication.run(MovieApplication.class);
    }
}

 application.yml:

server:
  port: 1000

spring:
  datasource:
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/movieweb?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8
    password: 123456

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

我先把数据库连上了,大家按照自己的更改吧,下面是mybatis的日志更改

成功了!哈哈哈哈

下面我们开始代码的编写

代码实现 

我们先从common层开始写,我们首先要知道我们要返回什么,我比较习惯从上往下写。

我想返回给前端的应该是这样的一个格式,清楚之后,上代码

common模块:

找到common层的枚举包(enums)

package com.yizhiliulianta.movie.common.enums;

import lombok.Getter;

@Getter
public enum ResultCodeEnum {

    SUCCESS(200,"成功"),
    FAIL(500,"失败");

    public int code;
    public String desc;

    ResultCodeEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    public static ResultCodeEnum getByCode(int codeVal){
        for (ResultCodeEnum resultCodeEnum : ResultCodeEnum.values()){
            if (resultCodeEnum.code == codeVal){
                return resultCodeEnum;
            }
        }
        return null;
    }
}

定义返回的状态码,成功200,失败500,getByCode方法是通过code获取对应的枚举。

common下的entity(通用实体类包)

package com.yizhiliulianta.movie.common.entity;

import com.yizhiliulianta.movie.common.enums.ResultCodeEnum;
import lombok.Data;

@Data
public class Result<T> {

    private Boolean success;

    private Integer code;

    private String message;

    private T data;

    public static <T> Result ok(T data){
        Result result = new Result();
        result.setSuccess(true);
        result.setCode(ResultCodeEnum.SUCCESS.getCode());
        result.setMessage(ResultCodeEnum.SUCCESS.getDesc());
        result.setData(data);
        return result;
    }

    //有数据返回失败
    public static <T> Result fail(T data){
        Result result = new Result();
        result.setSuccess(false);
        result.setCode(ResultCodeEnum.FAIL.getCode());
        result.setMessage(ResultCodeEnum.FAIL.getDesc());
        result.setData(data);
        return result;
    }


}

大家可以对着看一下,就是一个对应的实体类,两个方法,一个成功的返回实体类,一个失败的。

controller模块:

dto包:

package com.yizhiliulianta.movie.application.dto;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;

@Data
public class MovieDTO implements Serializable {
    /**
     * 序号
     */
    private Long id;
    /**
     * 电影名字
     */
    private String moviename;
    /**
     * 电影评分
     */
    private Double moviescore;

    /**
     * 自定义 getter 方法,用于处理 moviescore 的位数问题
     */
    public Double getMoviescore() {
        // 在这里处理位数问题,例如将 moviescore 保留一位小数
        if (this.moviescore != null) {
            return BigDecimal.valueOf(this.moviescore).setScale(1, RoundingMode.HALF_UP).doubleValue();
        } else {
            return null;
        }
    }
}

这里封装前端传来和返回的实体类,对参数进行了处理,评分只保留一位小数

convert包:

package com.yizhiliulianta.movie.application.convert;

import com.yizhiliulianta.movie.application.dto.MovieDTO;
import com.yizhiliulianta.movie.domain.bo.MovieBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface MovieDTOConverter {

    MovieDTOConverter INSTANCE = Mappers.getMapper(MovieDTOConverter.class);

    MovieDTO convertBoToMovieDTO(MovieBO movieBO);

    MovieBO convertDtoToMovieBO(MovieDTO movieDTO);


}

两个类型的数据转换实现,进入domain层之前要将dto转成bo,domain层处理完后吐出bo,我们需要再进行处理,将bo转成dto返回前端

controller包:

package com.yizhiliulianta.movie.application.controller;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import com.yizhiliulianta.movie.application.convert.MovieDTOConverter;
import com.yizhiliulianta.movie.application.dto.MovieDTO;
import com.yizhiliulianta.movie.common.entity.Result;
import com.yizhiliulianta.movie.domain.bo.MovieBO;
import com.yizhiliulianta.movie.domain.service.MovieDomainServcie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/movie")
@Slf4j
public class MovieController {

    @Resource
    private MovieDomainServcie movieDomainServcie;

    @PostMapping("/getMovie")
    public Result<MovieDTO> getMovie(@RequestBody MovieDTO movieDTO) {
        try {
            if (log.isInfoEnabled()) {
                log.info("MovieController.getMovie.dto:{}", JSON.toJSONString(movieDTO));
            }
            Preconditions.checkNotNull(movieDTO.getId(), "电影id不能为空");
            // 吞入前的数据映射
            MovieBO movieBO = MovieDTOConverter.INSTANCE.convertDtoToMovieBO(movieDTO);
            // 吞入
            MovieBO bo = movieDomainServcie.selectMovie(movieBO);
            //吐出前的数据映射
            MovieDTO movie = MovieDTOConverter.INSTANCE.convertBoToMovieDTO(bo);
            //吐出
            return Result.ok(movie);
        } catch (Exception e) {
            log.error("MovieController.getMovie.error{}", e.getMessage(), e);
            return Result.fail("查询失败");
        }
    }


}

在应用层首先进行参数校验和日志的打印,像我刚刚说的,执行数据的吞吐,和数据的映射,该层不涉及任何业务逻辑,只做参数处理

domain模块:

bo包:

package com.yizhiliulianta.movie.domain.bo;

import lombok.Data;

import java.io.Serializable;

@Data
public class MovieBO implements Serializable {
    /**
     * 序号
     */
    private Long id;
    /**
     * 电影名字
     */
    private String moviename;
    /**
     * 电影评分
     */
    private Double moviescore;
}

和dto没什么区别,大家可以往里增添什么的,都是可以的,很灵活

convert包:

package com.yizhiliulianta.movie.domain.convert;

import com.yizhiliulianta.movie.domain.bo.MovieBO;
import com.yizhiliulianta.movie.infra.entity.TMovie;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface MovieBOConverter {

    MovieBOConverter INSTANCE = Mappers.getMapper(MovieBOConverter.class);

    TMovie convertBoToMovie(MovieBO movieBO);

    MovieBO convertToMovieBO(TMovie movie);

}

与controller层的数据转换没什么区别,这是变成了与基础层的数据转换,将bo转成数据映射的实体类进行查询,查完之后再变成bo,吐给controller

service包:

package com.yizhiliulianta.movie.domain.service;

import com.yizhiliulianta.movie.domain.bo.MovieBO;

public interface MovieDomainServcie {

    MovieBO selectMovie(MovieBO movieBO);


}

service.impl包:

package com.yizhiliulianta.movie.domain.service.impl;

import com.alibaba.fastjson.JSON;
import com.yizhiliulianta.movie.domain.bo.MovieBO;
import com.yizhiliulianta.movie.domain.convert.MovieBOConverter;
import com.yizhiliulianta.movie.domain.service.MovieDomainServcie;
import com.yizhiliulianta.movie.infra.entity.TMovie;
import com.yizhiliulianta.movie.infra.service.TMovieService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
@Slf4j
public class MovieDomainServiceImpl implements MovieDomainServcie{

    @Resource
    private TMovieService movieService;

    @Override
    public MovieBO selectMovie(MovieBO movieBO) {
        TMovie tmovie = MovieBOConverter.INSTANCE.convertBoToMovie(movieBO);
        TMovie movie = movieService.queryById(tmovie.getId());
        MovieBO bo = MovieBOConverter.INSTANCE.convertToMovieBO(movie);
        if (log.isInfoEnabled()) {
            log.info("MovieController.selectMovie.bo:{}",
                    JSON.toJSONString(bo));
        }
        return bo;
    }
}

这里与刚刚说的一样,就是数据的转换,去调用基础层进行数据库的查询

下面基础设施层的东西,我就不去讲了,没什么东西,我是用一个小插件做的,带大家使用一下

我先把代码贴出来:

infra模块:

entity包:

package com.yizhiliulianta.movie.infra.entity;

import java.io.Serializable;

/**
 * (TMovie)实体类
 *
 * @author makejava
 * @since 2024-06-11 10:50:08
 */
public class TMovie implements Serializable {
    private static final long serialVersionUID = -68583078203147531L;
    /**
     * 序号
     */
    private Long id;
    /**
     * 电影名字
     */
    private String moviename;
    /**
     * 电影评分
     */
    private Double moviescore;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMoviename() {
        return moviename;
    }

    public void setMoviename(String moviename) {
        this.moviename = moviename;
    }

    public Double getMoviescore() {
        return moviescore;
    }

    public void setMoviescore(Double moviescore) {
        this.moviescore = moviescore;
    }

}

mapper包:

package com.yizhiliulianta.movie.infra.mapper;

import com.yizhiliulianta.movie.infra.entity.TMovie;
import org.apache.ibatis.annotations.Param;
import java.util.List;

/**
 * (TMovie)表数据库访问层
 *
 * @author makejava
 * @since 2024-06-11 10:50:07
 */

public interface TMovieDao {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    TMovie queryById(Long id);


    /**
     * 统计总行数
     *
     * @param tMovie 查询条件
     * @return 总行数
     */
    long count(TMovie tMovie);

    /**
     * 新增数据
     *
     * @param tMovie 实例对象
     * @return 影响行数
     */
    int insert(TMovie tMovie);

    /**
     * 批量新增数据(MyBatis原生foreach方法)
     *
     * @param entities List<TMovie> 实例对象列表
     * @return 影响行数
     */
    int insertBatch(@Param("entities") List<TMovie> entities);

    /**
     * 批量新增或按主键更新数据(MyBatis原生foreach方法)
     *
     * @param entities List<TMovie> 实例对象列表
     * @return 影响行数
     * @throws org.springframework.jdbc.BadSqlGrammarException 入参是空List的时候会抛SQL语句错误的异常,请自行校验入参
     */
    int insertOrUpdateBatch(@Param("entities") List<TMovie> entities);

    /**
     * 修改数据
     *
     * @param tMovie 实例对象
     * @return 影响行数
     */
    int update(TMovie tMovie);

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 影响行数
     */
    int deleteById(Long id);

}

service包:

package com.yizhiliulianta.movie.infra.service;

import com.yizhiliulianta.movie.infra.entity.TMovie;

/**
 * (TMovie)表服务接口
 *
 * @author makejava
 * @since 2024-06-11 10:50:08
 */
public interface TMovieService {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    TMovie queryById(Long id);


    /**
     * 新增数据
     *
     * @param tMovie 实例对象
     * @return 实例对象
     */
    TMovie insert(TMovie tMovie);

    /**
     * 修改数据
     *
     * @param tMovie 实例对象
     * @return 实例对象
     */
    TMovie update(TMovie tMovie);

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    boolean deleteById(Long id);

}

service.impl包:

package com.yizhiliulianta.movie.infra.service.impl;

import com.yizhiliulianta.movie.infra.entity.TMovie;
import com.yizhiliulianta.movie.infra.mapper.TMovieDao;
import com.yizhiliulianta.movie.infra.service.TMovieService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * (TMovie)表服务实现类
 *
 * @author makejava
 * @since 2024-06-11 10:50:08
 */
@Service("tMovieService")
public class TMovieServiceImpl implements TMovieService {
    @Resource
    private TMovieDao tMovieDao;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public TMovie queryById(Long id) {
        return this.tMovieDao.queryById(id);
    }

    /**
     * 新增数据
     *
     * @param tMovie 实例对象
     * @return 实例对象
     */
    @Override
    public TMovie insert(TMovie tMovie) {
        this.tMovieDao.insert(tMovie);
        return tMovie;
    }

    /**
     * 修改数据
     *
     * @param tMovie 实例对象
     * @return 实例对象
     */
    @Override
    public TMovie update(TMovie tMovie) {
        this.tMovieDao.update(tMovie);
        return this.queryById(tMovie.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Long id) {
        return this.tMovieDao.deleteById(id) > 0;
    }
}

resource.mapper文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yizhiliulianta.movie.infra.mapper.TMovieDao">

    <resultMap type="com.yizhiliulianta.movie.infra.entity.TMovie" id="TMovieMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="moviename" column="movieName" jdbcType="VARCHAR"/>
        <result property="moviescore" column="movieScore" jdbcType="NUMERIC"/>
    </resultMap>

    <!--查询单个-->
    <select id="queryById" resultMap="TMovieMap">
        select
          id, movieName, movieScore
        from t_movie
        where id = #{id}
    </select>

    <!--查询指定行数据-->
    <select id="queryAllByLimit" resultMap="TMovieMap">
        select
          id, movieName, movieScore
        from t_movie
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="moviename != null and moviename != ''">
                and movieName = #{moviename}
            </if>
            <if test="moviescore != null">
                and movieScore = #{moviescore}
            </if>
        </where>
        limit #{pageable.offset}, #{pageable.pageSize}
    </select>

    <!--统计总行数-->
    <select id="count" resultType="java.lang.Long">
        select count(1)
        from t_movie
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="moviename != null and moviename != ''">
                and movieName = #{moviename}
            </if>
            <if test="moviescore != null">
                and movieScore = #{moviescore}
            </if>
        </where>
    </select>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into t_movie(movieName, movieScore)
        values (#{moviename}, #{moviescore})
    </insert>

    <insert id="insertBatch" keyProperty="id" useGeneratedKeys="true">
        insert into t_movie(movieName, movieScore)
        values
        <foreach collection="entities" item="entity" separator=",">
        (#{entity.moviename}, #{entity.moviescore})
        </foreach>
    </insert>

    <insert id="insertOrUpdateBatch" keyProperty="id" useGeneratedKeys="true">
        insert into t_movie(movieName, movieScore)
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.moviename}, #{entity.moviescore})
        </foreach>
        on duplicate key update
        movieName = values(movieName),
        movieScore = values(movieScore)
    </insert>

    <!--通过主键修改数据-->
    <update id="update">
        update t_movie
        <set>
            <if test="moviename != null and moviename != ''">
                movieName = #{moviename},
            </if>
            <if test="moviescore != null">
                movieScore = #{moviescore},
            </if>
        </set>
        where id = #{id}
    </update>

    <!--通过主键删除-->
    <delete id="deleteById">
        delete from t_movie where id = #{id}
    </delete>

</mapper>

EasyCode插件

下面是使用EasyCode这个插件,大家可以去idea下载一个,很方便

 右键一个表,选择EasyCode  ---》  Generate Code

module选择infra模块

package选择infra包,别选错了

然后和我一样,勾选template就可以生成了

生成后是dao包,我个人习惯喜欢mapper,将生成的类转到mapper下了,可能会有报错,大家将生成的分页代码删掉就行了

最后我们来通过aippost来测试一下

测试 

完美,成功了

总结

我们梳理一下本次实现的过程,首先建立微服务的骨架,然后引入相关依赖

代码部分:用户传入参数,将参数封装成dto传入controller,controller进行参数检查和数据转换bo并传入domain层,domain层与基础设施层交互,查询数据,再转换成bo吐出给controller,最后controller接收到再进行数据的转换,变成dto返回前端。

后面我们进行用户鉴权和微服务之间调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/700403.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这三款思维导图工具,真的很好用

XMIND XMIND是优秀的国产思维导图和头脑风暴软件&#xff0c;非常符合国人的使用要求&#xff0c;旨在帮助用户理清思路、捕捉创意&#xff0c;并提高工作和生活效率。支持Linux、IOS、Android、MAC平台。 提供了非常多精美的结构图&#xff0c;例如鱼骨图、逻辑图、括号图、树…

【推荐收藏】2024年5款最佳 GPU 渲染引擎大盘点

2024年已经过半&#xff0c;对于从事3D渲染和数字内容创作的朋友来说&#xff0c;选择一款高效的GPU渲染引擎至关重要。今天&#xff0c;小编就来为大家盘点一下2024年5款最佳GPU渲染引擎。 1.V-Ray V-Ray是一款备受欢迎的3D GPU渲染引擎&#xff0c;被建筑、视觉特效、室内设…

Deep Freeze冰点还原8.57最新版软件安装包下载+详细安装步骤

​冰点还原精灵&#xff08;DeepFreeze&#xff09;是由Faronics公司出品的一款系统还原软件&#xff0c;能保留您的计算机配置&#xff0c;确保全面的端点保护。任何更改&#xff0c;无论是恶意更改还是无意更改&#xff0c;都会在重启时撤销。这就是“重启还原”&#xff0c;…

281 基于matlab的路径规划GUI交互

基于matlab的路径规划GUI交互。包括蚁量系统、蚁周系统、蚁密系统、蚁群系统、免疫混合算法。11种路径规划数据&#xff0c;最多225个规划点。蚁群和免疫算法的参数可进行设置&#xff0c;使得效果最佳。动态显示可视化规划结果。程序已调通&#xff0c;可直接运行。

【华为 ICT HCIA eNSP 习题汇总】——题目集21

1、OSPF协议中的hello报文不包括以下哪个字段&#xff1f; A、Priority&#xff08;优先级&#xff09; B、Neighbor&#xff08;邻居表&#xff09; C、Interval&#xff08;时间间隔&#xff09; D、Checksum&#xff08;校验和&#xff09; 考点&#xff1a;路由技术原理 解…

【C++】继承|切片|菱形继承|虚继承

目录 ​编辑 一.什么是继承 三大特性 继承的语法 访问控制 继承的总结 二.继承的作用域 三.切片 四. 派生类的默认成员函数 构造函数 析构函数 拷贝构造 赋值运算符重载 五.单继承和多继承 单继承 多继承 菱形继承 解决方式 六.虚继承 一.什么是继承 C中的…

Nginx+Tomcat负载均衡、动静分离原理

目录 一.Nginx负载均衡 1.负载均衡概念 2.负载均衡原理 3.Nginx反向代理 3.1.反向代理概念 3.2.Nginx实现负载均衡的主要配置项 二.Nginx动静分离 1.什么是动静分离 2.动态页面与静态页面区别 3.动静分离原理 三.NginxTomcat负载均衡的实验设计 1.部署nginx负载均衡…

VMware Workstation虚拟机进入U盘PE系统

注意事项 VMware Workstation虚拟机版本不能高于16.1.2版本&#xff01;&#xff01;&#xff01; 本实验使用的版本如下 实际操作 在已安装好的虚拟机处右键&#xff0c;点击设置。虚拟机安装win10教程请参考VMware Workstation安装win10操作系统-CSDN博客 在弹出的窗口点击…

苹果WWDC24一文总结,携手OpenAi,开启Ai新篇章

北京时间6月11日凌晨1点&#xff0c;苹果2024年全球开发者大会&#xff08;WWDC&#xff09;正式开幕。按照往年惯例&#xff0c;每年的WWDC大会&#xff0c;苹果都会将重心放在对新版系统的介绍上&#xff0c;本次也不例外&#xff0c;苹果发布了包括iOS 18、iPadOS18、macOS1…

2024/06/11--代码随想录算法1/17|理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

理论基础 动态规划&#xff1a;当前状态由前面的状态推导而来 贪心&#xff1a;局部选最优 动态规划5步曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 力扣链接 动态规划5步曲 确定d…

BDD100k

摘要 数据集推动视觉进步&#xff0c;但现有的驾驶数据集在视觉内容和支持任务方面都很贫乏&#xff0c;以研究自动驾驶的多任务学习。研究人员通常被限制在一个数据集上研究一小部分问题&#xff0c;而现实世界的计算机视觉应用需要执行各种复杂的任务。我们构建了一个包含10…

应急物资管理系统|DW-S300构筑现代化战备保障的利器

行业背景 智慧应急物资管理系统&#xff08;智物资DW-S300&#xff09;是一套成熟系统&#xff0c;依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 政府相关部门设立的应急物资库是…

element 表格第一列合并,第二列展开后出现错位情况

展开后发现蓝色一行挤下来&#xff0c;而且还错位了 解决思路&#xff1a;展开行&#xff0c;在dom上其实是新增了一行的高度&#xff0c;合并上新增一个高度就可以 <el-tablev-loading"tabLoading"fitref"oneRef"height"100%":span-method…

JAVA开发 使用Apache PDFBox库生成PDF文件,绘制表格

1. 表格位置定点 2.执行效果展示&#xff08;截取PDF文件图片&#xff09; 3.执行代码 当我们使用Apache PDFBox库在PDF文件中创建带有表格的内容&#xff0c;需要遵循几个步骤。PDFBox本身并没有直接的API来创建表格&#xff0c;但我们可以通过定位文本、绘制线条和单元格矩形…

准橙人工翻译微信小程序,100+专业领域的译者在线帮你翻译!藏语、维吾尔语、哈萨克语、壮语、彝文、蒙古语统统支持人工翻译!

亲爱的朋友们&#xff0c;我们深知每一种语言都承载着独特的文化和历史&#xff0c;为了传承和弘扬这些宝贵的文化遗产&#xff0c;我们诚挚地邀请具备翻译经验并熟练掌握以下任意一门语言的您加入我们的团队&#xff01; 中国少数民族语言&#xff1a;藏语、维吾尔语、哈萨克…

DHCP原理与配置(Linux)

目录 DHCP概念 使用DHCP的好处 DHCP的分配方式 DHCP租约过程 租约过程分4个步骤&#xff08;全过程广播&#xff09; 1. 客户机请求IP&#xff08;Discover&#xff1a;发现&#xff1b;客户端广播 发送一个数据包&#xff0c;其他主机也能接收到&#xff0c;如果是没有安…

StarRocks vs. Trino: 高并发性能背后的技术优势是什么?

Trino&#xff08;之前称 PrestoSQL&#xff09;项目最初由 Meta 开发&#xff0c;旨在让数据分析师能够在广泛的 Apache Hadoop 数据仓库上执行交互式查询。其高效处理大型数据集和复杂查询的能力&#xff0c;以及多数据源连接的灵活性&#xff0c;使其迅速成为大规模组织的首…

bugku题目(带WP)

CTF题型&#xff0c;带WP。根据给出的题目&#xff0c;找到flag。 1、眼见非实 下载实验文件是一个file .zip 解压这个file.zip压缩包&#xff0c;得到眼见为实.docx文件 双击打开这个文件&#xff0c;不能读取。 修改眼见为实.docx的后缀为眼见为实.zip&#xff0c;再进行解压…

蚁群算法的优化计算——旅行商问题(TSP)优化matlab

微♥关注“电击小子程高兴的MATLAB小屋”获得资料 一&#xff0c;理论基础 生物学家研究发现&#xff0c;蚂蚁在寻找食物时&#xff0c;会在其经过的路径上释放一种信息素&#xff0c;并能够感知其他蚂蚁释放的信息素。信息素浓度的大小表示路径的远近&#xff0c;浓度越高&a…

拼房、行程变更、跨月退改?复杂场景对账结算怎么办?

在实际商业场景中&#xff0c;销售渠道多样化、数据关联多方、场景多元化、业务逻辑多变性等都让对账成为一门“技术活”&#xff0c;也成为财务人员面前的“拦路虎”。尤其当面临多成本中心、跨项目和跨月退改的出差费用时。手动拆分费用、协调沟通、以及处理费用归属等问题&a…