独立完成系统开发五:mybatis-plus及代码生成器使用

独立完成系统开发五:mybatis-plus及代码生成器使用

mybatis-plus使用基础

如果你会使用mybatis,那么我相信mybatis-plus将会变得很容易,当然如果不了解mybatis那么请先去了解mybatis,因为mybatis-plus是mybatis的增强。

先说一下他的基本使用吧。mybatis-plus在项目的中的使用可以参考官网中快速开始的例子, 不过这个例子并不是写的很完整,所以我就在官网中的例子的基础上把他补全吧。把这例子看完后,我觉得mybatis-plus的使用就没问题了。

表结构

这里直接使用官网的:

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
	id INT NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
	age INT(11) NULL DEFAULT NULL COMMENT '年龄',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (id)
);

sprinboot中的依赖

<!--引入 Spring Boot Starter 父工程-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.4.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
        <!-- srpingboot依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
    	</dependency>
  		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  
 		 <!--mybatis-plus依赖 这里使用了mysql数据库,以及druid数据源-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
          	<version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
</dependencies>

配置

这里直接在application.yml中进行配置

spring:
  # DataSource druid Config
  datasource:
    username: root
    password: you_parssword
    url: jdbc:mysql://localhost:3306/myadmin_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    
#mybatis-plus配置
mybatis-plus:mapping包下
  #mapper.xml文件位置,放在项目中的mapping包下
  mapper-locations: classpath:com/myadminmain/**/mapping/*.xml
  configuration:
    #下划线大写转换,这个官网中的配置说是默认开启的,所以不加也行
    map-underscore-to-camel-case: true

添加 @MapperScan 注解,Mapper 文件扫描,当然如果不添加也行,不过你要在每个mapper接口中添加@Mapper注解。所以还是建议使用@MapperScan 注解,@MapperScan 注解可以添加在启动类中,也可以添加在mybatis-plus的配置类中。推荐加到配置类上,不过这里为了好演示就直接加到启动类中了

@SpringBootApplication
@MapperScan(basePackages = {"com.myadminmain.modular.*.dao","com.myadminmain.sys.*.dao"})
public class MyadminMainApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyadminMainApplication.class, args);
	}

}

entity类

@TableName("user")
public class User {
    // id 使用数据库自增
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
  
    @TableField("name")
    private String name;
  
    @TableField("age")
    private Integer age;
  
    @TableField("email")
    private String email;
  
    //get和set方法省略,当然有可能像官网中那样使用lombok,来自动添加get和set方法
}

这里如果数据库中的字段名和代码中的属性名一致,注解可以不用添加,但是在不一致的时候必须添加

Controller类

@RestController
@RequestMapping("/user")
public class UserController{

    @Autowired
    private UserService userService;

    /**
     * 功能描述: 查询所有用户
     * @param
     * @return java.util.List<com.myadminmain.sys.user.entity.User>
     * @author cdfan
     * @date 2020-03-22
     */
    @RequestMapping(value="/userInfo", method= RequestMethod.GET)
    public List<User> list() {
        return userService.userList();
    }
  
  /**
     * 功能描述: 根据主键查询用户,这里直接调用mybatis-plus提供的方法
     * @param userId
     * @return com.myadminmain.sys.user.entity.User
     * @author cdfan
     * @date 2020-03-22
     */
    @RequestMapping(value="/userInfo/{userId}", method= RequestMethod.GET)
    public User get(@PathVariable("userId") Integer userId) {
        return userService.getById(userId);
    }
  
   /**
     * 功能描述: 根据用户名,查询用户
     * @param name
     * @return java.util.List<com.myadminmain.sys.user.entity.User>
     * @author cdfan
     * @date 2020-03-22
     */
    @RequestMapping(value="/getUserByName", method= RequestMethod.GET)
    public List<User> getUserByName(@RequestParam(value = "name", required = false) String name) {
        return userService.getUserByName(name);
    }
}

Service

UserService接口
public interface UserService extends IService<User> {

    /**
     * 功能描述: 查询所有用户
     * @param
     * @return java.util.List<com.myadminmain.sys.user.entity.User>
     * @author cdfan
     * @date 2020-03-22
     */
    List<User> userList();
  
  	/**
     * 功能描述: 根据用户名,查询用户
     * @param name
     * @return java.util.List<com.myadminmain.sys.user.entity.User>
     * @author cdfan
     * @date 2020-03-22
     */
  	List<User> getUserByName(String name);
}

这里一定要继承mybatis-plus提供的IService接口并在泛型中指定我们的实体类型,在这个接口中提供了很多他为我们写好的接口方法,所以继承后我们在Controller中就可以直接调用mybatis-plus为我们提供的方法了

