关于mybatis-plus:聊聊mybatisplus的sql加载顺序

序本文次要钻研一下如果mybatis mapper定义了多个同名办法会不会有问题 MybatisConfigurationcom/baomidou/mybatisplus/core/MybatisConfiguration.java /** * MybatisPlus 加载 SQL 程序: * <p> 1、加载 XML中的 SQL </p> * <p> 2、加载 SqlProvider 中的 SQL </p> * <p> 3、XmlSql 与 SqlProvider不能蕴含雷同的 SQL </p> * <p>调整后的 SQL优先级:XmlSql > sqlProvider > CurdSql </p> */ @Override public void addMappedStatement(MappedStatement ms) { if (mappedStatements.containsKey(ms.getId())) { /* * 阐明已加载了xml中的节点; 疏忽mapper中的 SqlProvider 数据 */ logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file"); return; } mappedStatements.put(ms.getId(), ms); }MybatisSqlSessionFactoryBeancom/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java ...

August 27, 2023 · 7 min · jiezi

关于mybatis-plus:聊聊mybatisplus的SafetyEncryptProcessor

序本文次要钻研一下mybatis-plus的SafetyEncryptProcessor SafetyEncryptProcessormybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/SafetyEncryptProcessor.java public class SafetyEncryptProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { /** * 命令行中获取密钥 */ String mpwKey = null; for (PropertySource<?> ps : environment.getPropertySources()) { if (ps instanceof SimpleCommandLinePropertySource) { SimpleCommandLinePropertySource source = (SimpleCommandLinePropertySource) ps; mpwKey = source.getProperty("mpw.key"); break; } } /** * 解决加密内容 */ if (StringUtils.isNotBlank(mpwKey)) { HashMap<String, Object> map = new HashMap<>(); for (PropertySource<?> ps : environment.getPropertySources()) { if (ps instanceof OriginTrackedMapPropertySource) { OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) ps; for (String name : source.getPropertyNames()) { Object value = source.getProperty(name); if (value instanceof String) { String str = (String) value; if (str.startsWith("mpw:")) { map.put(name, AES.decrypt(str.substring(4), mpwKey)); } } } } } // 将解密的数据放入环境变量,并处于第一优先级上 if (CollectionUtils.isNotEmpty(map)) { environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map)); } } }}SafetyEncryptProcessor实现了EnvironmentPostProcessor接口,在postProcessEnvironment办法中先是找到mpw.key,而后遍历所有PropertySource的所有属性,找到mpw:结尾的,而后进行解密并替换到密文,最初放在environment的第一个PropertySourcespring.factoriesmybatis-plus-boot-starter/src/main/resources/META-INF/spring.factories ...

August 26, 2023 · 1 min · jiezi

关于mybatis-plus:Jimmer-VS-MyBatisPlus查询自关联表

首发于Enaium的集体博客 本文是对Jimmer文档中对象抓取器-自关联递归抓取局部的介绍,之后会比照MyBatisPlus的查问自关联表的能力。 对象抓取器是 jimmer-sql 一个十分弱小的特色,具备可媲美 GraphQL 的能力。即便用户不采纳任何 GraphQL 相干的技术栈,也能在 SQL 查问层面失去和 GraphQL 类似的对象图查问能力。筹备数据库和实体类create table tree_node( node_id bigint not null, name varchar(20) not null, parent_id bigint);alter table tree_node add constraint pk_tree_node primary key(node_id);alter table tree_node add constraint uq_tree_node unique(parent_id, name);alter table tree_node add constraint fk_tree_node__parent foreign key(parent_id) references tree_node(node_id);insert into tree_node( node_id, name, parent_id) values (1, 'Home', null), (2, 'Food', 1), (3, 'Drinks', 2), (4, 'Coca Cola', 3), (5, 'Fanta', 3), (6, 'Bread', 2), (7, 'Baguette', 6), (8, 'Ciabatta', 6), (9, 'Clothing', 1), (10, 'Woman', 9), (11, 'Casual wear', 10), (12, 'Dress', 11), (13, 'Miniskirt', 11), (14, 'Jeans', 11), (15, 'Formal wear', 10), (16, 'Suit', 15), (17, 'Shirt', 15), (18, 'Man', 9), (19, 'Casual wear', 18), (20, 'Jacket', 19), (21, 'Jeans', 19), (22, 'Formal wear', 18), (23, 'Suit', 22), (24, 'Shirt', 22);@Entitypublic interface TreeNode { @Id @Column(name = "NODE_ID") long id(); String name(); @Null @ManyToOne TreeNode parent(); @OneToMany(mappedBy = "parent") List<TreeNode> childNodes();}指定查问的深度咱们能够看到,这是一个自关联的表,每个节点都有一个父节点,也能够有多个子节点。 ...

May 30, 2023 · 6 min · jiezi

关于mybatis-plus:dbtemplate-是什么

dbtemplate是什么即不是mybatis,又不是orm,那dbtemplate到底实现了什么性能? 通过配置文件,实现操作数据表的(增|删|改|查)能力。 通过配置文件,可配置服务端口,并启动服务(反对协定: HTTP 和 GRPC ) 可嵌入到设施上,充当拜访设施端数据库的中间件,也能够是小型WEB服务端的抉择(当然要看你业务的复杂程度,基本上数据表的操作都能满足,适宜疾速交付使用)。 前端通过HTTP协定(或GRPC协定),POST一个公开的API接口并传递相应参数,就能够操作数据表。 传递的参数会不会受到SQL注入?配置文件反对配置SQL注入正则表达式,开发人员当时要设计好SQL语句模板哟。 集成场景是HTTP API集成 或 GRPC集成 调用 集成到嵌入式设施,用于设施根本信息配置应用.疾速搭建小型服务端,实用于疾速交付产品场景.如何应用- 服务端运行dbtemplate -fileUrl dbtemplate.xml - 前端调用通过一个API 实现 增|删|改|查 操作 postman调试阐明: Method: POSTURL: http://<ip>:<port>/postCommonMethodHeader: KEY为 Content-Type VALUE为 application/x-www-form-urlencodedBody: KEY为 jsondata VALUE为 { "id":"1", "method":"sel", "cols":"*", "where":" and d_ip like '%192.168.' ", "pageSize":10, "currentPage":0 }参考资料https://github.com/hw2499/dbtemplate

April 22, 2023 · 1 min · jiezi

关于mybatis-plus:MybatisPlus详解一篇带入了解底层原理

一. MP简介 咱们晓得,Mybatis属于一个半自动的ORM框架。之所以说Mybatis是一个半自动的ORM框架,起因是它还须要咱们本人在注解或是映射文件中编写SQL语句,并没有实现齐全的自动化。SQL语句的编写,尽管减少了我的项目和业务需要实现的灵活性,但对一些根本表的操作而言,无疑减少了数据库操作的复杂度,显得没有那么不便。 而Mybatis-Plus则是全自动的ORM框架,依照开闭准则,在不影响Mybatis现有性能的状况下,实现了对Mybatis的性能加强。如果你之前有过Mybatis的应用教训,当初学习Mybatis-Plus几乎就是手到擒来。 明天就带大家来学习一下Mybatis-Plus框架的根本应用,请掏出小本本吧。 二. Mybatis-Plus特点MP具备如下根本特点:通过在实体类中与数据库表进行映射;无需编写SQL语句,简化了CRUD操作;通常实用于单表。 三. MP应用 接下来会在SpringBoot我的项目环境中,带着大家一起应用Mybatis-Plus。 1、新建SpringBoot我的项目创立SpringBoot我的项目的过程,在这里就给大家省略了,如果有不相熟的同学,能够参考之前对于SpringBoot的教程文章 2、增加外围依赖咱们在SpringBoot我的项目中增加须要用到的外围依赖包。 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!-- MySql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version></dependency><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.1.tmp</version></dependency><!-- 连接池 --><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version></dependency><!--简化bean代码的工具包--><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <version>1.18.4</version></dependency><dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version></dependency></dependencies>  所以,实体类中就能够省去CRUD办法的代码实现。package com.qf.pro.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.qf.pro.pojo.User; public interface UserDao extends BaseMapper<User> { //BaseMapper<User> 默认封装了CRUD办法,通过泛型制订了所操作的实体类及表} 编写配置类这样当SpringBoot启动时,就能够扫描到dao层代码所在的包。package com.qf.mp2103.dao;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; @Configuration@MapperScan("com.qf.mp2103.dao")public class MybatisPlusConfig { @Beanpublic PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor();}} 业务实现咱们要在业务逻辑层里调用各种CRUD办法,这也是Mybatis-Plus比拟外围、重要的点,MybatisPlus会依据之前的配置主动生成SQL语句。package com.qf.mp2103.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.qf.mp2103.dao.UsersDao;import com.qf.mp2103.pojo.Users;import com.qf.mp2103.service.UsersService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; ...

April 10, 2023 · 2 min · jiezi

关于mybatis-plus:底层到底做了什么-mybatisplus的一次select调用过程

应用mybatis-plus创立db mapper,只须要写一个接口继承BaseMapper,比方示例中的EntityMapper。 @Mapper@Repositorypublic interface EntityMapper extends BaseMapper<Entity> {} 本文将解释在底层一次select调用是怎么实现。 次要包含以下几个局部: 外围类及包容器初始化Mapper bean初始化代理办法调用外围类及所在包次要波及以下包和类。 <artifactId>mybatis-plus-annotation</artifactId> MybatisSqlSessionFactoryBean ServiceImpl<artifactId>mybatis-spring</artifactId> MapperFactoryBean<artifactId>mybatis-plus-core</artifactId> MybatisConfiguration MybatisMapperRegistry MybatisMapperProxyFactory MybatisMapperProxy MybatisMapperMethod容器初始化spring 递归初始化以下类,包含: MybatisPlusAutoConfigurationMybatisSqlSessionFactoryBeanMybatisConfigurationMybatisMapperRegistry这里不一一阐明各类的初始化过程,只拿出MybatisMapperRegistry。该类用来理论存储mapper的实现 public class MybatisMapperRegistry extends MapperRegistry { private final Configuration config; private final Map<Class<?>, MybatisMapperProxyFactory<?>> knownMappers = new HashMap();}mapper bean的初始化1、spring调用 MapperFactoryBean 初始化申明的mapper接口的bean。 public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { public T getObject() throws Exception { return this.getSqlSession().getMapper(this.mapperInterface); }}该办法会递归调用上文曾经初始化的 MybatisConfiguration、 MybatisMapperRegistry,最终应用 MybatisMapperProxyFactory 生成代理类。 生成代理类,并放入 MybatisMapperRegistry 容器中public class MybatisMapperProxyFactory<T> {protected T newInstance(MybatisMapperProxy<T> mapperProxy) { return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); }}select调用获取注入的代理类bean调用select办法理论调用代理办法 ...

February 22, 2023 · 1 min · jiezi

关于mybatis-plus:关于使用MyBatisPlus后id变一长串雪花算法的踩坑

我建的表是这样的: create table user( id bigint auto_increment comment 'id' primary key, username varchar(256) null comment '用户昵称', userAccount varchar(256) null comment '账号', avatarUrl varchar(1024) null comment '用户头像', gender tinyint null comment '性别', userPassword varchar(512) not null comment '明码', phone varchar(128) null comment '电话', email varchar(512) null comment '邮箱', userStatus int default 0 not null comment '用户转态 0 失常', createTime datetime default CURRENT_TIMESTAMP null comment '创立工夫', updateTime datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新工夫', isDelete tinyint default 0 null invisible comment '是否逻辑删除 默认0') comment '用户';能够看到我的id是bigint 自增,但自测的时候: ...

January 12, 2023 · 1 min · jiezi

关于mybatis-plus:MyBatisPlus之雪花算法

背景须要抉择适合的计划去应答数据规模的增长,以应答逐步增长的拜访压力和数据量。数据库的扩大形式次要包含:业务分库、主从复制,数据库分表。 数据库分表将不同业务数据扩散存储到不同的数据库服务器,可能撑持百万甚至千万用户规模的业务,但如果业务持续倒退,同一业务的单表数据也会达到单台数据库服务器的解决瓶颈。例如,淘宝的几亿用户数据,如果全副寄存在一台数据库服务器的一张表中,必定是无奈满足性能要求的,此时就须要对单表数据进行拆分。单表数据拆分有两种形式:垂直分表和程度分表。示意图如下: 垂直分表垂直分表适宜将表中某些不罕用且占了大量空间的列拆分进来。例如,后面示意图中的 nickname 和 description 字段,假如咱们是一个婚恋网站,用户在筛选其余用户的时候,次要是用 age 和 sex 两个字段进行查问,而 nickname 和 description 两个字段次要用于展现,个别不会在业务查问中用到。description 自身又比拟长,因而咱们能够将这两个字段独立到另外一张表中,这样在查问 age 和 sex 时,就能带来肯定的性能晋升。 程度分表程度分表适宜表行数特地大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字能够作为参考,但并不是相对规范,要害还是要看表的拜访性能。对于一些比较复杂的表,可能超过 1000万就要分表了;而对于一些简略的表,即便存储数据超过 1 亿行,也能够不分表。但不管怎样,当看到表的数据量达到千万级别时,作为架构师就要警惕起来,因为这很可能是架构的性能瓶颈或者隐患。程度分表相比垂直分表,会引入更多的复杂性,例如要求全局惟一的数据id该如何解决。 主键自增①以最常见的用户 ID 为例,能够依照 1000000 的范畴大小进行分段,1 ~ 999999 放到表 1中,1000000 ~ 1999999 放到表2中,以此类推。 ②简单点:分段大小的选取。分段太小会导致切分后子表数量过多,减少保护复杂度;分段太大可能会导致单表仍然存在性能问题,个别倡议分段大小在 100 万至 2000 万之间,具体须要依据业务选取适合的分段大小。 ③长处:能够随着数据的减少平滑地裁减新的表。例如,当初的用户是 100 万,如果减少到 1000 万,只须要减少新的表就能够了,原有的数据不须要动。 ④毛病:散布不平均。如果依照 1000 万来进行分表,有可能某个分段理论存储的数据量只有 1 条,而另外一个分段理论存储的数据量有 1000 万条。 取模①同样以用户 ID 为例,如果咱们一开始就布局了 10 个数据库表,能够简略地用 user_id % 10 的值来示意数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号为 6 的子表中。 ...

September 15, 2022 · 1 min · jiezi

关于mybatis-plus:MyBatusPlus保姆级快速上手教程

为简化开发而生Mybatis简化JDBC操作 MyBatis-Plus(简称 MP)是一个 MyBatis 的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。 1、个性无侵入:只做加强不做扭转,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会主动注入根本 CURD,性能根本无损耗,间接面向对象操作弱小的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过大量配置即可实现单表大部分 CRUD 操作,更有弱小的条件结构器,满足各类应用需要反对 Lambda 模式调用:通过 Lambda 表达式,不便的编写各类查问条件,无需再放心字段写错反对主键主动生成:反对多达 4 种主键策略(内含分布式惟一 ID 生成器 - Sequence),可自在配置,完满解决主键问题反对 ActiveRecord 模式:反对 ActiveRecord 模式调用,实体类只需继承 Model 类即可进行弱小的 CRUD 操作反对自定义全局通用操作:反对全局通用办法注入( Write once, use anywhere )内置代码生成器:采纳代码或者 Maven 插件可疾速生成 Mapper 、 Model 、 Service 、 Controller 层代码,反对模板引擎,更有超多自定义配置等您来应用内置分页插件:基于 MyBatis 物理分页,开发者无需关怀具体操作,配置好插件之后,写分页等同于一般 List 查问分页插件反对多种数据库:反对 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能剖析插件:可输入 Sql 语句以及其执行工夫,倡议开发测试时启用该性能,能疾速揪出慢查问内置全局拦挡插件:提供全表 delete 、 update 操作智能剖析阻断,也可自定义拦挡规定,预防误操作2、反对数据库mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver达梦数据库 、 虚谷数据库 、 人大金仓数据库3、疾速开始1、创立数据库DROP TABLE IF EXISTS user;CREATE TABLE user( id BIGINT(20) 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));INSERT INTO user (id, name, age, email) VALUES(1, 'Jone', 18, 'test1@baomidou.com'),(2, 'Jack', 20, 'test2@baomidou.com'),(3, 'Tom', 28, 'test3@baomidou.com'),(4, 'Sandy', 21, 'test4@baomidou.com'),(5, 'Billie', 24, 'test5@baomidou.com');2、新建Spring Boot我的项目导入依赖 ...

September 4, 2022 · 4 min · jiezi

关于mybatis-plus:mybatisplus使用selectOne方法查询多条记录报错的解决方案

mybatis-plus在应用selectOne办法查问记录查到多条时会抛出异样,但有些业务场景须要只取一条的状况,查出来数组再取第一个的计划显得也不不便,能够用如下aop的办法解决: @Aspect@Componentpublic class MybatisAspectj { // 配置织入点 @Pointcut("execution(public * com.baomidou.mybatisplus.core.mapper.BaseMapper.selectOne(..))") public void selectOneAspect() { } @Before("selectOneAspect()") public void beforeSelect(JoinPoint point) { Object arg = point.getArgs()[0]; if (arg instanceof AbstractWrapper) { arg = (AbstractWrapper) arg; ((AbstractWrapper) arg).last("limit 1"); } }}首先创立本人的mapper继承BaseMapper(惯例操作),之后创立这个aop配置类,对selectOne办法进行解决。以上内容转载自:https://blog.csdn.net/qq_4371...

August 16, 2022 · 1 min · jiezi

关于mybatis-plus:MybatisX工具快速代码生成

MybatisX插件在idea疾速代码生成1.应用idea的Database工具连贯通数据库略 2.操作2.1 抉择MybatisX-Generator选中所有表->右键抉择->MybatisX-Generator 2.2 抉择选项11.module path抉择我的项目模块2.base package写本人的包名3.ignore table prefix:表名有前缀须要疏忽,填写上4.表名和className的对应关系实时会列出来2.3 抉择选项21.抉择Mybatis-Plus32.去掉toString/hashCode/equals3.勾选Lombok4.抉择Mybatis-plus35.看下目录对不?点击实现即可

June 17, 2022 · 1 min · jiezi

关于mybatis-plus:假装是小白之重学MyBatis二

前言本篇咱们来介绍MyBatis插件的开发,这个也是来源于我之前的一个面试经验,面试官为我如何统计Dao层的慢SQL,我过后的答复是借助于Spring的AOP机制,拦挡Dao层所有的办法,但面试官又问,这事实上不齐全是SQL的执行工夫,这其中还有其余代码的工夫,问我还有其余思路吗? 我想了想说没有,面试官接着问,有接触过MyBatis插件的开发吗? 我说没接触过。 但前面也给我过了,我认为这个问题是有价值的问题,所以也放在了我的学习打算中。 看本篇之前倡议先看: 《代理模式-AOP绪论》《伪装是小白之重学MyBatis(一)》如果有人问下面两篇文章在哪里能够找的到,能够去掘金或者思否翻翻,目前公众号还没有,预计年中会将三个平台的文章对立一下。 概述翻阅官网文档的话,MyBatis并没有给处插件的具体定义,但基本上还是拦截器,MyBatis的插件就是一些可能拦挡某些MyBats外围组件办法,加强性能的拦截器。官网文档中列出了四种可供加强的切入点: Executor执行SQL的外围组件。拦挡Executor 意味着要烦扰或加强底层执行的CRUD操作ParameterHandler拦挡该ParameterHandler,意味着要烦扰SQL参数注入、读取的动作。ResultSetHandler拦挡该ParameterHandler, 要烦扰/加强封装后果集的动作StatementHandler拦挡StatementHandler ,则意味着要烦扰/加强Statement的创立和执行的动作当然还是从HelloWorld开始要做MyBatis的插件,首先要实现MyBatis的Interceptor 接口 , 留神类不要导错了,Interceptor很热门,该类位于org.apache.ibatis.plugin.Interceptor下。实现该接口,MyBatis会将该实现类当作MyBatis的拦截器,那拦挡哪些办法,该怎么指定呢? 通过@Intercepts注解来实现,上面是应用示例: @Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))public class MyBatisPluginDemo implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("into invocation .........."); System.out.println(invocation.getTarget()); System.out.println(invocation.getMethod().getName()); System.out.println(Arrays.toString(invocation.getArgs())); return invocation.proceed(); }}@Intercepts能够填多个@Signature,@Signature是办法签名,type用于定位类,method定位办法名,args用于指定办法的参数类型。三者加在一起就能够定位到具体的办法。留神写完还须要将此插件注册到MyBatis的配置文件中,让MyBatis加载该插件。留神这个标签肯定要放在environments下面,MyBatis严格限制住了标签的程序。 <plugins> <plugin interceptor="org.example.mybatis.MyBatisPluginDemo"></plugin></plugins>咱们来看下执行后果: 性能剖析插件走起那拦挡谁呢? 目前也只有Executor 和StatementHandler 供咱们抉择,咱们自身是要看SQL耗时,Executor 离SQL执行还有些远,一层套一层才走到SQL执行,MyBatis中标签的执行过程在《MyBatis源码学习笔记(一) 初遇篇》曾经讲述过了,这里不再赘述,目前来看StatementHandler 是离SQL最近的, 它的实现类就间接走到JDBC了,所以咱们拦挡StatementHandler ,那有的插入插了很多值,咱们要不要拦挡,当然也要拦挡, 咱们的插件办法如下: @Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}), @Signature(type = StatementHandler.class,method = "update" ,args = Statement.class )})public class MyBatisSlowSqlPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("-----开始进入性能剖析插件中----"); long startTime = System.currentTimeMillis(); Object result = invocation.proceed(); long endTime = System.currentTimeMillis(); // query办法入参是statement,所以咱们能够将其转为Statement if (endTime - startTime > 1000){ } return result; }}那对应的SQL该怎么拿? 咱们还是到StatementHandler去看下: ...

