本次记录的是微服务的初步认识和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返回前端。
后面我们进行用户鉴权和微服务之间调用