UserServiceImpl接口实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public List<User> userList() {
        return this.list();
    }
  
 	@Override
    public List<User> getUserByName(String name) {
        return this.baseMapper.getUserByName(name);
    }
}

这里实现类也要继承mybatis-plus提供的ServiceImpl类并指定mapper类以及实体类的泛型。这样在我们的service实现类里面就可以直接使用ServiceImpl中定义的接口方法了,当我们要调用我们自己定义的mapper中的接口方法,我们可以通过this.baseMapper.xxx()来调用。其实我们点击去看源码可以发现,ServiceImpl他是实现了IService的,而我们使用的baseMapper其实就是我们传入mapper类,这样我们就可以使用baseMapper来调用我们自己的mapper接口方法了,当然baseMapper还可以调用mybatis-plus提供的方法,因为我们可以看到他要求的泛型中我们提供的mapper必须是一个继承了BaseMapper的mapper类

//ServiceImpl部分源码
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {

    protected Log log = LogFactory.getLog(getClass());

    @Autowired
    protected M baseMapper;
}

mapper接口

public interface UserMapper extends BaseMapper<User> {

    /**
     * 功能描述: 根据用户名,查询用户
     * @param name
     * @return java.util.List<com.myadminmain.sys.user.entity.User>
     * @author cdfan
     * @date 2020-03-22
     */
    List<User> getUserByName(@Param("name") String name);

}

从上面的service实现类中的源码中可以看出,他要求传入的mapper必须要是一个继承了BaseMapper的mapper,所以我们定义自己的mapper的时候需要继承BaseMapper并指定对应的泛型类。

mapper.xml

<?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.myadminmain.sys.user.dao.UserMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.myadminmain.sys.user.entity.User">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="age" property="age" />
        <result column="email" property="email" />
    </resultMap>


    <select id="getUserByName" resultMap="BaseResultMap">
        SELECT u.id,u.name,u.age,r.email
        FROM user u
        WHERE u.name = #{name}
    </select>
</mapper>

mapper.xml里面我们就可以直接写sql我们要的sql了。我的建议是由于mybatis-plus提供了基本的crud方法,所有如果是一些复杂的查询我们可以自己写sql,如果是简单的直接使用mybatis-plus提供的方法就可以了。

总结

通过上面的例子,我相信基本的使用流程是木有问题了的,在了解了基本的使用流程之后,那么你可能会有怎么几个疑问

  • mybatis-plus提供了那些crud接口?参考
  • 提供的接口可以实现自定义条件吗?参考
  • mybatis-plus的配置有那些?参考
  • 实体类中提供了那些注解,注解中可以有那些属性 参考
  • 方法中什么才能称得上是复杂查询?什么时候才需要我们自己写sql

其实这些问题在官网中都可以找到答案,具体链接我放在问题后面了,至于最后一个问题,我认为在mybatis-plus中称得上复杂查询的应该是有多表连接的查询,如果是单表查询不管怎么查我们都可以通过条件构造器构造出来,但是如果多表查询mybatis-plus好像没有实现,不过hibernate倒是实现了。所以如果是多表关联查询还是自己写sql吧。

mybatis-plus实现原理

因为mybatis-plus是基于mybatis的,所以要了解mybatis-plus得先熟悉mybatis的。所以这里我先介绍一下mybatis,下面是我对于mybatis的理解:

首先mybatis底层是通过jdbc对数据库进行操作的,他是对jdbc的封装。相对于jdbc

  • mybatis将sql语句进行了统一的管理也就是将sql统一放到了xml映射文件中
  • 提供的参数映射、结果映射、结果缓存、动态sql以及对重复sql提供了处理

使用mybatis我们只需要在xml映射文件中编写对应的sql并提供对应的接口,使用的时候基于接口直接调用就可以了。而不用像jdbc那样每次使用需要自己创建连接、在java代码中编写sql并构建jdbc Statements对象、自己对结果进行转换处理,释放资源等等。