May 2, 2022 · 5 min · jiezi

关于mybatis-plus:Mybatisplus-分页插件多条件查询

mybatis-plus官网提供了分页插件,(spring-boot)应用办法如下: //Spring boot形式@Configuration@MapperScan("com.test.*.mapper*")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; } // 最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; }}如何辨别新旧版呢?其实你把这两种代码都复制到你的我的项目里,哪个类存在就用哪个办法。 注入分页插件后,如何应用呢? mapper写法: // 这是mapper本人提供的办法,参数是wrapper 实用于单表查 <P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);// 自定义sql,实用于多表联查IPage<UserVO> queryUserList(Page<UserVO> page, @Param("dto") ConditionDTO conditionDTO);必须要次要的点:1.Page为IPage的实现类,分页返回和传入Page是同一个对象2.Page类必须放在第一位3.第二个参数和后续参数必须加@Param,(根本类型和实体类型都须要加),否则会报找不到属性谬误。 ...

December 16, 2021 · 1 min · jiezi

关于mybatis-plus:MyBatisPlus-SQLServer-分页查询

SpringBoot 演示我的项目: POM<!-- MyBatis-Plus --><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.4</version></dependency><!-- SQLServer 驱动--><dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>sqljdbc4</artifactId> <version>4.0</version></dependency>Config# DataSource Configspring: datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://127.0.0.1:1433;databaseName=my_db username: sa password: 123456# Print SQLmybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImplModel@Data@TableName("user")public class User { private Long id; private String name;}Mapper@Servicepublic interface UserMapper extends BaseMapper<User> {}Test@SpringBootTestpublic class LessonTest { @Autowired private UserMapper userMapper; /** * 测试分页获取用户 */ @Test public void testPageUsers() { // 第 1 页,每页 10 条记录 Page<User> page = new Page<>(1, 10); // 设置查问条件:依照用户名排序 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByAsc("Name"); // 分页获取用户(可在控制台查看生成的 SQL) Page<User> users = userMapper.selectPage(page, wrapper); // 验证用户数应为 10 assert users.size() == 10; }}

