小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

一篇文章读懂mybatis-plus原理和CRUD接口

# MyBatis,mybatis,java,spring boot 额外说明

收录于:23天前

Myabtis-Plus配置

MybatisPlus官网

官方文档有关于spring boot配置mybatis-plus的详细教程,这里不再赘述。

Mybatis-plus是基于spring的,只能在spring相关的应用上实现。

  1. 介绍父项目

在这里插入图片描述
注意mybatis-plus和spring boot的版本匹配不然会出现
在这里插入图片描述

推荐版本

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

参考pom依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>


    </dependencies>

配置并连接数据库

  • 创建数据库表

在这里插入图片描述

  • 添加一些数据

在这里插入图片描述

  • application.properties中配置datasource

datasource是连接数据库必须的配置,数据库驱动包通过反射来加载这些信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://139.9.209.93/task?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true

  • ORM 映射

将数据库表映射到Java对象以实现信息交换。

@Data
@TableName("bt_04")
public class BT_04 {
    

    @TableId(value = "opt_id")
    private Integer id;

    @TableField("drag_start_lat")
    private Double DragStartLat;

    @TableField("drag_start_lon")
    private Double DragStartLon;

    @TableField("drag_end_lat")
    private Double DragEndLat;

    @TableField("drag_end_lon")
    private Double DragEndLon;
}

注意@TableName@TableField相辅相成共同完成ORM映射。后者辅助前者。

姓名 影响 评论
@表名 完成数据库表到对象的映射 bean开头的参数是数据库表名
@TableField 完整的字段到属性映射 mp 默认从下划线命名转换为驼峰式命名。如果遵守规则,可以省略注释。参数是对象属性。
@表Id 完整的主键映射 可设置参数自增等条件

car_name[下环线命名]
carName[驼峰命名]
mybatis-plus默认前者转后者。

mp作为myabtis的升级版本,延续了前身的注释,同时还添加了很多新的注释。

在这里插入图片描述
具体请参考官网MyBatis-Plus

之前已经通过@TableFieId注解将数据库表实现映射了,而关系的桥梁就是sql,那么如何应用sql呢?
通过mybatis的mapper接口和mapper映射文件关联即可。但是mp有更好的方法就是BaseMapper
一个BaseMapper管理一个映射关系。

米阿布蒂斯

public interface BTfourMapper {
        //extends BaseMapper<BT_04>

    @Select("select * from bt_04")
    List<BT_04> query();
}

mybatis-plus

public interface BTfourMapper extends BaseMapper<BT_04>{
     
    
}

使用BaseMapper的通用映射更加方便,无需编写sql语句。

可以看到BaseMapper中定义了很多接口来满足一般的需求。

在这里插入图片描述

因此,只要集成了接口,就可以调用方法,因为通用绑定实现了表的唯一性。

BaseMapper的CRUD接口

public interface BTfourMapper extends BaseMapper<BT_04>{
    

}

任意接口继承BaseMapper就可以使用其方法,在通过泛型绑定变成相关表的专属mapper,例如

//mybatis

public interface UserMapper{
    
	/** *相关CURD操作 */
	
	List<User> queryAll();

	...
		
	//每个查询返回结果使用泛型接收,所以mapper下的有需要的都要写
}


//mybatis-plus
public interface UserMapper extends BaseMapper<User>{
    
} 

//在接口处使用泛型,后续直接使用

BaseMapper的具体方法和使用请参考官网

映射器示例:

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class BTtest {
    

    @Autowired
    BTfourMapper bTfourMapper;

    @Test
    public void method(){
    
        List<BT_04> bt_04s = bTfourMapper.selectList(null);
        System.out.println(bt_04s);

    }
}

本次单元测试中自动组装的mapper是一个只集成了BaseMapper的接口。 selectList是接口super的方法。参数为Wraper(查询条件包装器),null不填。

在这里插入图片描述

BaseMapper中包含了基本的CURD操作,其实就是对mapper做了一个包装,因为每个mapper无外乎都是CURD操纵,具有统一型,面向不同的对象又具有多样性,包装后的mapper具有通用性。---- 感谢开发团队 -

下面是BaseMapper的源码:

public interface BaseMapper<T> {
    
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);

    IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}

需要注意的是,使用BaseMapper时,必须添加泛型。

Model的CRUD接口

细心的朋友可能会发现,在使用mapper的时候,是有对应的xml文件映射的,或者是有对应的注解绑定在sql语句上的?

是的,上面的例子中并没有出现sql语句。它是如何实施的? CURD 操作也有相似之处,例如