mybatis的整个流程是,这里只是简单的整体介绍,更详细的请看源码,当然看的时候也建议结合源码一起看:

  • 如果我们是根据配置文件进行配置的那么他首先会根据对配置文件进行解析然后将解析的数据设置到Configuration中,其中我们xml映射文件中定义的每个sql标签会被解析为一个个MappedStatement并存储在Configuration的一个map属性中key为命名空间+语句id,value为MappedStatement对象。然后在通过Configuration创建默认的sqlSessionFactory也就是DefaultSqlSessionFactory
  • 有了 sqlSessionFactory我们就可以通过sqlSessionFactory获取sqlSession了。获取sqlSession中,他会根据事务以及Configuration创建一个Executor,由于默认启用了一级缓存所以Executor默认为CachingExecutor,并且CachingExecutor中包裹了一个默认的SimpleExecutor(对于sqlSession的操作他都会通过Executor来执行,Executor用于维护缓存以及通过StatementHandler构建jdbc Statement并执行jdbc Statement)。然后再通过Configuration和Executor创建sqlSession,默认创建的sqlSession为DefaultSqlSession
  • 创建sqlSession之后我们可以直接通过sqlSession调用对应的方法并传入xml映射文件中定义的方法名全称执行对应的sql。也可以通过sqlSession获取mapper接口对象,然后通过mapper接口对象调用对应的方法进行操作。其中通过sqlSession获取到的mapper接口对象为一个MapperProxy代理对象。对mapper接口对象的调用最后都会转到代理对象中,在代理对象中最后也是通过sqlSession调用对应的方法并传入xml映射文件中定义的方法名全称。只不过方法的全称不用我们自己传入由程序根据接口方法解析
  • 在sqlSession的方法中,他会根据传入的方法名全称(namespace+方法id)获取Configuration中加载的MappedStatement。然后再通过Executor执行对应的方法并传入MappedStatement和参数
  • 在Executor的方法中由于他默认创建的是CachingExecutor,所以他会先获取当前MappedStatement的缓存key,然后判断是否存在缓存。如果存在则直接从缓存中获取结果。如果不存在则会通过CachingExecutor中的SimpleExecutor调用对应的方法,最后会调用SimpleExecutor的doQuerydoUpdate方法。在方法中他会通过Configuration和MappedStatement以及参数之类的创建StatementHandler,具体创建的StatementHandler实现会根据xml映射文件中定义的标签的statementType属性决定。默认为PreparedStatementHandler。并且PreparedStatementHandler中还会创建ParameterHandler用于对参数处理以及ResultSetHandler用于对结果处理。默认ParameterHandler的实现类为DefaultParameterHandler,ResultSetHandler的实现类为DefaultResultSetHandler
  • 拿到StatementHandler后他会先创建Statement,当然这里的Statement为PreparedStatement,然后再通过ParameterHandler对PreparedStatement设置参数,在ParameterHandler设置参数的时候会通过TypeHandler类型处理器,对java类型和jdbc数据库类型的进行转换。
  • 最后在通过StatementHandler调用对应的方法执行构建好的PreparedStatement通过jdbc对数据库进行操作。之后如果是查询操作还会再通过resultSetHandler处理结果集,在处理结果集的时候依旧会通过TypeHandler类型处理器对jdbc数据库类型和java类型进行转换。最后在将处理的结果返回

由于mybatis-plus通常都是结合spring一起使用,所以这边结合spring来说,在spring中使用mybatis-plus我们需要注册MybatisSqlSessionFactoryBean,而spring中使用mybatis注册的是SqlSessionFactoryBean。注册后这个类会根据配置信息生成Configuration然后再通过Configuration创建SqlSessionFactory。而他们的区别是mybatis-plus构建的Configuration为MybatisConfiguration。里面定义了一些额外的操作以及配置信息。因为通过mybatis执行sql语句的时候是通过映射文件中的namespace+id找到Configuration中对应的MappedStatement然后进行操作的。每个MappedStatement对应映射文件中定义的sql语句。mybatis-plus之所以不用编写sql就是他默认会根据mapper接口生成对应的MappedStatement,至于其他的逻辑跟mybatis基本一致

在spring中mybatis-plus向Configuration中添加额外的MappedStatement并不是在创建SqlSessionFactory的过程中添加的,创建SqlSessionFactory的过程中只会添加我们在xml映射文件中定义的sql所对应的MappedStatement,这是mybatis原有的逻辑。mybatis-plus提供的MappedStatement是在创建mapper接口所对应的MapperFactoryBean对象的时候添加的。当我们添加了@MapperScan映射器扫描,那么会在spring容器中添加与扫描路径下mapper接口对应的MapperFactoryBean(具体实现是他会修改接口beanDefinition的beanClass为MapperFactoryBean,然后再MapperFactoryBean的构造器参数设置为实际接口类型),当然我们如果不用映射器扫描也可以自己手动在spring容器中注册对应的MapperFactoryBean。MapperFactoryBean实现了FactoryBean,spring容器初始化时会调用FactoryBean对象中getObject方法并将返回的bean添加到spring容器中。而在MapperFactoryBean的getObject会通过SqlSession创建对应的mapper代理对象然后返回,所以在spring容器中就有mapper代理对象了,所以我们可以直接注入mapper对象。而mybatis-plus提供的MappedStatement添加的时机为,MapperFactoryBean在初始化的时候会验证Configuration中是否存在对应的mapper接口,如果不存在就会向Configuration中添加对应的mapper接口并生成对应的MappedStatement。因为使用mybatis-plus之后Configuration为MybatisConfiguration所以添加mapper接口的时候就会根据mybatis-plus的方式通过mapper接口直接生成对应的MappedStatement。