December 2, 2021 · 1 min · jiezi

关于mybatis-plus:mybatis批量插入自动生成主键跟日期

在网上查了很多对于mybatis-plus的批量插入,因为大多应用自增主键,不须要主动生成uuid主键网上的大部分代码为<insert id="insertBatch" parameterType="java.util.List"> insert into user (id, name, age, create_date, create_time, create_date_time) values <foreach collection="list" item="item" separator=","> <trim prefix="(" suffix=")" suffixOverrides=","> #{item.id}, #{item.name}, #{item.age}, #{item.createDate}, #{item.createTime},#{item.createDateTime} </trim> </foreach></insert>下面这种形式是必须传入列表时就生成id,但有时候并不想这样做,想间接从前端传入列表就进行批量插入,而user表主键id又不是自增型,此时想到了一个上面这种形式。<insert id="insertBatch" parameterType="java.util.List"> insert into user (id, name, age, create_date) values <foreach collection="list" item="item" separator=","> <trim prefix="(" suffix=")" suffixOverrides=","> (select replace(uuid(), '-', '') as id), #{item.name}, #{item.age}, current_date(),current_time(),current_timestamp() </trim> </foreach></insert>replace(uuid(), '-', '') 将生成的uuid格局中的-替换''成32位的字符串current_date 生成yyyy-MM-dd日期格局current_time 生成HH:mm:ss工夫格局current_timestamp 生成yyyy-MM-dd HH:mm:ss日期工夫格局

September 2, 2021 · 1 min · jiezi

关于mybatis-plus:Mybatisplus分页

增加依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency>在启动类编辑 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); return interceptor; }Mapper层编写IPage<User> getList(Page<Object> page);xml文件编写SQL <select id="getList" resultMap="getUserDate"> select * from user<!-- select * from user 最初不能增加;号--> </select>Test测试 @Test void Test01(){ Page<Object> page = new Page<>(0, 2); List<User> records = userMapper.getList(page).getRecords(); records.forEach(item->System.out.println(item)); }

August 29, 2021 · 1 min · jiezi

关于mybatis-plus:报错-Invalid-bound-statement-not-found-comxxxmapperxxx

Mybaits-puls我的项目启动时报错 Invalid bound statement (not found): com.xxx.mapper.xxx起因:我的项目启动时未找到mapper中的办法 解决办法:在springboot或mybaits-puls的配置文件中加上: mybatis-plus.mapperLocations=classpath:XXX/XXXX/mapper/**/*.xml 地址最终指向你的xml文件即可

August 26, 2021 · 1 min · jiezi

关于mybatis-plus:Mybatis-Plus-自定义sql-多表-带条件-分页-查询

原文链接:Mybatis Plus 自定义sql 多表 带条件 分页 查问

August 16, 2021 · 1 min · jiezi

关于mybatis-plus:学习MyBatisPlus3这一篇就够了

原文链接:学习MyBatis-Plus3这一篇就够了

August 15, 2021 · 1 min · jiezi

关于mybatis-plus:Be-Extra-Careful-about-Pitfalls-of-MyBatisPlus-2x

What an Unreliable Lad You Are @TableFieldMybatis-Plus introduces many powerful annotations for us to indicate the mapping between entity properties and table fields. It's a great coding experience working with those annotations and IService relevant helper class. However, it's actually not true in Mybatis-Plus 2.x. Why? @TableField in 3.x could replace ResultMap fully, even applied in customized Mapper select statement like the below example. package com.john.model;@TableName("user")public class User { @TableId private String id; @TableField("user_name") private String userName;}<mapper> <select id="getUsers" resultType="com.john.model.User"> select * from user </select></mapper>Believe me gentles, 2.x would let you down definitely. @TableField is out of work in above situation. What we have to do is declare the relations by ResultMap, or keep the entity property name as the same as the table field which is case-sensitive. ...

August 13, 2021 · 2 min · jiezi

关于mybatis-plus:mybatis-plus-看这篇就够了一发入魂

mybatis-plus是一款Mybatis加强工具,用于简化开发,提高效率。下文应用缩写mp来简化示意mybatis-plus,本文次要介绍mp搭配SpringBoot的应用。 注:本文应用的mp版本是以后最新的3.4.2,晚期版本的差别请自行查阅文档 官方网站:baomidou.com/ 疾速入门创立一个SpringBoot我的项目导入依赖 <!-- pom.xml --> <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>mybatis-plus</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mybatis-plus</name> <properties> <java.version>1.8</java.version> </properties> <dependencies> <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-configuration-processor</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>配置数据库 # application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/yogurt?serverTimezone=Asia/Shanghai username: root password: root mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启SQL语句打印创立一个实体类 package com.example.mp.po; import lombok.Data; import java.time.LocalDateTime; @Data public class User { private Long id; private String name; private Integer age; private String email; private Long managerId; private LocalDateTime createTime; }创立一个mapper接口 package com.example.mp.mappers; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User; public interface UserMapper extends BaseMapper<User> { }在SpringBoot启动类上配置mapper接口的扫描门路 package com.example.mp; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.mp.mappers") public class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }在数据库中创立表 DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键', name VARCHAR(30) DEFAULT NULL COMMENT '姓名', age INT(11) DEFAULT NULL COMMENT '年龄', email VARCHAR(50) DEFAULT NULL COMMENT '邮箱', manager_id BIGINT(20) DEFAULT NULL COMMENT '直属下级id', create_time DATETIME DEFAULT NULL COMMENT '创立工夫', CONSTRAINT manager_fk FOREIGN KEY(manager_id) REFERENCES user (id) ) ENGINE=INNODB CHARSET=UTF8; INSERT INTO user (id, name, age ,email, manager_id, create_time) VALUES (1, '大BOSS', 40, 'boss@baomidou.com', NULL, '2021-03-22 09:48:00'), (2, '李经理', 40, 'boss@baomidou.com', 1, '2021-01-22 09:48:00'), (3, '黄主管', 40, 'boss@baomidou.com', 2, '2021-01-22 09:48:00'), (4, '吴组长', 40, 'boss@baomidou.com', 2, '2021-02-22 09:48:00'), (5, '小菜', 40, 'boss@baomidou.com', 2, '2021-02-22 09:48:00')编写一个SpringBoot测试类 package com.example.mp; import com.example.mp.mappers.UserMapper; import com.example.mp.po.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest public class SampleTest { @Autowired private UserMapper mapper; @Test public void testSelect() { List<User> list = mapper.selectList(null); assertEquals(5, list.size()); list.forEach(System.out::println); } }筹备工作实现 ...

May 19, 2021 · 14 min · jiezi

关于mybatis-plus:mybatisplus代码生成器完整示例代码