// 表a查询

select username,password,status from user

//查询表2

select code,name,status from sys_dict

同样的,只要表的字段与对应的SQL条件匹配就可以通用,如下所示:

package com.baomidou.mybatisplus.core.enums;

public enum SqlMethod {
    
    INSERT_ONE("insert", "插入一条数据(选择字段插入)", "<script>\nINSERT INTO %s %s VALUES %s\n</script>"),
    DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "<script>\nDELETE FROM %s WHERE %s=#{%s}\n</script>"),
    DELETE_BY_MAP("deleteByMap", "根据columnMap 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>"),
    DELETE("delete", "根据 entity 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>"),
    DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量删除数据", "<script>\nDELETE FROM %s WHERE %s IN (%s)\n</script>"),
    LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),
    LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),
    LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", "<script>\nUPDATE %s %s WHERE %s IN (%s) %s\n</script>"),
    UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    UPDATE("update", "根据 whereEntity 条件,更新记录", "<script>\nUPDATE %s %s %s\n</script>"),
    LOGIC_UPDATE_BY_ID("updateById", "根据ID 修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    LOGIC_UPDATE_ALL_COLUMN_BY_ID("updateAllColumnById", "根据ID 选择修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
    SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s}"),
    SELECT_BY_MAP("selectByMap", "根据columnMap 查询一条数据", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>\nSELECT %s FROM %s WHERE %s IN (%s)\n</script>"),
    SELECT_ONE("selectOne", "查询满足条件一条数据", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_COUNT("selectCount", "查询满足条件总记录数", "<script>\nSELECT COUNT(%s) FROM %s %s\n</script>"),
    SELECT_LIST("selectList", "查询满足条件所有数据", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_PAGE("selectPage", "查询满足条件所有数据(并翻页)", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_MAPS("selectMaps", "查询满足条件所有数据", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_MAPS_PAGE("selectMapsPage", "查询满足条件所有数据(并翻页)", "<script>\nSELECT %s FROM %s %s\n</script>"),
    SELECT_OBJS("selectObjs", "查询满足条件所有数据", "<script>\nSELECT %s FROM %s %s\n</script>"),
    LOGIC_SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s"),
    LOGIC_SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>\nSELECT %s FROM %s WHERE %s IN (%s) %s\n</script>");

    private final String method;
    private final String desc;
    private final String sql;

    private SqlMethod(String method, String desc, String sql) {
    
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }

    public String getMethod() {
    
        return this.method;
    }

    public String getDesc() {
    
        return this.desc;
    }

    public String getSql() {
    
        return this.sql;
    }
}

SqlMethod是mybatis封装sql的类,位于com.baomidou.mybatisplus.core.enums包下该类的返回值是一个集合。通过SqlMethod.获取需要的sql语句。

SqlMethod不是泛型没有通用性,还需要包装类对其解析封装并具有泛型,这个类就是Model

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.extension.activerecord;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.TableInfoHelper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;

public abstract class Model<T extends Model> implements Serializable {
    
    private static final long serialVersionUID = 1L;

    public Model() {
    
    }

    public boolean insert() {
    
        SqlSession sqlSession = this.sqlSession();

        boolean var2;
        try {
    
            var2 = SqlHelper.retBool(sqlSession.insert(this.sqlStatement(SqlMethod.INSERT_ONE), this));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var2;
    }

    public boolean insertOrUpdate() {
    
        return !StringUtils.checkValNull(this.pkVal()) && !Objects.isNull(this.selectById(this.pkVal())) ? this.updateById() : this.insert();
    }

    public boolean deleteById(Serializable id) {
    
        SqlSession sqlSession = this.sqlSession();

        boolean var3;
        try {
    
            var3 = SqlHelper.delBool(sqlSession.delete(this.sqlStatement(SqlMethod.DELETE_BY_ID), id));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var3;
    }

    public boolean deleteById() {
    
        Assert.isFalse(StringUtils.checkValNull(this.pkVal()), "deleteById primaryKey is null.", new Object[0]);
        return this.deleteById(this.pkVal());
    }

    public boolean delete(Wrapper<T> queryWrapper) {
    
        Map<String, Object> map = new HashMap(1);
        map.put("ew", queryWrapper);
        SqlSession sqlSession = this.sqlSession();

        boolean var4;
        try {
    
            var4 = SqlHelper.delBool(sqlSession.delete(this.sqlStatement(SqlMethod.DELETE), map));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var4;
    }

    public boolean updateById() {
    
        Assert.isFalse(StringUtils.checkValNull(this.pkVal()), "updateById primaryKey is null.", new Object[0]);
        Map<String, Object> map = new HashMap(1);
        map.put("et", this);
        SqlSession sqlSession = this.sqlSession();

        boolean var3;
        try {
    
            var3 = SqlHelper.retBool(sqlSession.update(this.sqlStatement(SqlMethod.UPDATE_BY_ID), map));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var3;
    }

    public boolean update(Wrapper<T> updateWrapper) {
    
        Map<String, Object> map = new HashMap(2);
        map.put("et", this);
        map.put("ew", updateWrapper);
        SqlSession sqlSession = this.sqlSession();

        boolean var4;
        try {
    
            var4 = SqlHelper.retBool(sqlSession.update(this.sqlStatement(SqlMethod.UPDATE), map));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var4;
    }

    public List<T> selectAll() {
    
        SqlSession sqlSession = this.sqlSession();

        List var2;
        try {
    
            var2 = sqlSession.selectList(this.sqlStatement(SqlMethod.SELECT_LIST));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var2;
    }

    public T selectById(Serializable id) {
    
        SqlSession sqlSession = this.sqlSession();

        Model var3;
        try {
    
            var3 = (Model)sqlSession.selectOne(this.sqlStatement(SqlMethod.SELECT_BY_ID), id);
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var3;
    }

    public T selectById() {
    
        Assert.isFalse(StringUtils.checkValNull(this.pkVal()), "selectById primaryKey is null.", new Object[0]);
        return this.selectById(this.pkVal());
    }

    public List<T> selectList(Wrapper<T> queryWrapper) {
    
        Map<String, Object> map = new HashMap(1);
        map.put("ew", queryWrapper);
        SqlSession sqlSession = this.sqlSession();

        List var4;
        try {
    
            var4 = sqlSession.selectList(this.sqlStatement(SqlMethod.SELECT_LIST), map);
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var4;
    }

    public T selectOne(Wrapper<T> queryWrapper) {
    
        return (Model)SqlHelper.getObject(this.selectList(queryWrapper));
    }

    public IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper) {
    
        Map<String, Object> map = new HashMap(2);
        map.put("ew", queryWrapper);
        map.put("page", page);
        SqlSession sqlSession = this.sqlSession();

        try {
    
            page.setRecords(sqlSession.selectList(this.sqlStatement(SqlMethod.SELECT_PAGE), map));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return page;
    }

    public Integer selectCount(Wrapper<T> queryWrapper) {
    
        Map<String, Object> map = new HashMap(1);
        map.put("ew", queryWrapper);
        SqlSession sqlSession = this.sqlSession();

        Integer var4;
        try {
    
            var4 = SqlHelper.retCount((Integer)sqlSession.selectOne(this.sqlStatement(SqlMethod.SELECT_COUNT), map));
        } finally {
    
            this.closeSqlSession(sqlSession);
        }

        return var4;
    }

    public SqlRunner sql() {
    
        return new SqlRunner(this.getClass());
    }

    protected SqlSession sqlSession() {
    
        return SqlHelper.sqlSession(this.getClass());
    }

    protected String sqlStatement(SqlMethod sqlMethod) {
    
        return this.sqlStatement(sqlMethod.getMethod());
    }

    protected String sqlStatement(String sqlMethod) {
    
        return SqlHelper.table(this.getClass()).getSqlStatement(sqlMethod);
    }

    protected Serializable pkVal() {
    
        return (Serializable)ReflectionKit.getMethodValue(this, TableInfoHelper.getTableInfo(this.getClass()).getKeyProperty());
    }

    protected void closeSqlSession(SqlSession sqlSession) {
    
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.getClass()));
    }
}

有了Model后,直接使用该类可以进行CRUD,但必须有一个对应的继承BaseMapper的mapper。基本步骤就是实体类继承Model添加泛型,对应Mapper继承BaseMapper,然后实体类对象也可以进行CRUD操作。

例子:

//实体类继承Model添加泛型
@TableName("user")
public class User extends Model<User> {
    

    @TableId
    private int id;

    @TableField("username")
    private String username;

    @TableField("password")
    private String password;
}
//对应接口继承BaseMapper
public interface UserMapper extends BaseMapper<User>{
    
}
//实体类直接调用方法
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserMapperTest {
    

    //@Autowired User user;

    @Test
    public void one(){
    
        User user = new User();
        List<User> users = user.selectList(null);
        System.out.println(users);
    }
}

Model调用CRUD接口的限制是需要接收实体类参数。

IService的CRUD接口

服务调用方法的基本方法调用的服务层比mapper层多。

//实体类
@TableName("user_address")
public class UserAddress {
    

    private int id;

    private int address_id;

    private String detail_address;

    private int user_id;

    private Date create_time;

    private Date update_time;

    private String name;

    private String phone;
}
//mapper层
public interface UserAddressMapper extends BaseMapper<UserAddress> {
    
}

如果mapper没有@MapperScan注解,则需要添加@Component

service接口继承Iservice,并且service接口的实现类要继承ServiceImpl<xxxMapper,pojo>

//service层

//接口
public interface UserAddressService extends IService<UserAddress> {
    
}

//接口实现类
@Service
public class UserAddressServiceImpl extends ServiceImpl<UserAddressMapper, UserAddress>implements UserAddressService{
    
}

在这里插入图片描述

具体在哪一层调用需要根据实际需要而定。

//测试
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class UserAddressTest {
    

    @Autowired
    UserAddressService userAddressService;

    @Test
    public void methodOne(){
    
        UserAddress one = userAddressService.getById(1);
        System.out.println(one);
    }
}

在这里插入图片描述

总结:

1、模型:使用映射类pojo继承模型。使用该类可以直接进行CRUD操作,但是必须有对应的继承BaseMapper的Mapper;

2、BaseMapper和Iservice
BaseMapper是使用dao层数据进行CRUD,只需要进行使用dao层接口继承BaseMapper即可;
Iservice是使用service层进行CRUD,需要使用service接口继承Iservice,并且service接口的实现类要继承ServiceImpl<xxxMapper,pojo>;

BaseMapper和Iservice中提供的方法类似,只不过Iservice提供了批量操作的实现,例如:批量添加、批量修改。

. . .

相关推荐

额外说明

zk ACL 权限

                                                                                             

额外说明

深度学习基础入门篇[六]:模型调优,学习率设置(Warm Up、loss自适应衰减等),batch size调优技巧,基于方差放缩初始化方法。

【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍:【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、

额外说明

【Java】三种方式快速统计代码运行时长

方法一 (推荐) 使用apache提供的工具类 import org.apache.commons.lang3.time.StopWatch; public class Test { public static void main(Strin

额外说明

【Unity3D 灵巧小知识点】 | 使用宏定义和Application.platform判断运行平台

Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方

额外说明

nginx配置访问本地静态资源

nginx作为一款高性能的服务器,用途很多,除了可以做后端服务器的代理,负载均衡之外你,还有一个用途就是做静态资源的缓存服务器,比如在前后端分离的项目中,为了加速前端页面的响应速度,我们可以将前端的相关资源,例如html,js,css或者图片等放到ngi

额外说明

自动遍历测试工具appcrawler学习

自动遍历测试用例维护成本较低,而且能够尽可能的自动化覆盖回归业务 常见遍历工具主要有 Google android原生monkey以及app crawler 百度smartmonkey 腾讯newmonkey 头条的zhangzhao maxim sev

额外说明

企业级信息系统开发讲课笔记3.2 基于Java配置类整合SSM框架实现用户登录

文章目录 零、本节学习目标 一、采用MVC架构 二、用户登录运行效果 三、基于Java配置类整合SSM框架实现用户登录 (一)创建数据库与表 1、创建数据库 2、创建用户表 3、在用户表里插入记录 (二)创建Maven项目 (三)添加相关依赖 (四)创建

额外说明

安卓案例:闪烁满天星

安卓案例:闪烁满天星 一、运行效果 星星的位置和颜色都会不断地变化。 二、涉及知识点 1、自定义视图 2、定时器 3、定时器任务 4、线程 三、实现步骤 1、创建安卓应用StarrySky 2、准备月亮图片moon.jpg,拷贝到mipmap目录里

额外说明

回车与换行的区别

回车与换行的区别 符号 ASCII码 意义 \n 10 换行 \r 13 回车CR windows – 回车 == 先回车后换行 windows下的点一下回车,效果是:回车换行,就是\r\n ‘\r’ 回车,回到当前行的行首,而不会换到下一行,如果接着输

额外说明

Java微服务篇2——SpringCloud

Java微服务篇2——SpringCloud 1、微服务架构 1.1、单体应用架构 的⽤户量、数据量规模都⽐较⼩,项目所有的功能模块都放在一个工程中编码、 编译、打包并且部署在一个Tomcat容器中的架构模式就是单体应用架构,这样的架构既简单实 ⽤、便于

ads via 小工具