下面可以看到SqlSessionFactory创建后,Configuration中是没有MappedStatement的
在这里插入图片描述
在spring容器初始化时会创建所有的MapperFactoryBean,然后在MapperFactoryBean的初始化方法中调用checkDaoConfig,然后根据mapper添加MappedStatement

  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        // 这里的Configuration实现为MybatisConfiguration。然后根据接口添加MappedStatement
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

分页插件

前面说了mybatis-plus的基本使用,下面在来说一个最常用的分页功能吧,在mybatis-plus中是提供了分页插件的。使用起来也很简单,在添加分页插件之后我们要做的就是是把分页对象构建好,然后传给对应的操作接口就可以了。

配置

分页插件的配置只需要在容器中添加一个组件就可以了

@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

使用

使用的时候分两种情况:

一种是直接调用mybatis-plus提供的分页方法进行分页例如:IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);这里page为分页对象,而queryWarpper为查询时的条件构造对象

还有一种方法是我们调用我们自己写的sql进行分页,在调用我们自己写的sql时,我们只需要在定义mapper接口时第一个参数要求传入分页对象就可以了,如果第一个参数为分页对象,那么mapper接口调用时mybatis-plus会自动对sql进行分页。

在我们调用分页方法的时候他会自动返回一个分页对象,至于对象中有哪些属性,大家可以自己去源码里面看看page对象中的属性哈。

分页对象的构建

分页对象的构建这里写一下我自己的方案。首先在需要进行分页的时候前端会自动传入这么几个参数:

  • currentPage(当前页,页码)
  • limit (每页多少条数据)
  • sort (排序字段名称多个以,分割)
  • order (asc或desc(升序或降序))

所以我们可以自己定义一个方法,使用的时候直接调用这个方法就可以获取分页对象了

public <T> Page<T> defaultPage(Class<T> clazz) {
        HttpServletRequest request = HttpUtil.getRequest();
        //当前页,页码
        int currentPage = Integer.valueOf(ObjectUtils.isEmpty(request.getParameter("currentPage")) ? "1" : request.getParameter("currentPage"));
        //每页多少条数据
        int limit = Integer.valueOf(ObjectUtils.isEmpty(request.getParameter("limit")) ? "10" : request.getParameter("limit"));

        //排序字段名称以,分割
        String sortStr = request.getParameter("sort");
        String[] sort = null;
        if(!ObjectUtils.isEmpty(sortStr)){
            sort = sortStr.split(",");
            for (int i = 0; i < sort.length; i++) {
                // 将大写转换为下划线的格式
                sort[i] = StrUtil.underscoreName(sort[i]);
            }
        }
        //asc或desc(升序或降序)
        String order = request.getParameter("order");

        Page<T> page = new Page<T>(currentPage, limit);
        if (ObjectUtils.isEmpty(sort)) {
            return page;
        } else {
            List<OrderItem> orderList;
            if (Order.ASC.getDes().equals(order)) {
                orderList = OrderItem.ascs(sort);
            } else {
                //默认为降序
                orderList = OrderItem.descs(sort);
            }
            page.setOrders(orderList);
            return page;
        }
}

添加这个工厂之后,后面获取分页对象我们只需要直接使用Page<User> page = this.defaultPage(User.class);就可以获取分页对象了。

代码生成器

代码生成器的核心的实现无非就是定义好模板之后,在将获取的数据填充到模板中在生成文件。市面上代码生成器有很多例如:懒猴子CGMagicalCoder等等这些都是免费的。但是把代码的生成依赖于第三方这个好像不太合适哈,首先安全性得不到保障而且万一没有外网呢。所以项目里面还是得有自己的代码生成器的。为了避免重复造轮子,mybatis-plus是为我们提供了代码生成器的,通过mybatis-plus可以帮我们生成后台中用的代码。并且模板、数据都可以自定义。

并且mybatis-plus除了可以生成代码中经常使用的Controller、Service、ServiceImpl、Mapper、Mapper.xml、Entity这几个文件我们还可以用它来生成一些其他额外的文件,例如我们可以让他生成前端的文件,当然这需要进行额外配置,具体可以参考官网的例子写的还是挺详细的