间接代码展现: package cn.qxstar;import cn.qxstar.common.base.entity.BaseEntity;import cn.qxstar.common.utils.web.controller.BaseController;import com.baomidou.mybatisplus.generator.config.rules.DateType;import org.junit.Test;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;public class TestMP { /** * 代码生成 示例代码 */ @Test public void testGenerator() { //1. 全局配置 GlobalConfig config = new GlobalConfig(); config.setOpen(false) .setAuthor("莐星") // 作者 .setOutputDir("D:\\softWare\\IDEA\\workspace\\chstar-blog\\chstar-service\\biz-service\\src\\main\\java") // 生成门路 .setFileOverride(true) // 文件笼罩 .setIdType(IdType.ASSIGN_ID) // 主键策略 数字id .setServiceName("%sService") // 设置生成的service接口的名 .setMapperName("%sMapper") .setServiceImplName("%sServiceImpl") .setControllerName("%sController") .setSwagger2(true) .setBaseResultMap(true) .setBaseColumnList(true) .setXmlName("%sMapper") .setDateType(DateType.TIME_PACK); //2. 数据源配置 DataSourceConfig dsConfig = new DataSourceConfig(); dsConfig.setDbType(DbType.MYSQL) // 设置数据库类型 .setDriverName("com.mysql.cj.jdbc.Driver") .setUrl("jdbc:mysql://localhost:3306/chstar-blog") .setUsername("root") .setPassword("123456"); //3. 策略配置 StrategyConfig stConfig = new StrategyConfig(); stConfig.setCapitalMode(true) //全局大写命名 .setLogicDeleteFieldName("deleted") //NamingStrategy.underline_to_camel下划线转驼峰命名 user_id -> userId .setNaming(NamingStrategy.underline_to_camel) .setEntityTableFieldAnnotationEnable(true)//开启实体注解 .setRestControllerStyle(true)//rest格调controller .setChainModel(true)//链式setter .setSuperControllerClass(BaseController.class)//父类 .setSuperEntityClass(BaseEntity.class)//父类 .setControllerMappingHyphenStyle(true)//? .setTablePrefix("t_");//表名前缀 //4. 包名策略配置 PackageConfig pkConfig = new PackageConfig(); pkConfig.setParent("cn.qxstar.biz_service") .setMapper("mapper") .setService("service") .setServiceImpl("service.impl") .setController("controller") .setEntity("entity") .setXml("mapper.xml"); //5. 整合配置 AutoGenerator ag = new AutoGenerator(); ag.setGlobalConfig(config) .setDataSource(dsConfig) .setStrategy(stConfig) .setPackageInfo(pkConfig); //6. 执行 ag.execute(); }}

May 10, 2021 · 1 min · jiezi

关于mybatis-plus:第四阶段day02

SpringBoot高级用法1.1 对于配置文件的阐明1.1.1 properties配置文件阐明1.语法:key=value 构造2.properties中的属性值默认条件都是String类型 不须要增加引号 1.1.2 YML文件阐明1.语法: key: (空格)valueYml文件有父子级构造,采纳缩进的形式实现server: port: 80801.2 利用配置文件为属性赋值 (升高耦合性)1.2.1 XML形式为属性赋值1).编辑YML配置文件 为redis设置属性和属性值:redis.host: 127.0.0.1redis.port: 63791.2.2 properties形式为属性赋值1).定义properties文件 redis2.host=127.0.0.1redis2.port=63791.2.3 如果XML和Properties重名以谁为准YML形式为准, 注意事项:属性最好不要反复. 1.3 XML配置环境切换#筛选执行环境spring: profiles: actice: test(选用test环境配置) ---环境分割线#定义环境名称spring: profiles: test---环境分割线#定义环境名称spring: profiles: pord1.4 SpringBoot整合Mybatis1.4.1 导入jar包 <!--增加数据库驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--springBoot整合jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--springBoot整合mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency>1.4.2 编辑YML配置文件# Spring整合Mybatis配置mybatis:type-aliases-package: com.jt.pojomapper-locations: classpath:/mybatis/mappers/*.xml#开启驼峰映射 configuration: map-underscore-to-camel-case: true1.4.3 编辑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"><!--namespace 惟一确定Mapper接口的 肯定不能反复--><mapper namespace="com.jt.mapper.UserMapper"><!--定义别名包 type-aliases-package: com.jt.pojo 在程序执行时,如果定义了别名包则会主动的拼接门路 resultType="com.jt.pojo.User" 规定: 1.如果依据别名找不到则依据本人的定义查问类型 --><select id="findAll" resultType="User"> select * from user</select><!-- 开启驼峰映射规定的阐明 阐明:属性user_id 依据驼峰命名规定应该叫userId 例子: 1.user表: user_id user_name user_age 2.User对象: userId, userName userAge 驼峰命名规定 只有开启驼峰映射规定,则Mybatis会主动的实现映射. 原理: user_id(字段) ~~~去除两头的"_"线之后首字母大写 userId 将数据映射给属性userId(属性) 注意事项:一旦开启驼峰映射规定,则必须依照要求来写.--></mapper>MyBatisPuls1.1 ORM思维采纳对象的形式操作数据库 ...

November 25, 2020 · 1 min · jiezi

关于mybatis-plus:SpringBoot高级篇二MybatisPlusMP

MybatisPlus 介绍MyBatis-Plus(简称 MP)是一个 MyBatis 的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。 MP的个性无侵入:只做加强不做扭转,引入它不会对现有工程产生影响,如丝般顺滑损耗小:启动即会主动注入根本 CURD,性能根本无损耗,间接面向对象操作弱小的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过大量配置即可实现单表大部分 CRUD 操作,更有弱小的条件结构器,满足各类应用需要反对 Lambda 模式调用:通过 Lambda 表达式,不便的编写各类查问条件,无需再放心字段写错反对主键主动生成:反对多达 4 种主键策略(内含分布式惟一 ID 生成器 - Sequence),可自在配置,完满解决主键问题反对 ActiveRecord 模式:反对 ActiveRecord 模式调用,实体类只需继承 Model 类即可进行弱小的 CRUD 操作反对自定义全局通用操作:反对全局通用办法注入( Write once, use anywhere )内置代码生成器:采纳代码或者 Maven 插件可疾速生成 Mapper 、 Model 、 Service 、 Controller 层代码,反对模板引擎,更有超多自定义配置等您来应用内置分页插件:基于 MyBatis 物理分页,开发者无需关怀具体操作,配置好插件之后,写分页等同于一般 List 查问分页插件反对多种数据库:反对 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能剖析插件:可输入 Sql 语句以及其执行工夫,倡议开发测试时启用该性能,能疾速揪出慢查问内置全局拦挡插件:提供全表 delete 、 update 操作智能剖析阻断,也可自定义拦挡规定,预防误操作 历史背景问题1: 当初操作的数据库任然采纳 sql语句的形式进行编辑. 操作sql的形式本质就是面对过程的操作形式.需要: 因为传统的sql 开发效率低,并且无论如许简略的sql都须要程序员本人编辑.很繁琐(无趣).想法: 是否以面向对象的形式操作数据库!!! ...

November 21, 2020 · 2 min · jiezi

关于mybatis-plus:MyBatisPlus系列之代码生成器

代码生成器,也叫逆向工程,是依据数据库里的表构造,主动生成对应的实体类、映射文件和接口。 这和hibernate的主动建表恰好相反。 很早之前,写了一个代码生成器的工程,本人也始终在用,很不便,也通过大量验证,也反对很多数据库。 看到很多小伙伴在为数据库生成实体类发愁,现分享给大家,进步开发效率。 mybatis-plus-code-generator 工程介绍工程地址:mybatis-plus-code-generator:https://github.com/fengwenyi/mybatis-plus-code-generator 目录构造: Config类针对每个人的习惯,能够在这个类里进行配置 /* 包名:service / public static final String PACKAGE_NAME_SERVICE = "repository"; /* 包名:service.impl / public static final String PACKAGE_NAME_SERVICE_IMPL = "repository.impl"; /* 包名:model / public static final String PACKAGE_NAME_MODEL = "entity"; /* 包名:dao / public static final String PACKAGE_NAME_DAO = "mapper"; /* 包名:xml / public static final String PACKAGE_NAME_XML = "xml"; /* 文件名后缀:Model / public static final String FILE_NAME_MODEL = "%sEntity"; /* 文件名后缀:Dao / public static final String FILE_NAME_DAO = "I%sMapper"; /* 文件名后缀:Mapper / public static final String FILE_NAME_XML = "%sMapper"; /* MP结尾,Service结尾 / public static final String FILE_NAME_SERVICE = "MP%sRepository"; /* 文件名后缀:ServiceImpl / public static final String FILE_NAME_SERVICE_IMPL = "%sRepositoryImpl"; /* 逻辑删除字段 / public static final String FIELD_LOGIC_DELETE_NAME = "delete_status"; /* 作者 / public static final String AUTHOR = "Erwin Feng"; /* 是否反对Swagger,默认不反对 / public static final Boolean SWAGGER_SUPPORT = false; ...

November 16, 2020 · 2 min · jiezi

关于mybatis-plus:MybatisPlus-page-源码

IPage外部原理也是基于拦截器,然而这个拦挡的是办法以及办法中的参数,这个也会判断是否是查问操作。 如果是查问操作,才会进入分页的解决逻辑。 进入分页逻辑解决后,拦截器会通过反射获取该办法的参数 进行判断是否存在IPage对象的实现类。如果不存在则不进行分页,存在则将该参数赋值给IPage对象。 而后进行拼接sql的解决实现分页操作。 然而应用IPage须要注入一个bean拦截器交给spring进行治理。如下。否则不会进行拦挡 注入paginationInterceptor 分页bean@Beanpublic PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();}拦挡源码public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); // SQL 解析 this.sqlParser(metaObject); // 先判断是不是SELECT操作 (2019-04-10 00:37:31 跳过存储过程) MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType() || StatementType.CALLABLE == mappedStatement.getStatementType()) { return invocation.proceed(); } // 针对定义了rowBounds,做为mapper接口办法的参数 BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); Object paramObj = boundSql.getParameterObject(); // 判断参数里是否有page对象 IPage<?> page = null; if (paramObj instanceof IPage) { page = (IPage<?>) paramObj; } else if (paramObj instanceof Map) { for (Object arg : ((Map<?, ?>) paramObj).values()) { if (arg instanceof IPage) { page = (IPage<?>) arg; break; } } } /* * 不须要分页的场合,如果 size 小于 0 返回后果集 */ if (null == page || page.getSize() < 0) { return invocation.proceed(); } if (this.limit > 0 && this.limit <= page.getSize()) { //解决单页条数限度 handlerLimit(page); } String originalSql = boundSql.getSql(); Connection connection = (Connection) invocation.getArgs()[0]; if (page.isSearchCount() && !page.isHitCount()) { SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), countSqlParser, originalSql); this.queryTotal(sqlInfo.getSql(), mappedStatement, boundSql, page, connection); if (page.getTotal() <= 0) { return null; } } DbType dbType = Optional.ofNullable(this.dbType).orElse(JdbcUtils.getDbType(connection.getMetaData().getURL())); IDialect dialect = Optional.ofNullable(this.dialect).orElse(DialectFactory.getDialect(dbType)); String buildSql = concatOrderBy(originalSql, page); //执行物理分页 DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize()); Configuration configuration = mappedStatement.getConfiguration(); List<ParameterMapping> mappings = new ArrayList<>(boundSql.getParameterMappings()); Map<String, Object> additionalParameters = (Map<String, Object>) metaObject.getValue("delegate.boundSql.additionalParameters"); model.consumers(mappings, configuration, additionalParameters); metaObject.setValue("delegate.boundSql.sql", model.getDialectSql()); metaObject.setValue("delegate.boundSql.parameterMappings", mappings); return invocation.proceed(); }参考博客`https://blog.csdn.net/xiaolegeaizy/article/details/108461284 ...

November 5, 2020 · 2 min · jiezi