生成器的使用

生成器的使用在官网上有个很完整的例子根据提供的配置项进行配置就可以了,配置项可以分为这么几部分:

DataSourceConfig(数据源配置)、StrategyConfig( 数据库表配置)、PackageConfig( 包名配置)、TemplateConfig( 模板配置)、GlobalConfig(全局策略配置)、InjectionConfig(注入配置

每种配置都提供了很多的配置项,结合官网的例子根据实际情况进行配置就可以了,这里就不给实际例子了。大家可以参考官网中的例子

这里主要说一个很重要的问题:那就是通常我们的代码不可能每个项目都相同,所以这个时候我们就需要自定义模板。

mybatis-plus支持的模板引擎有3中分别是:Velocity、Freemarker以及Beetl,默认使用的是Velocity。我们可以自己选择熟悉的模板引擎来编写模板。由于我那个都不熟悉所以就选择了默认的。其实选择模板引擎就是选择哪种语法来编写你的模板,这个so easy啦,语法嘛看几眼就会了哈。所以Velocity的使用大家可以自己问度娘哈。

模板的语法搞定了之后,那么还存在一个问题,那就是模板中可以使用的变量有那些呢?

这个问题我之前由于眼瞎找了很久(看文档一定要仔细啊),模板中可以使用的变量在官网中其实已经说了,但是之前没仔细看文档还找了一下午,哎,说多了都是泪。这个我给个官网的截图:

在这里插入图片描述

其实就写在最前面,他说: 在AbstractTemplateEngine 类中方法 getObjectMap 返回 objectMap 的所有值都可以在模板中使用。我们来看看AbstractTemplateEngine 中的getObjectMap 方法,至于具体的值是什么大家可以自行debug去看源码哈

    /**
     * 渲染对象 MAP 信息
     *
     * @param tableInfo 表信息对象
     * @return ignore
     */
    public Map<String, Object> getObjectMap(TableInfo tableInfo) {
        Map<String, Object> objectMap = new HashMap<>(30);
        ConfigBuilder config = getConfigBuilder();
        if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
            objectMap.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
            objectMap.put("controllerMappingHyphen", StringUtils.camelToHyphen(tableInfo.getEntityPath()));
        }
        objectMap.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
        objectMap.put("config", config);
        objectMap.put("package", config.getPackageInfo());
        GlobalConfig globalConfig = config.getGlobalConfig();
        objectMap.put("author", globalConfig.getAuthor());
        objectMap.put("idType", globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString());
        objectMap.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName());
        objectMap.put("versionFieldName", config.getStrategyConfig().getVersionFieldName());
        objectMap.put("activeRecord", globalConfig.isActiveRecord());
        objectMap.put("kotlin", globalConfig.isKotlin());
        objectMap.put("swagger2", globalConfig.isSwagger2());
        objectMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        objectMap.put("table", tableInfo);
        objectMap.put("enableCache", globalConfig.isEnableCache());
        objectMap.put("baseResultMap", globalConfig.isBaseResultMap());
        objectMap.put("baseColumnList", globalConfig.isBaseColumnList());
        objectMap.put("entity", tableInfo.getEntityName());
        objectMap.put("entitySerialVersionUID", config.getStrategyConfig().isEntitySerialVersionUID());
        objectMap.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
        objectMap.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
        objectMap.put("chainModel", config.getStrategyConfig().isChainModel());
        objectMap.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
        objectMap.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
        objectMap.put("superEntityClass", getSuperClassName(config.getSuperEntityClass()));
        objectMap.put("superMapperClassPackage", config.getSuperMapperClass());
        objectMap.put("superMapperClass", getSuperClassName(config.getSuperMapperClass()));
        objectMap.put("superServiceClassPackage", config.getSuperServiceClass());
        objectMap.put("superServiceClass", getSuperClassName(config.getSuperServiceClass()));
        objectMap.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
        objectMap.put("superServiceImplClass", getSuperClassName(config.getSuperServiceImplClass()));
        objectMap.put("superControllerClassPackage", verifyClassPacket(config.getSuperControllerClass()));
        objectMap.put("superControllerClass", getSuperClassName(config.getSuperControllerClass()));
        return Objects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap);
    }

所以说只要在这个方法中添加到objectMap中的数据在模板中都是可以直接使用的。

当然除了objectMap中的数据,我们还可以自己传入我们额外自定义的数据

InjectionConfig injectionConfig = new InjectionConfig() {
    //自定义属性注入:abc
    //在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
    @Override
    public void initMap() {
        Map<String, Object> map = new HashMap<>();
        // 设置自定义的值
        map.put("customKey", "value");
        this.setMap(map);
    }
};
AutoGenerator mpg = new AutoGenerator();
//配置自定义属性注入
mpg.setCfg(injectionConfig);

这样配置之后我们在模板中直接通过$!{cfg.customKey}就可以引用我们设置的属性了。

最后给一个我的Controller的模板,这里面包含了基本的curd操作:

package ${package.Controller};

##  获取主键
#foreach($field in ${table.fields})
    #if(${field.keyFlag})
        #set($keyPropertyName=${field.propertyName})
    #end
#end
##  获取主键的get方法
#set($keyPropertyGetMethod="get"+$keyPropertyName.substring(0,1).toUpperCase()+$keyPropertyName.substring(1)+"()")
## 查询条件
#set($queryFields=${cfg.queryFields})
#set($tableFields=${table.fields})
## 获取查询字段信息
#set($filterFields=[])
#foreach($field in $tableFields)
    #foreach($filterCode in $queryFields)
        #if(${field.propertyName} == $filterCode)
            #set($temp=$filterFields.add($field))
        #end
    #end
#end
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.common.resultdata.ResultData;
import com.common.annotion.BussinessLog;
import com.common.annotion.Permission;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import com.exception.MyAdminException;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#if(!$queryFields.isEmpty())
import org.springframework.web.bind.annotation.RequestParam;
#end
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
import ${package.Entity}.${entity};
import ${package.Service}.${table.serviceName};

import java.util.List;

/**
 * @author ${author}
 * @version 1.0
 * @date ${date}
 * @description: $!{cfg.modelName}管理 前端控制器
 */
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
@Api(tags = "$!{table.controllerName}", description = "$!{cfg.modelName}操作api")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()
#end
#else
    #if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
    #else
public class ${table.controllerName} {
    #end

    @Autowired
    private ${table.serviceName} ${table.entityPath}Service;