关于mybatis-plus:Mybatis-Plus知识点快速抽取

初始化初始化初始化初始化初始化初始化

October 25, 2020 · 1 min · jiezi

关于mybatis-plus:mybatisplus学习整理

一、mybatis-plus简介:Mybatis-Plus(简称MP)是一个 Mybatis 的加强工具,在 Mybatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。二、springboot整合mybatis-plus:pom.xml: <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version></dependency><dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope></dependency>$\color{red}{PS:集成mybatis-plus要把mybatis、mybatis-spring去掉,防止抵触}$mapper public interface UserMapper extends BaseMapper<User> {}test //批量查问测试~~~~@Testpublic void mybatisPlusTest() { List<User> users = userMapper.selectList(null); //Assert.assertEquals(5,users.size()); System.out.println(users);}@Testpublic void mybatisPlusTest() { User user = new User(); user.setName("东方不败"); user.setEmail("dfbb@163.com"); user.setId(1000L); user.setAge(20); System.out.println(userMapper.insert(user));//响应后果1,胜利}

October 14, 2020 · 1 min · jiezi

关于mybatis-plus:MyBatis-进阶MyBatisPlus基于-Springboot-演示

这一篇从一个入门的根本体验介绍,再到对于 CRUD 的一个具体介绍,在介绍过程中将波及到的一些问题,例如逐步策略,主动填充,乐观锁等内容说了一下,只选了一些重要的内容,还有一些没提及到,具体能够参考官网,简略的看完,其实会发现,如果遇到单表的 CRUD ,间接用 MP 必定难受,如果写多表,还是用 Mybatis 多点,毕竟间接写 SQL 会直观一点,MP 给我的感觉,就是办法封装了很多,还有一些算比拟是用的插件,然而可读性会略微差一点,不过集体有集体的认识哇,祝大家国庆高兴 ~ 一 引言最后的 JDBC,咱们须要写大量的代码来实现与根本的 CRUD ,或者会在肯定水平上应用 Spring 的 JdbcTemplate 或者 Apache 的 DBUtils ,这样一些对 JDBC 的简略封装的工具类。 再到后再应用 Mybatis 等一些优良的长久层框架,大大的简化了开发,咱们只须要应用肯定的 XML 或者注解就能够实现原来的工作 JDBC --> Mybatis 无疑简化了开发者的工作,而明天咱们所讲额 MyBatis-Plus 就是在 MyBatis 的根底上,更加的简化开发,来一起看看吧! 二 初识 MyBatis-Plus下列介绍来自官网: (一) 概述MyBatis-Plus(简称 MP)是一个 MyBatis 的加强工具,在 MyBatis 的根底上只做加强不做扭转,为简化开发、提高效率而生。 咱们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的 1P、2P,基友搭配,效率翻倍。 总之一句话:MyBatis-Plus —— 为简化开发而生! (二) 个性无侵入:只做加强不做扭转,引入它 不会 对现有工程产生影响,如丝般顺滑损耗小:启动即会主动注入根本 CRUD,性能根本无损耗,间接面向对象操作弱小的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过大量配置即可实现单表大部分 CRUD 操作,更有弱小的条件结构器,满足各类应用需要反对 Lambda 模式调用:通过 Lambda 表达式,不便的编写各类查问条件,无需再放心字段写错反对主键主动生成:反对多达 4 种主键策略(内含分布式惟一 ID 生成器 - Sequence),可自在配置,完满解决主键问题反对 ActiveRecord 模式:反对 ActiveRecord 模式调用,实体类只需继承 Model 类即可进行弱小的 CRUD 操作反对自定义全局通用操作:反对全局通用办法注入( Write once, use anywhere )内置代码生成器:采纳代码或者 Maven 插件可疾速生成 Mapper 、 Model 、 Service 、 Controller 层代码,反对模板引擎,更有超多自定义配置等您来应用内置分页插件:基于 MyBatis 物理分页,开发者无需关怀具体操作,配置好插件之后,写分页等同于一般 List 查问分页插件反对多种数据库:反对 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库内置性能剖析插件:可输入 Sql 语句以及其执行工夫,倡议开发测试时启用该性能,能疾速揪出慢查问内置全局拦挡插件:提供全表 delete 、 update 操作智能剖析阻断,也可自定义拦挡规定,预防误操作(三) 反对数据库mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserver 、presto 、Gauss 、FirebirdPhoenix 、clickhouse 、Sybase ASE 、 OceanBase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库三 入门初体验依照官网的案例简略试一下 ,注:官网是基于 Springboot 的示例 ...

October 3, 2020 · 12 min · jiezi

关于mybatis-plus:Mybatisplus-之-selectPage分页查询

筹备工作做好间接进行分页查问 筹备工作在后面的文章中有提到过,不分明的能够翻看https://segmentfault.com/a/11... 1.新建配置文件 2.新建对象,封装查问后果 3.分页查问

September 25, 2020 · 1 min · jiezi

关于mybatis-plus:通过对象直接增删该查