    /**
     * 功能描述: 根据主键查询$!{cfg.modelName}
     *
     * @param ${keyPropertyName} $!{cfg.modelName}id
     * @return com.common.resultdata.ResultData<${package.Entity}.${entity}>
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_query")
    @BussinessLog("$!{cfg.modelName}-单记录查询")
    @ApiOperation(value = "$!{cfg.modelName}-单记录查询", notes = "根据主键查询$!{cfg.modelName}")
    @ApiImplicitParam(name = "${keyPropertyName}", value = "$!{cfg.modelName}id", required = true, dataType = "int", paramType = "path")
    @RequestMapping(value = "/${table.entityPath}Info/{${keyPropertyName}}", method = RequestMethod.GET)
    public ResultData<${entity}> get(@PathVariable("${keyPropertyName}") Integer ${keyPropertyName}) {
        return new ResultData<${entity}>(${table.entityPath}Service.getById(${keyPropertyName}));
    }

    /**
     * 功能描述: 查询所有$!{cfg.modelName}
     *
     * @return com.common.resultdata.ResultData<java.util.List<${package.Entity}.${entity}>>
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_query")
    @BussinessLog("$!{cfg.modelName}-列表查询")
    @ApiOperation(value = "$!{cfg.modelName}-列表查询", notes = "查询所有$!{cfg.modelName}")
    @RequestMapping(value = "/${table.entityPath}Info", method = RequestMethod.GET)
    public ResultData<List<${entity}>> list() {
        return new ResultData<List<${entity}>>(${table.entityPath}Service.list());
    }

    /**
     * 功能描述: 分页查询$!{cfg.modelName}
     *
     * @return com.common.resultdata.ResultData<com.baomidou.mybatisplus.core.metadata.IPage<${package.Entity}.${entity}>>
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_query")
    @BussinessLog("$!{cfg.modelName}-分页查询")
    @ApiOperation(value = "$!{cfg.modelName}-分页查询", notes = "分页查询$!{cfg.modelName}")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "currentPage", value = "当前页,页码,默认为1", dataType = "int", paramType = "query"),
        @ApiImplicitParam(name = "limit", value = "每页多少条数据,默认为10", dataType = "int", paramType = "query"),
        @ApiImplicitParam(name = "sort", value = "排序字段", dataType = "String", paramType = "query"),
#if(!$queryFields.isEmpty())
        @ApiImplicitParam(name = "order", value = "asc或desc(升序或降序),默认为升序", dataType = "String", paramType = "query"),
    #foreach($field in $filterFields)
        #set($fieldType=${field.propertyType})
        #set($fieldComment=${field.comment})
        #if(${field.propertyType}=="LocalDateTime")
            #set($fieldType="String")
            #set($fieldComment=${field.comment}+",格式为yyyy-MM-dd,范围日期用,号分割")
        #end
        #if($foreach.count==$filterFields.size())
        @ApiImplicitParam(name = "${field.propertyName}", value = "$fieldComment", dataType = "$fieldType", paramType = "query")})
        #else
        @ApiImplicitParam(name = "${field.propertyName}", value = "$fieldComment", dataType = "$fieldType", paramType = "query"),
        #end
    #end
#else
        @ApiImplicitParam(name = "order", value = "asc或desc(升序或降序),默认为升序", dataType = "String", paramType = "query")})
#end
    @RequestMapping(value = "/${table.entityPath}InfoPage", method = RequestMethod.GET)
#if(!$queryFields.isEmpty())
    public ResultData<IPage<${entity}>> page(
    #foreach($field in $filterFields)
        #set($fieldType=${field.propertyType})
        #if(${field.propertyType}=="LocalDateTime")
            #set($fieldType="String")
        #end
        #if($foreach.count==$filterFields.size())
            @RequestParam(value = "${field.propertyName}", required = false) $fieldType ${field.propertyName}) {
        #else
            @RequestParam(value = "${field.propertyName}", required = false) $fieldType ${field.propertyName},
        #end
    #end
#else
    public ResultData<IPage<${entity}>> page() {
#end
        Page<${entity}> page = this.defaultPage(${entity}.class);
#if(!$queryFields.isEmpty())
    #set($arg = "page")
    #foreach($field in $filterFields)
        #set($arg=$arg+", "+${field.propertyName})
    #end
        return new ResultData<IPage<${entity}>>(${table.entityPath}Service.pageInfo($arg));
#else
        return new ResultData<IPage<${entity}>>(${table.entityPath}Service.page(page));
#end
    }

    /**
     * 功能描述: 查询$!{cfg.modelName}数据的数量
     *
     * @return com.common.resultdata.ResultData<java.lang.Integer>
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_query")
    @BussinessLog("$!{cfg.modelName}-数量查询")
    @ApiOperation(value = "$!{cfg.modelName}-数量查询", notes = "查询$!{cfg.modelName}数据的数量")
    @RequestMapping(value = "/${table.entityPath}InfoCount", method = RequestMethod.GET)
    public ResultData<Integer> count() {
        return new ResultData<Integer>(${table.entityPath}Service.count());
    }

    /**
     * 功能描述: 新增$!{cfg.modelName}
     *
     * @param ${table.entityPath} $!{cfg.modelName}实体对象
     * @return com.common.resultdata.ResultData
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_add")
    @BussinessLog("$!{cfg.modelName}-新增")
    @ApiOperation(value = "$!{cfg.modelName}-新增", notes = "新增$!{cfg.modelName}")
    @ApiImplicitParam(name = "${table.entityPath}", value = "$!{cfg.modelName}实体对象", required = true, dataType = "${entity}", paramType = "body")
    @RequestMapping(value = "/${table.entityPath}Info", method = RequestMethod.POST)
    public ResultData add(@RequestBody ${entity} ${table.entityPath}) {
        ${table.entityPath}Service.save(${table.entityPath});
        return SUCCESS;
    }

    /**
     * 功能描述: 修改$!{cfg.modelName},对象中必须有主键
     *
     * @param ${table.entityPath} $!{cfg.modelName}实体对象
     * @return com.common.resultdata.ResultData
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_edit")
    @BussinessLog("$!{cfg.modelName}-修改")
    @ApiOperation(value = "$!{cfg.modelName}-修改", notes = "修改$!{cfg.modelName},对象中必须有主键")
    @ApiImplicitParam(name = "${table.entityPath}", value = "$!{cfg.modelName}实体对象", required = true, dataType = "${entity}", paramType = "body")
    @RequestMapping(value = "/${table.entityPath}Info", method = RequestMethod.PUT)
    public ResultData update(@RequestBody ${entity} ${table.entityPath}) {
        if(ObjectUtils.isEmpty(${table.entityPath}.${keyPropertyGetMethod})){
            throw new MyAdminException("修改对象中必须存在主键");
        }
        ${table.entityPath}Service.updateById(${table.entityPath});
        return SUCCESS;
    }

    /**
     * 功能描述: 根据主键删除$!{cfg.modelName}
     *
     * @param ${keyPropertyName} $!{cfg.modelName}id
     * @return com.common.resultdata.ResultData
     * @author ${author}
     * @date ${date}
     */
    @Permission("${table.entityPath}_delete")
    @BussinessLog("$!{cfg.modelName}-删除")
    @ApiOperation(value = "$!{cfg.modelName}-删除", notes = "根据主键删除$!{cfg.modelName}")
    @ApiImplicitParam(name = "${keyPropertyName}", value = "$!{cfg.modelName}id", required = true, dataType = "int", paramType = "path")
    @RequestMapping(value = "/${table.entityPath}Info/{${keyPropertyName}}", method = RequestMethod.DELETE)
    public ResultData delete(@PathVariable("${keyPropertyName}") Integer ${keyPropertyName}) {
        ${table.entityPath}Service.removeById(${keyPropertyName});
        return SUCCESS;
    }

}
#end