@SpringBootTestclass SpringbootDemo2MybatisApplicationTests { @Autowired private UserDao userDao; @Test public void testFind(){ List<User> userList = userDao.findAll(); System.out.println(userList); } @Test public void testSelect01(){ List<User> userList = userDao.selectList(null); System.out.println(userList); } /** * 业务: 查问id=11的用户信息 主键... */ @Test public void testSelect02(){ User user = userDao.selectById(11); System.out.println(user); } /** * 业务: 查问name属性为"小乔"的数据 * sql: select * from user where name="小乔"; * 对象的形式 > sql形式 */ @Test public void testSelect03(){ //条件结构器 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "小乔"); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 业务: 查问name属性为"小乔"的数据 并且 age >=18岁 * sql: select * from user where name="小乔" and age>=18; * * 大于 > gt| 小于 < lt | * 大于等于 >= ge | 小于等于 le */ @Test public void testSelect04(){ //条件结构器 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "小乔") .ge("age", 18); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 业务: 查问name中蕴含 "精"的用户,并且sex为女 * 业务: 查问name中蕴含 以精结尾的数据,并且sex为女 * sql: select * from user where name like "%精%" and sex="女"; */ @Test public void testSelect05(){ //条件结构器 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.likeLeft("name", "精") .eq("sex", "女"); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 需要: 查问user表中的数据 要求依照年龄降序排列,如果年龄雷同依照id降序排列 */ @Test public void testSelect06(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.orderByDesc("age","id"); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 需要: 查问name属性为null的数据. * where name is null */ @Test public void testSelect07(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.isNull("name"); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 查问name="小乔" age=17 性别=女的用户 * 如果传递的是对象.会依据对象中不为null的属性充当where条件. */ @Test public void testSelect08(){ User user = new User(); user.setName("小乔").setAge(17).setSex("女"); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); List<User> userList = userDao.selectList(queryWrapper); System.out.println(userList); } /** * 查问id=1,3,5,10数据. * sql: select * from user where id in (1,3,5,10) */ @Test public void testSelect09(){ //转化时,留神应用对象的类型 Integer[] ids = {1,3,5,10}; List<Integer> idList = Arrays.asList(ids); List<User> userList = userDao.selectBatchIds(idList); System.out.println(userList); } @Test public void testInsert(){ User user = new User(); user.setName("特朗普").setAge(70).setSex("男"); userDao.insert(user); } /** * 删除name=null的数据,或者name="特朗普" */ @Test public void testDelete(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.isNull("name") .or() .eq("name", "特朗普"); userDao.delete(queryWrapper); } /** * 案例1: 将id=1的数据的年龄改为8000岁. * sql1: update user set age=8000 where id=1; * 案例2: 要求将name="黑熊精" age=5500. * sql2: update user set age=5500 where name="黑熊精"; */ @Test public void testUpdate(){ User user = new User(); user.setId(1); //主键充当where条件 user.setAge(8000); userDao.updateById(user); //参数阐明 1.实体对象 封装set条件的值 // 2.更新条件结构器 User temp = new User(); temp.setAge(5500); UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name", "黑熊精"); userDao.update(temp,updateWrapper); }}2.pojo层实现实现实现实现实现@Data@TableName //("user") //1.将对象与表 进行一对一关联public class User implements Serializable { @TableId(type = IdType.AUTO) //主键的信息 设定自增 private Integer Id; //@TableField("name") //如果字段名称与属性的名称统一(蕴含驼峰规定),能够省略不写 private String name; private Integer age; private String sex;}mapper层层层层层层层层层层层层层层层层层层层层层层层层层层层public interface UserDao extends BaseMapper<User> { ...

September 24, 2020 · 2 min · jiezi

关于mybatis-plus:MybatisPlus使用

简介业务场景mybatisplus该框架目标是帮忙咱们主动实现单表的CRUD; 介绍该框架指标是对mybatis框架只做加强不做扭转-->简化开发,提高效率 个性官网中个性如下: 原理原理-->以对象的形式操作数据库 实现: 利用注解实现对象与表绑定 属性与字段绑定.定义一个公共的Mapper接口,定义公共的CRUD操作 利用泛型辨别对象.依照特定的语法,将对象转化为sql语句:如下例:例子: 将user对象插入数据库中. userMapper.insert(user对象); //程序员实现的最初的代码 sql: insert into 表名(字段.....) values (属性值......); 由mp动静拼接之后交由mybatis执行.通过注解绑定,由底层生成表名(字段.....);传入属性值为为对象中的值;再由MP动静拼接之后交由mybatis执行. spring整合MP1.增加jar包依赖 <!--spring整合mybatis-plus 只导入MP包,删除mybatis包--><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version></dependency>增加MP的jar包后要将mybatis的jar包删除,MP中会蕴含mybatis,增加两个可能会抵触. 2.原理具体实现:1)编辑pojo类须要增加注解:@TableName("表名")--实现表与对象关联,如果pojo类名与表名统一(不辨别大小写),能够疏忽注解参数中表名@TableId(type = IdType.AUTO)--在主键属性上增加该注解,示意此属性为主键且主键自增(IdType次要用auto表主键自增/UUID表全局惟一) 2)Mapper接口继承BaseMapper<T>--留神理论利用时,泛型T的引入,写你所用的pojo类即可,不增加的话数据库不能实现操作 3)批改yml配置文件信息,将mybatis改为mybatis-plus即可,别的不须要更改,做到只加强不批改: mybatis-plus: #定义别名包 type-aliases-package: com.jt.pojo #增加xml文件的依赖 mapper-locations: classpath:/mybatis/mappers/*.xml #开启驼峰映射 configuration: map-underscore-to-camel-case: trueMP API应用以下别离是CRUD的常见API应用,具体阐明见官网和代码中注解--https://baomidou.com/ package com.jt.test;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.jt.mapper.UserMapper;import com.jt.pojo.User;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.Arrays;import java.util.List;@SpringBootTestpublic class TestMybatis { @Autowired private UserMapper userMapper; //本地定义的findAll办法以及sql @Test public void test01(){ List<User> users = userMapper.findAll(); System.out.println(users); } //测试MP办法 @Test public void test02(){ //查问user表全副数据 List<User> users = userMapper.selectList(null); System.out.println(users); } /** * 1.测试入库 * 将用户信息 王大锤 20 男 入库 */ @Test public void insert(){ User user=new User(); user.setName("王大锤").setAge(20).setSex("男"); userMapper.insert(user); } /** * 2.查问练习 * 查问ID为21的用户 */ @Test public void select01(){ User user = userMapper.selectById(21); System.out.println(user); //查问记录总数 int count = userMapper.selectCount(null); System.out.println(count); } /** * 需要:查问性别为女,年龄大于100岁 * 条件结构器:动静拼接where条件的 多条件中默认的连接符and * 常见逻辑运算符 * 1.eq = 2.gt > 3.lt < * 4.ge >= 5.le <= */ @Test public void select03(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("sex","女") .gt("age",100); List<User> users = userMapper.selectList(queryWrapper); System.out.println(users); } /** * 需要:1.查问名字中蕴含'精'字的男性 "%精%" * 2.查问以精结尾的 %精 * 3.查问以精结尾的 精% */ @Test public void select04(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.likeLeft("name","精") //queryWrapper.like("name","精") .eq("sex","男"); List<User> users = userMapper.selectList(queryWrapper); System.out.println(users); } /** * 需要:查问sex=女 ,之后依照age倒序排列,如果年龄雷同依照id排列 */ @Test public void select05(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("sex","女") .orderByDesc("age","id"); List<User> users = userMapper.selectList(queryWrapper); System.out.println(users); } /** * 需要:查问id为1,3,5,7,8,9的数据 * 关键字 in or * 在关联查问中慎用 */ @Test public void select06(){ Integer[] ids = {1,3,5,7,8,9}; List<Integer> list = Arrays.asList(ids); List<User> users = userMapper.selectBatchIds(list); System.out.println(users); } /** * 需要:删除id为53的数据 */ @Test public void delete01(){ userMapper.deleteById(53); } /** * 需要:批量删除id为1,2,3,4的数据 */ @Test public void delete02(){ Integer[] ids={1,2,3,4}; List<Integer> list = Arrays.asList(ids); userMapper.deleteBatchIds(list); } /** * 需要:删除age为30的男性 */ @Test public void delete03(){ QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("age","30") .eq("sex","男"); userMapper.delete(queryWrapper); } /** * 需要:将id为6的性别批改为女性 */ @Test public void update01(){ User user=new User(); user.setId(6).setSex("女"); userMapper.updateById(user); } /** * 需要:将name为悟空的角色名字改为卡卡罗特,性别改为男 */ @Test public void update02(){ User user = new User(); user.setName("卡卡罗特").setSex("男"); QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("name","悟空"); userMapper.update(user,queryWrapper); }}

August 27, 2020 · 2 min · jiezi

关于mybatis-plus:从零开始手写-mybatis四-mybatis-事务管理机制详解

前景回顾第一节 从零开始手写 mybatis(一)MVP 版本 中咱们实现了一个最根本的能够运行的 mybatis。 第二节 从零开始手写 mybatis(二)mybatis interceptor 插件机制详解 第三节 从零开始手写 mybatis(三)jdbc pool 从零实现数据库连接池 本节咱们一起来学习一下 mybatis 中的事务管理。 mybatis 中的事务管理mybatis 事务有两种应用形式: 应用JDBC的事务管理机制:即应用 java.Sql.Connection对象实现对事务的提交,回滚和敞开操作。应用MANAGED的事务管理机制:mybatis自身不会去实现事务管理的相干操作,而是交个内部容器来治理事务。当与spring整合应用后,个别应用spring来治理事务。事务工厂 TransactionFactory接口定义这个是对事务的一个工厂,接口如下: public interface TransactionFactory { /** * Sets transaction factory custom properties. * @param props */ void setProperties(Properties props); /** * Creates a {@link Transaction} out of an existing connection. * @param conn Existing database connection * @return Transaction * @since 3.1.0 */ Transaction newTransaction(Connection conn); /** * Creates a {@link Transaction} out of a datasource. * @param dataSource DataSource to take the connection from * @param level Desired isolation level * @param autoCommit Desired autocommit * @return Transaction * @since 3.1.0 */ Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);}次要就是如何依据一个 DataSource 创立一个 Transaction。 ...

July 19, 2020 · 6 min · jiezi

mybatisplus使用MybatisAutoGenerator代码生成器1

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成包括 Entity、Mapper、Mapper XML、Service、Controller 数个模块的代码,可以提升开发效率. 首先,进入 https://start.spring.io 生成一个springboot简单项目下步,数据表SQL CREATE TABLE `soldier` ( `soldier_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '士兵编号', `soldier_name` varchar(30) NOT NULL COMMENT '士兵名字', `join_army_time` timestamp NOT NULL COMMENT '参军时间', PRIMARY KEY (`soldier_id`), KEY `sid` (`soldier_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 依赖以下有些依赖不1定是必须的,但积累甚多,就一并贴出, 版本号是经过多次甄选的. <!-- dependencies --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <!-- 为true,即热部署有效 --> <optional>false</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-- mybatis-plus自动代码生成 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.0.7.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.1</version> </dependency> <!-- 低于1.3.7也许会报错 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--日志记录配置 --> <dependency> <groupId>com.github.vindell</groupId> <artifactId>spring-boot-starter-log4j2-plus</artifactId> <version>1.0.5.RELEASE</version> </dependency> <!--日志记录配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <!-- pagehelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!-- session --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-core</artifactId> </dependency> <!-- 简化实体类(set/get/tostring/hashcode...etc) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- HikariCP数据源,阿里数据源druid会因LocalDateTime报错 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.3.1</version> </dependency> <!-- java for mysql connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!-- tomcat的支持 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- ActiveMQ:Apache所提供的一个开源的消息系统 --> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.15.9</version> </dependency> <!-- Java语法糖帮助包,帮助简化代码,减少方法 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>4.3.1</version> </dependency> <!-- servlet依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> </dependencies>配置mybatis-plus.properties文件#此处为本项目src所在路径(代码生成器输出路径)OutputDir=/home/gzh/eclipse-workspace/Boot-Demo/src/main/java#mapper.xml的生成位置OutputDirXml=/home/gzh/eclipse-workspace/Boot-Demo/src/main/resources#数据库表名(此处切不可为空,如果为空,则默认读取数据库的所有表名)tableName=soldier#存放所生成代码文件的上一级包名#className=自填#设置作者author=gene#正常情况下,下面的代码无需修改#自定义包路径parent=cn.example.demo#数据库地址url=jdbc:mysql://localhost:3306/test00?serverTimezone=CTT&characterEncoding=UTF-8&useSSL=false#mysql:username & passworduserName=plhpassword=1234Java代码注意导入的package,不要导错 ...

November 5, 2019 · 2 min · jiezi

Java代码生成的设计方案与实践

diboot devtools 2.0 已经发布上线,实现了精简内核+后端开发助理。在此把我们目前代码生成部分的设计思路梳理一下,以便需要的朋友少走一些弯路。1. 编程技术的发展趋势最近观看《美国工厂》的感触:在传统制造业,当生产线的工人成本高昂效率低下的时候,企业管理者便会开始探寻降本增效之道,自动化便是首选的解决方案。 软件工程类似于建筑工程有着其自身的复杂度,但即便是像更复杂的汽车制造,不也一样逐步被自动化么。有人说编程将是最后一个被自动化/AI替代的行业,但不代表程序员可以高枕无忧。如果一个程序员只会CRUD,那他可能是第一批被替代的。因为替代是逐步性的,就像软件行业的自动化可能经过代码生成、轻代码、无代码。 2. 代码生成的设计方案2.1 要盖楼先打好地基有的生成器,无任何基础代码直接生成,结果就像一个项目中无任何高级别开发者做封装,直接交给程序员写的一样,随着代码量的增多,可维护性会差的难以想象。 所以,盖楼前先打好地基,封装一个基础的通用内核,提供常用开发场景的最佳实现,比如CRUD通用处理、各分层的轻量封装、常用工具类等。 2.2 生成器的理想方案通用内核职责范围要明确,职责范围内的功能做到最佳实践,避免设计成大而臃肿。通用内核解决CRUD、关联查询等通用场景,实现代码最简化,降低上手门槛和生成器的实现难度。使用简单易上手,最好可以在开发环境启动,直接将代码生成在本地IDE项目中。界面化操作代码生成,随用随生,灵活应对需求(数据结构)变更场景。数据结构驱动代码,实现代码与数据结构的联动同步。支持重复性相似功能的通用生成。支持参数配置实现差异化代码生成。如果受众广,需要基于主流技术及数据库。3. Java代码生成的实践基于Spring Boot 2.x,最主流高效的开发框架基于Mybatis-plus实现CRUD的通用解决方案封装内核实现关联查询的无SQL解决方案及其他常用场景生成器封装成spring boot starter,实现依赖启动提供操作界面实现数据结构与代码同步配置参数实现Lombok、Swagger等开关提取数据库差异,支持MySQL、MariaDB、PostgreSQL、Oracle、SQL Server深入交流请加微信: wx20201024diboot v2 githubdiboot 简单高效的轻代码开发框架 (欢迎star)

November 5, 2019 · 1 min · jiezi

从0手写springCloud项目二-框架代码详解

写在前面前面一篇将springCloud所需的几个组件搭建起来了,接下来以user模块为例子主要记录一下项目中集成的技术,框架,和使用方式。我想从以下几个地方总结: mybatis-plus3lcn5.0.2liquibaseoauth2others(es,rabbitmq,全局异常处理,feign之间异常处理,logback,mysql,redis)集成mybatis-plus3可能有些同学常用的持久层框架是jpa,但是就我实践而言,mybatisplus好用的不是一丁点,个人建议用mybatisplus...现在plus3的版本支持的还是蛮多的:乐观锁,版本号,代码生成器,分页插件,热加载,通用枚举,自动填充,动态数据源....详见官网(https://mp.baomidou.com),而且3.0集成也比2.x简单了不少,最简单的只需要加一个pom坐标即可,但是如果需要个性化配置,我们还是要写config,当然也不麻烦,很简单的。 pom <!--mybatis-plus--><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version></dependency>yml //可以看到我的各个文件存放的路径分别在哪里 mybatis: type-aliases-package: cn.iamcrawler.crawler_common.domain.goddess mapper-locations: cn/iamcrawler/crawlergoddess/mapper/*Mapper.xml type-handlers-package: cn.iamcrawler.crawlergoddess.mapper.typehandler global-config: refresh-mapper: true 那么接下来继承plus的两个方法就可以了 /** * Created by liuliang on 2019/3/21. */public interface DataUserMapper extends BaseMapper<DataUser> {}@Service@Slf4jpublic class DataUserService extends ServiceImpl<DataUserMapper,DataUser>{}ps:大家可以想一想,spring的controller,service,mapper需要使用注解将其标识为bean才能扫描得到,而这里的mapper却没有添加@Repository注解,这是因为我在注解类上加了一个注解,指定spring启动的时候扫描到这个包下,所以这里就可以不用添加@Repository注解,也可以被spring扫描到啦。主类的注解如下: @SpringBootApplication@EnableEurekaClient@EnableFeignClients@EnableTransactionManagerServer@MapperScan("cn.iamcrawler.crawlergoddess.mapper")//上面说的,就是这个注解!public class CrawlerGoddessApplication { public static void main(String[] args) { SpringApplication.run(CrawlerGoddessApplication.class, args); }}是的,就是这么简单。plus最简单的集成就完成了,接下来继承mapper接口,service接口就可以写sql了。具体详见mybatis-plus官网。 ps:最后还有一个小知识点,其实大家可以看到,我的xml是放在和mapper一起的(默认是放在resource下面,打包成jar的时候路径就是我们常说的classpath),而我们知道,在maven打包jar的时候,是不会扫描这个java下的.xml文件的,为什么我打包jar也可以扫描呢?是因为我在maven里面配置了一个将java下的xml也打包的程序,如下: <build> <finalName>crawler-goddess</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <!---我上面说的就是下面这个配置哦--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>集成lcn5.0.2微服务集成分布式事务框架已经不是一个新鲜的事情,在这方面,我前面也写过好几个文章介绍过,其中有springboot1.5.9+lcn4.1.0( https://segmentfault.com/a/11... ), 有springboot2.1.3+lcn5.0.2( https://segmentfault.com/a/11... )。大家可以按需阅读并配置,当然这里我这个demo配置的是5.0.2版本.毕竟这个版本比4.1.0强大了不少,并且配置也更加方便。具体大家也可以参照lcn官网,描述的非常详细(http://www.txlcn.org) ...

June 18, 2019 · 1 min · jiezi

springboot-整合mybatisplus-组成后台开发基本框架

一直想搞一套后台基本开发框架出来,无奈太忙(其实太懒),最近受到两位大佬的启发,就改动了一下大佬做好的东西。初始版本:https://github.com/lihengming... 改进版本:https://github.com/tangzhiman... 我是直接从改进版本开始开发的,出于尊重,应当把初始版本的链接也放上来。 改进版本主要用的是TKmybatis 由于公司一直用的是mybatis-plus,所以我这里的版本是基于mybatis-plus开发的。 放大招!可不许再加班了 具体技术点不再赘述!springboot以及mybatis-plus的资源已经很多。 欢迎喷! 计划在之后把spring-cloud方面的常用技术点也整合进来。 github地址:https://github.com/sjf1256754...

April 25, 2019 · 1 min · jiezi

Mybatis-Plus 真好用(乡村爱情加持)

写在前面MyBatis的增强方案确实有不少,甚至有种感觉是现在如果只用 “裸MyBatis”,不来点增强插件都不好意思了。这不,在上一篇文章《Spring Boot项目利用MyBatis Generator进行数据层代码自动生成》 中尝试了一下 MyBatis Generator。这次来点更加先进的 Mybatis-Plus,SQL语句都不用写了,分页也是自动完成,嗯,真香!数据库准备CREATE TABLE tbl_user( user_id BIGINT(20) NOT NULL COMMENT ‘主键ID’, user_name VARCHAR(30) NULL DEFAULT NULL COMMENT ‘姓名’, user_age INT(11) NULL DEFAULT NULL COMMENT ‘年龄’, PRIMARY KEY (user_id)) charset = utf8;MyBatis-Plus加持工程搭建 (不赘述了)依赖引入<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.12</version></dependency>主要是 Mybatis Plus、Lombok(不知道Lombok干嘛的?可以看这里)、Druid连接池 等依赖。MyBatis Plus配置项目配置mybatis-plus: mapper-locations: classpath:/mapper/*Mapper.xml新增 MyBatis Plus配置类@Configuration@MapperScan(“cn.codesheep.springbtmybatisplus.mapper”)public class MyBatisConfig {}看到没,几乎零配置啊,下面就可以写业务逻辑了业务编写实体类@Data@TableName(“tbl_user”)public class User { @TableId(value = “user_id”) private Long userId; private String userName; private Integer userAge;}Mapper类public interface UserMapper extends BaseMapper<User> {}这里啥接口方法也不用写,就可以实现增删改查了!Service类Service接口:public interface UserService extends IService<User> { int insertUser( User user ); int updateUser( User user ); int deleteUser( User user ); User findUserByName( String userName ); IPage getUserPage( Page page, User user );}Service实现:@Service@AllArgsConstructorpublic class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 增 @Override public int insertUser(User user) { return baseMapper.insert( user ); } // 改 @Override public int updateUser(User user) { return baseMapper.updateById( user ); } // 删 @Override public int deleteUser(User user) { return baseMapper.deleteById( user.getUserId() ); } // 查 @Override public User findUserByName( String userName ) { return baseMapper.getUserByName( userName ); }}Controller类@RestController@RequestMapping("/user")public class UserContorller { @Autowired private UserService userService; // 增 @PostMapping( value = “/insert”) public Object insert( @RequestBody User user ) { return userService.insertUser( user ); } // 改 @PostMapping( value = “/update”) public Object update( @RequestBody User user ) { return userService.updateUser( user ); } // 删 @PostMapping( value = “/delete”) public Object delete( @RequestBody User user ) { return userService.deleteUser( user ); } // 查 @GetMapping( value = “/getUserByName”) public Object getUserByName( @RequestParam String userName ) { return userService.findUserByName( userName ); }}通过以上几个简单的步骤,我们就实现了 tbl_user表的增删改查,传统 MyBatis的 XML文件一个都不需要写!实际实验【《乡爱》加持】启动项目很牛批的 logo就会出现接下来通过 Postman来发送增删改查的请求插入记录通过 Postman随便插入几条记录 POST localhost:8089/user/insert{“userId”:3,“userName”:“刘能”,“userAge”:“58”}{“userId”:4,“userName”:“赵四”,“userAge”:“58”}{“userId”:5,“userName”:“谢广坤”,“userAge”:“58”}{“userId”:6,“userName”:“刘大脑袋”,“userAge”:“58”}修改记录修改记录时需要带用户ID,比如我们修改 赵四 那条记录的名字为 赵四(Zhao Four)删除记录修改记录时同样需要带用户ID,比如删除ID=6 那条 刘大脑袋的记录查询记录(普通查询,下文讲分页查询)比如,按照名字来查询:GET localhost:8089/user/getUserByName?userName=刘能最关心的分页问题首先装配分页插件@Beanpublic PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor();}Mapper类public interface UserMapper extends BaseMapper<User> { // 普通查询 User getUserByName( String userName ); // 分页查询 IPage<List<User>> getUsersPage( Page page, @Param(“query”) User user );}Service类@Service@AllArgsConstructorpublic class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 查:普通查 @Override public User findUserByName( String userName ) { return baseMapper.getUserByName( userName ); } // 分页查 @Override public IPage getUserPage(Page page, User user) { return baseMapper.getUsersPage( page, user ); }}Controller类@GetMapping( value = “/page”)public Object getUserPage( Page page, User user ) { return userService.getUserPage( page, user );}实际实验一下,我们分页查询 年龄 = 58 的多条记录:可以看到结果数据中,除了给到当前页数据,还把总记录条数,总页数等一并返回了,很是优雅呢 !写在最后由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!My Personal Blog:CodeSheep 程序羊 ...

April 12, 2019 · 2 min · jiezi

mybatis-plus源码分析之sql注入器

mybatis-plus是完全基于mybatis开发的一个增强工具,它的设计理念是在mybatis的基础上只做增强不做改变,为简化开发、提高效率而生,它在mybatis的基础上增加了很多实用性的功能,比如增加了乐观锁插件、字段自动填充功能、分页插件、条件构造器、sql注入器等等,这些在开发过程中都是非常实用的功能,mybatis-plus可谓是站在巨人的肩膀上进行了一系列的创新,我个人极力推荐。下面我会详细地从源码的角度分析mybatis-plus(下文简写成mp)是如何实现sql自动注入的原理。温故知新我们回顾一下mybatis的Mapper的注册与绑定过程,我之前也写过一篇「Mybatis源码分析之Mapper注册与绑定」,在这篇文章中,我详细地讲解了Mapper绑定的最终目的是将xml或者注解上的sql信息与其对应Mapper类注册到MappedStatement中,既然mybatis-plus的设计理念是在mybatis的基础上只做增强不做改变,那么sql注入器必然也是在将我们预先定义好的sql和预先定义好的Mapper注册到MappedStatement中。现在我将Mapper的注册与绑定过程用时序图再梳理一遍:解析一下这几个类的作用:SqlSessionFactoryBean:继承了FactoryBean和InitializingBean,符合spring loc容器bean的基本规范,可在获取该bean时调用getObject()方法到SqlSessionFactory。XMLMapperBuilder:xml文件解析器,解析Mapper对应的xml文件信息,并将xml文件信息注册到Configuration中。XMLStatementBuilder:xml节点解析器,用于构建select/insert/update/delete节点信息。MapperBuilderAssistant:Mapper构建助手,将Mapper节点信息封装成statement添加到MappedStatement中。MapperRegistry:Mapper注册与绑定类,将Mapper的类信息与MapperProxyFactory绑定。MapperAnnotationBuilder:Mapper注解解析构建器,这也是为什么mybatis可以直接在Mapper方法添加注解信息就可以不用在xml写sql信息的原因,这个构建器专门用于解析Mapper方法注解信息,并将这些信息封装成statement添加到MappedStatement中。从时序图可知,Configuration配置类存储了所有Mapper注册与绑定的信息,然后创建SqlSessionFactory时再将Configuration注入进去,最后经过SqlSessionFactory创建出来的SqlSession会话,就可以根据Configuration信息进行数据库交互,而MapperProxyFactory会为每个Mapper创建一个MapperProxy代理类,MapperProxy包含了Mapper操作SqlSession所有的细节,因此我们就可以直接使用Mapper的方法就可以跟SqlSession进行交互。饶了一圈,发现我现在还没讲sql注入器的源码分析,你不用慌,你得体现出老司机的成熟稳定,之前我也跟你说了sql注入器的原理了,只剩下源码分析,这时候我们应该在源码分析之前做足前戏,前戏做足就剩下撕、拉、扯、剥开源码的外衣了,来不及解释了快上车!源码分析从Mapper的注册与绑定过程的时序图看,要想将sql注入器无缝链接地添加到mybatis里面,那就得从Mapper注册步骤添加,果然,mp很鸡贼地继承了MapperRegistry这个类然后重写了addMapper方法:com.baomidou.mybatisplus.MybatisMapperRegistry#addMapper:public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { // TODO 如果之前注入 直接返回 return; // throw new BindingException(“Type " + type + // " is already known to the MybatisPlusMapperRegistry.”); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<>(type)); // It’s important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won’t try. // TODO 自定义无 XML 注入 MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } }}方法中将MapperAnnotationBuilder替换成了自家的MybatisMapperAnnotationBuilder,在这里特别说明一下,mp为了不更改mybatis原有的逻辑,会用继承或者直接粗暴地将其复制过来,然后在原有的类名上加上前缀“Mybatis”。com.baomidou.mybatisplus.MybatisMapperAnnotationBuilder#parse:public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); parseCache(); parseCacheRef(); Method[] methods = type.getMethods(); // TODO 注入 CURD 动态 SQL (应该在注解之前注入) if (BaseMapper.class.isAssignableFrom(type)) { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); } for (Method method : methods) { try { // issue #237 if (!method.isBridge()) { parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods();}sql注入器就是从这个方法里面添加上去的,首先判断Mapper是否是BaseMapper的超类或者超接口,BaseMapper是mp的基础Mapper,里面定义了很多默认的基础方法,意味着我们一旦使用上mp,通过sql注入器,很多基础的数据库操作都可以直接继承BaseMapper实现了,开发效率爆棚有木有!com.baomidou.mybatisplus.toolkit.GlobalConfigUtils#getSqlInjector:public static ISqlInjector getSqlInjector(Configuration configuration) { // fix #140 GlobalConfiguration globalConfiguration = getGlobalConfig(configuration); ISqlInjector sqlInjector = globalConfiguration.getSqlInjector(); if (sqlInjector == null) { sqlInjector = new AutoSqlInjector(); globalConfiguration.setSqlInjector(sqlInjector); } return sqlInjector;}GlobalConfiguration是mp的全局缓存类,用于存放mp自带的一些功能,很明显,sql注入器就存放在GlobalConfiguration中。这个方法是先从全局缓存类中获取自定义的sql注入器,如果在GlobalConfiguration中没有找到自定义sql注入器,就会设置一个mp默认的sql注入器AutoSqlInjector。sql注入器接口:// SQL 自动注入器接口public interface ISqlInjector { // 根据mapperClass注入SQL void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass); // 检查SQL是否注入(已经注入过不再注入) void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass); // 注入SqlRunner相关 void injectSqlRunner(Configuration configuration);}所有自定义的sql注入器都需要实现ISqlInjector接口,mp已经为我们默认实现了一些基础的注入器:com.baomidou.mybatisplus.mapper.AutoSqlInjectorcom.baomidou.mybatisplus.mapper.LogicSqlInjector其中AutoSqlInjector提供了最基本的sql注入,以及一些通用的sql注入与拼装的逻辑,LogicSqlInjector在AutoSqlInjector的基础上复写了删除逻辑,因为我们的数据库的数据删除实质上是软删除,并不是真正的删除。com.baomidou.mybatisplus.mapper.AutoSqlInjector#inspectInject:public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { String className = mapperClass.toString(); Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); if (!mapperRegistryCache.contains(className)) { inject(builderAssistant, mapperClass); mapperRegistryCache.add(className); }}该方法是sql注入器的入口,在入口处添加了注入过后不再注入的判断功能。// 注入单点 crudSql@Overridepublic void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) { this.configuration = builderAssistant.getConfiguration(); this.builderAssistant = builderAssistant; this.languageDriver = configuration.getDefaultScriptingLanguageInstance(); // 驼峰设置 PLUS 配置 > 原始配置 GlobalConfiguration globalCache = this.getGlobalConfig(); if (!globalCache.isDbColumnUnderline()) { globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase()); } Class<?> modelClass = extractModelClass(mapperClass); if (null != modelClass) { // 初始化 SQL 解析 if (globalCache.isSqlParserCache()) { PluginUtils.initSqlParserInfoCache(mapperClass); } TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass); injectSql(builderAssistant, mapperClass, modelClass, table); }}注入之前先将Mapper类提取泛型模型,因为继承BaseMapper需要将Mapper对应的model添加到泛型里面,这时候我们需要将其提取出来,提取出来后还需要将其初始化成一个TableInfo对象,TableInfo存储了数据库对应的model所有的信息,包括表主键ID类型、表名称、表字段信息列表等等信息,这些信息通过反射获取。com.baomidou.mybatisplus.mapper.AutoSqlInjector#injectSql:protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo table) { if (StringUtils.isNotEmpty(table.getKeyProperty())) { /** 删除 / this.injectDeleteByIdSql(false, mapperClass, modelClass, table); /* 修改 / this.injectUpdateByIdSql(true, mapperClass, modelClass, table); /* 查询 / this.injectSelectByIdSql(false, mapperClass, modelClass, table); } /* 自定义方法 / this.inject(configuration, builderAssistant, mapperClass, modelClass, table);}所有需要注入的sql都是通过该方法进行调用,AutoSqlInjector还提供了一个inject方法,自定义sql注入器时,继承AutoSqlInjector,实现该方法就行了。com.baomidou.mybatisplus.mapper.AutoSqlInjector#injectDeleteByIdSql:protected void injectSelectByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) { SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID; SqlSource sqlSource; if (batch) { sqlMethod = SqlMethod.SELECT_BATCH_BY_IDS; StringBuilder ids = new StringBuilder(); ids.append("\n<foreach item="item" index="index" collection="coll" separator=",">"); ids.append("#{item}"); ids.append("\n</foreach>"); sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(), table.getKeyColumn(), ids.toString()), modelClass); } else { sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(), table.getKeyColumn(), table.getKeyProperty()), Object.class); } this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);}我随机选择一个删除sql的注入,其它sql注入都是类似这么写,SqlMethod是一个枚举类,里面存储了所有自动注入的sql与方法名,如果是批量操作,SqlMethod的定义的sql语句在添加批量操作的语句。再根据table和sql信息创建一个SqlSource对象。com.baomidou.mybatisplus.mapper.AutoSqlInjector#addMappedStatement:public MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource, SqlCommandType sqlCommandType, Class<?> parameterClass, String resultMap, Class<?> resultType, KeyGenerator keyGenerator, String keyProperty, String keyColumn) { // MappedStatement是否存在 String statementName = mapperClass.getName() + “.” + id; if (hasMappedStatement(statementName)) { System.err.println("{" + statementName + “} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.”); return null; } /* 缓存逻辑处理 */ boolean isSelect = false; if (sqlCommandType == SqlCommandType.SELECT) { isSelect = true; } return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null, parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn, configuration.getDatabaseId(), languageDriver, null);}sql注入器的最终操作,这里会判断MappedStatement是否存在,这个判断是有原因的,它会防止重复注入,如果你的Mapper方法已经在Mybatis的逻辑里面注册了,mp不会再次注入。最后调用MapperBuilderAssistant助手类的addMappedStatement方法执行注册操作。到这里,一个sql自动注入器的源码就分析完了,其实实现起来很简单,因为它利用了Mybatis的机制,站在巨人的肩膀上进行创新。我希望在你们今后的职业生涯里,不要只做一个只会调用API的crud程序员,我们要有一种刨根问底的精神。阅读源码很枯燥,但阅读源码不仅会让你知道API底层的实现原理,让你知其然也知其所以然,还可以开阔你的思维,提升你的架构设计能力,通过阅读源码,可以看到大佬们是如何设计一个框架的,为什么会这么设计。 ...