具体生成的代码是什么样的大家可以到演示地址去代码生成器中查看哈

除了上面说的这几个功能,mybatis-plus还提供了很多很好用的插件,例如自动填充功能、性能分析插件等等,有兴趣大家可以自己去官网上逛逛

结语

使用mybatis-plus以及他的代码生成器在实际开发中大概可以减少50%-80%的工作量,因为首先我们对于数据库表的插入、修改、删除是不需要在编写sql了的当然一些简单的查询也不需要在编写sql,直接调用对应的方法就可以了,还有就是对于项目中用到的文件类我们都可以通过代码生成器直接生成,因为可以自定义模板,所以我们可以在模板中将一些常用的以及可以预知的代码逻辑写入模板到时候直接生成就可以了。所以使用mybatis-plus以及代码生成器之后,我们的主要工作就是编写业务逻辑,以及在代码生成不符合要求的时候改改代码就可以了。这样在很大程度上减少了一些重复的工作从而极大的提高工作效率。

项目地址:githubgitee演示环境(账号/密码:admin/123456)

上一篇:独立完成系统开发四:前端功能优化及插件分享

下一篇:独立完成系统开发六:安全管理之认证

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Mybatis-Plus 是一个基于 Mybatis 的增强工具,它简化了 Mybatis开发流程,提供了很多实用的功能,其中之一就是代码生成器代码生成器可以根据数据库表结构自动生成实体类、mapper 接口、xml 文件等代码,极大地提高了开发效率。但是,Mybatis-Plus 自动生成的实体类并不一定满足我们的需求,因此我们需要自定义模板来生成我们需要的类。 下面,我将介绍如何使用 Mybatis-Plus 自定义模板生成 dto、vo、convertor。 1. 创建自定义模板 我们需要在 Mybatis-Plus代码生成器中创建自定义模板。打开 Mybatis-Plus代码生成器,点击“自定义模板”选项卡,然后点击“添加模板”按钮,输入模板名称和文件名,然后点击“保存”按钮。 在模板编辑器中,我们可以使用 FreeMarker 语法来编写模板。下面是一个简单的模板示例,用于生成 dto 类: ``` package ${dtoPackage}; import lombok.Data; @Data public class ${dtoName} { <#list table.columnList as column> private ${column.javaType} ${column.propertyName}; </#list> } ``` 在这个模板中,我们使用 `${}` 占位符来引用变量。`dtoPackage` 和 `dtoName` 变量由代码生成器自动提供,表示生成的 dto 类的包名和类名。`table` 变量表示当前生成的表信息,包含表名、列名、列类型等信息。我们使用 `<#list>` 和 `</#list>` 标签来遍历表的列信息,并生成对应的属性。 2. 配置代码生成器使用自定义模板之前,我们需要配置代码生成器。打开 Mybatis-Plus代码生成器,点击“全局配置”选项卡,在“自定义配置”中添加以下配置: ``` # dto 模板配置 dto=templates/dto.java.vm dtoOutputDir=src/main/java/${dtoPackage}/ ``` 其中,`dto` 是我们自定义模板的名称,`dtoOutputDir` 是生成的 dto 类的输出目录。我们还可以添加其他自定义模板和输出目录,例如: ``` # vo 模板配置 vo=templates/vo.java.vm voOutputDir=src/main/java/${voPackage}/ # convertor 模板配置 convertor=templates/convertor.java.vm convertorOutputDir=src/main/java/${convertorPackage}/ ``` 3. 生成代码 配置完成后,我们可以点击“执行”按钮来生成代码。代码生成器将根据配置生成实体类、mapper 接口、xml 文件、dto、vo、convertor 等代码。我们只需要将生成的代码复制到项目中即可。 总结 使用 Mybatis-Plus代码生成器可以大大提高开发效率,而自定义模板则可以生成我们需要的类。通过自定义模板,我们可以生成 dto、vo、convertor 等常用类,避免手写重复的代码,提高开发效率和代码质量。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值