March 18, 2019 · 3 min · jiezi

mybatis-plus 的一种很别扭的用法

熟悉 mybatis-plus 的人都知道,mybatis-plus 提供两种包含预定义增删改查操作的接口:com.baomidou.mybatisplus.core.mapper.BaseMappercom.baomidou.mybatisplus.extension.service.IService对比这两个接口,操作都差不多,名字有一点点改变,比如 BaseMapper 里面叫 insert() 的方法,在 IService 里面叫 save()。其实我也不是很清楚为什么要单独设计 IService 接口,但是两者确实有区别,就是 IService 提供批处理操作,BaseMapper 没有。另外,IService 的默认实现 com.baomidou.mybatisplus.extension.service.impl.ServiceImpl 就是调用 BaseMapper 来操作数据库,所以我猜 IService 是 Java 8 之前对 BaseMapper 所做的扩展,而 Java 8 之后,因为有了 default 方法,ServiceImpl 里面的东西其实都可以移到 BaseMapper 里面了。除此之外还有就是 IService 依赖于 Spring 容器,而 BaseMapper 不依赖;BaseMapper 可以继承并添加新的数据库操作,IService 要扩展的话还是得调用 Mapper,显得有些多此一举。所以,如果你既要使用批处理操作,又要添加自己的数据库操作,那就必须两个接口一起用。比如在下面一个示例项目中,就同时存在两者:// StudentService.java@Servicepublic class StudentService extends ServiceImpl<StudentMapper, Student> {}// StudentMapper.java@Componentpublic interface StudentMapper extends BaseMapper<Student> { @Select(“select * from STUDENT where FIRST_NAME=#{firstName}”) List<Student> selectByFirstName(@Param(“firstName”) String firstName);}这样每个实体都要创建两个文件,很麻烦。可不可以简化呢?可以,就像下面这样:// StudentService.java@Servicepublic class StudentService extends ServiceImpl<StudentMapper, Student> { public interface StudentMapper extends BaseMapper<Student> { @Select(“select * from STUDENT where FIRST_NAME=#{firstName}”) List<Student> selectByFirstName(@Param(“firstName”) String firstName); }}对,你没看错,就把 Mapper 直接写在 Service 里面就好。有人就会问了,这个 Mapper 能用吗?告诉你,能:@AutowiredStudentService.StudentMapper studentMapper;像上面这样引用过来,照常使用即可。另外还有一种方式就是通过 Service 把 Mapper 暴露出来:public class StudentService extends ServiceImpl<StudentMapper, Student> { public StudentMapper getMapper() { return this.baseMapper; } …这个 baseMapper 也是 StudentMapper 的实例。这样的话,使用的时候就只需要引用 StudentService 一个对象了:List list = studentService.getMapper().selectByFirstName(“First”); ...

March 14, 2019 · 1 min · jiezi