当前位置: 首页 > news >正文

比 MyBatis 快 100 倍,天生支持联表

开源项目简介

d7d4291c1b004ad7883cda866f5ce53c.png
比 [MyBatis效率快 100 倍的条件检索引擎,天生支持联表,使一行代码实现复杂列表检索成为可能! 

开源协议

使用[Apache])-2.0开源协议

界面展示

314cd2ab92ba4c70a686ad558df464df.png
你的产品给你画了以上一张图,还附带了一些要求: 

检索结果分页展示
可以按任意字段排序
按检索条件统计某些字段值
这时候,后台接口该怎么写???使用 Mybatis 或 Hibernate 写 100 行代码是不是还打不住?而使用 Bean Searcher,只需 一行代码 便可实现上述要求!!!

功能概述

特性

[支持 实体多表映射]
[支持 动态字段运算符]
[支持 分组聚合 查询]
[支持 Select | Where | From 子查询]
[支持 实体类嵌入参数]
[支持 字段转换器]
[支持 Sql 拦截器]
[支持 数据库 Dialect 扩展]
[支持 多数据源 与 动态数据源]
[支持 注解缺省 与 自定义]
[支持 字段运算符 扩展]
[等等]
快速开发

使用 Bean Searcher 可以极大节省后端的复杂列表检索接口的开发时间

集成简单

可以和任意 Java Web 框架集成,如:SpringBoot、Grails、Jfinal 等

扩展性强

面向接口设计,用户可自定义扩展 Bean Searcher 中的任何组件

支持 注解缺省

约定优于配置,可省略注解,可复用原有域类,同时支持自定义注解

支持 多数据源

分库分表?在这里特别简单,告别分库分表带来的代码熵值增高问题

支持 Select 指定字段

同一个实体类,可指定只 Select 其中的某些字段,或排除某些字段

支持 参数过滤器

支持添加多个参数过滤器,可自定义参数过滤规则

支持 字段转换器

支持添加多个字段转换器,可自定义数据库字段到实体类字段的转换规则

支持 SQL 拦截器

支持添加多个 SQL 拦截器,可自定义 SQL 生成规则

技术选型

框架目的:只一行代码实现:多表联查分页搜索任意字段组合过滤任意字段排序多字段统计
[架构]

723d057978a84d00a6bcd81a6ef903e2.png 

为什么用

这绝不是一个重复的轮子

虽然 增删改 是 hibernate 和 mybatis、data-jdbc 等等 ORM 的强项,但查询,特别是有 多条件、联表、分页、排序 的复杂的列表查询,却一直是它们的弱项。

传统的 ORM 很难用较少的代码实现一个复杂的列表检索,但 Bean Searcher 却在这方面下足了功夫,这些复杂的查询,几乎只用一行代码便可以解决。

例如,这样的一个典型的需求:

74f2d4b3416f46a39df2117f168d4d1c.png 

后端需要写一个检索接口,而如果用传统的 ORM 来写,代码之复杂是可以想象的。

而 Bean Searcher 却可以:

只一行代码实现以上功能

首先,你有一个实体类:

@SearchBean(tables="user u, role r", joinCond="u.role_id = r.id", autoMapTo="u") public class User {  private long id;  private String username;  private int status;  private int age;  private String gender;  private Date joinDate;  private int roleId;  @DbField("r.name")  private String roleName;  // Getters and setters...}
然后你就可以用一行代码实现这个用户检索接口:

@RestController@RequestMapping("/user")public class UserController {     @Autowired    private BeanSearcher beanSearcher; // 注入 BeanSearcher 的检索器     @GetMapping("/index")    public SearchResult<User> index(HttpServletRequest request) {        // 这里只写一行代码        return beanSearcher.search(User.class, MapUtils.flat(request.getParameterMap()), new String[]{ "age" });    }  }
这一行代码实现了以下功能:

多表联查
分页搜索
组合过滤
任意字段排序
字段统计
例如,该接口支持如下请求:

[GET: /user/index]
[无参请求(默认分页):]
[{ "dataList": [ { "id": 1, "username": "Jack", "status": 1, "level": 1, "age": 25, "gender": "Male", "joinDate": "2021-10-01" }, ... // 默认返回 15 条数据 ], "totalCount": 100, "summaries": [ 2500 // age 字段统计 ] }]
[GET: /user/index? page=1 & size=10]
[指定分页参数]
[GET: /user/index? status=1]
[返回 status = 1 的用户]
[GET: /user/index? name=Jac & name-op=sw]
[返回 name 已 Jac 开头的用户]
[GET: /user/index? name=Jack & name-ic=true]
[返回 name = Jack(忽略大小写)的用户]
[GET: /user/index? sort=age & order=desc]
[按字段 age 降序查询]
[GET: /user/index? onlySelect=username,age]
[只检索 username 与 age 两个字段:]
[{ "dataList": [ { "username": "Jack", "age": 25 }, ... ], "totalCount": 100, "summaries": [ 2500 ] }]
[GET: /user/index? selectExclude=joinDate]
[检索时排除 joinDate 字段]
参数构建器

Map<String, Object> params = MapUtils.builder()        .selectExclude(User::getJoinDate) // 排除 joinDate 字段        .field(User::getStatus, 1) // 过滤:status = 1        .field(User::getName, "Jack").ic() // 过滤:name = 'Jack' (case ignored)        .field(User::getAge, 20, 30).op(Opetator.Between) // 过滤:age between 20 and 30        .orderBy(User::getAge, "asc") // 排序:年龄,从小到大        .page(0, 15) // 分页:第 0 页, 每页 15 条        .build();List<User> users = beanSearcher.searchList(User.class, params);
快速开发

使用 Bean Searcher 可以极大地节省后端的复杂列表检索接口的开发时间!

普通的复杂列表查询只需一行代码
单表检索可复用原有 Domain,无需定义 SearchBean
集成简单

可以和任意 Java Web 框架集成,如:SpringBoot、Spring MVC、Grails、Jfinal 等等。

Spring Boot 项目,添加依赖即集成完毕:

implementation 'com.ejlchina:bean-searcher-boot-stater:3.6.0'
接着便可在 Controller 或 Service 里注入检索器:

/** * 注入 Map 检索器,它检索出来的数据以 Map 对象呈现 */@Autowiredprivate MapSearcher mapSearcher; /** * 注入 Bean 检索器,它检索出来的数据以 泛型 对象呈现 */@Autowiredprivate BeanSearcher beanSearcher;
其它框架,使用如下依赖:

implementation 'com.ejlchina:bean-searcher:3.6.0'
然后可以使用 SearcherBuilder 构建一个检索器:

DataSource dataSource = ... // 拿到应用的数据源 // DefaultSqlExecutor 也支持多数据源SqlExecutor sqlExecutor = new DefaultSqlExecutor(dataSource); // 构建 Map 检索器MapSearcher mapSearcher = SearcherBuilder.mapSearcher()        .sqlExecutor(sqlExecutor)        .build(); // 构建 Bean 检索器BeanSearcher beanSearcher = SearcherBuilder.beanSearcher()        .sqlExecutor(sqlExecutor)        .build();
扩展性强

面向接口设计,用户可自定义扩展 Bean Searcher 中的任何组件!

比如你可以:

自定义 FieldOp 来支持更多的字段运算符
自定义 FieldConvertor 来支持任意的 特殊字段类型
自定义 DbMapping 来实现自定义注解,或者让 Bean Searcher 识别其它 ORM 的注解
自定义 ParamResolver 来支持其它形式的检索参数
自定义 Dialect 来支持更多的数据库
等等.

一文带你入门 MyBatis

MyBatis 是什么?

MyBatis是第一个支持自定义SQL、存储过程和高级映射的类持久框架。MyBatis消除了大部分JDBC的样板代码、手动设置参数以及检索结果。MyBatis能够支持简单的XML和注解配置规则。使Map接口和POJO类映射到数据库字段和记录。

下面我们通过一个简单的项目搭建来带你认识一下MyBatis的使用和一些核心组件的讲解。

MyBatis 项目构建

为了快速构建一个MyBatis项目,我们采用SpringBoot快速搭建的方式。搭建好后在对应的pom.xml下添加如下的maven依赖,主要作用在于引入mybatis一些jar包和类库

主要分为四个步骤:

快速构建项目,引入核心maven dependency依赖
构建POJO类和接口式编程的 Mapper类,编写SQL语句
编写config.properties数据库驱动等配置
构建Mybatis核心配置文件即mybatis-config.xml,引入数据库驱动,映射Mapper类
编写Junit单元测试类
<!-- mybatis 核心依赖-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.4.6</version>
</dependency>
<!-- 数据库驱动包 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.25</version>
</dependency>
<!-- 单元测试包-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
为了便于更好的说明文章的主旨,这里就不贴出全部代码了,会贴出核心代码部分

编写对应的POJO类和接口式编程Mapper类,这里我们以部门业务逻辑为例,构建一个部门类,有三个属性即部门编号、部门名称、位置,下面是部分代码:

Dept.java

package com.mybatis.beans;
public class Dept {
  
    private Integer deptNo;
    private String  dname;
    private String  loc;
  
    public Dept() {}
    public Dept(Integer deptNo, String dname, String loc) {
        this.deptNo = deptNo;
        this.dname = dname;
        this.loc = loc;
    }
        get and set...
}
MyBatis最核心的功能之一就是接口式编程,它可以让我们编写Mapper接口和XML文件,从而把参数和返回结果映射到对应的字段中。

DeptDao.java

package com.mybatis.dao;
public interface DeptDao {

      // 通过部门名称查询
    public Dept findByDname(String Dname);
      // 通过部门编号查询
    public Dept findByDeptNo(Integer deptno);
}
在/resources 下新建com.mybatis.dao 包,在其内编写对应的XML配置文件,此XML配置文件和Mapper互为映射关系。

<mapper namespace="com.mybatis.dao.DeptDao" >

    <sql id="DeptFindSql">
     select * from dept
      </sql>
  
    <select id="findByDeptNo" resultType="com.mybatis.beans.Dept">
        <include refid="DeptFindSql"></include>
        where deptno = #{deptNo}
    </select>

    <select id="findByDname" resultType="com.mybatis.beans.Dept">
        <include refid="DeptFindSql"></include>
        where dname = #{dname}
    </select>

</mapper>
上述的 就是映射到Mapper接口类的命名空间
<select>标签用于编写查询语句,查询完成之后需要把结果映射到对象或者map集合等,需要用到resultType属性指定对应的结果集。
上述采用了和的标签写法,为了方便的映射到实体类,需要修改的话统一修改即可,降低耦合性。
构建完成基础的SQL语句和映射之后,下面来构建MySQL数据库驱动,在/resources 下创建config.properties类

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=123456
在/resources 下编写MyBatis核心配置文件myBatis-config.xml,引入数据库驱动,映射Mapper类

<configuration>
    <!-- 设置导入外部properties文件位置 -->
    <properties resource="config.properties"></properties>
  
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.mybatis.dao"/>
    </mappers>
</configuration>
configuration 标签很像是Spring 中的 beans 标签或者是基于注解的配置@Configuration,也就是MyBatis的核心配置环境,使用 properties 标签引入外部属性环境,也就是数据库驱动配置,使用 mappers 映射到Mapper所在的包,这里指的就是DeptDao.java所在的包。

在test包下面新建一个Junit单元测试类,主要流程如下:

39ccb38d6fc5493f9672968bec008a8e.png
MyBatisTest.java 代码如下: 

public class MyBatisTest {

    private SqlSession sqlSession;


    /**
     * 读取配置文件,创建SQL工厂,打开会话
     * @throws Exception
     */
    @Before
    public void start() throws Exception{
        InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        sqlSession = factory.openSession();
    }

    /**
     * 销毁会话
     */
    @After
    public void destroy() {
        if(sqlSession != null){
            sqlSession.close();
        }
    }

    @Test
    public void test(){
        DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
        Dept dept = deptDao.findByDeptNo(1);
        System.out.println(dept.getDname());
    }
}
@Before 和 @After 是junit工具包中的类,@Before在执行@Test 测试其主要业务之前加载,@After 在执行@Test 测试完成之后加载。

整体结构如下:

4c9029e2f0184423815588b071867bd9.png
MyBatis 整体架构 

MyBatis的架构大概是这样的,最上面是接口层,接口层就是开发人员在Mapper或者是Dao接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置Mapper -> xml层级之间的参数映射,SQL解析,SQL执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理。

4fd30345e5ea41dd9aff9c34d5217c69.png
接口层 

在不与Spring 集成的情况下,使用MyBatis执行数据库的操作主要如下:

InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
sqlSession = factory.openSession();
其中的SqlSessionFactory,SqlSession是MyBatis接口的核心类,尤其是SqlSession,这个接口是MyBatis中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。

数据处理层

配置解析
在Mybatis初始化过程中,会加载mybatis-config.xml配置文件、映射配置文件以及Mapper接口中的注解信息,解析后的配置信息会形成相应的对象并保存到Configration对象中。之后,根据该对象创建SqlSessionFactory对象。待Mybatis初始化完成后,可以通过SqlSessionFactory创建SqlSession对象并开始数据库操作。

SQL解析与scripting模块
Mybatis实现的动态SQL语句,几乎可以编写出所有满足需要的SQL。

Mybatis中scripting模块会根据用户传入的参数,解析映射文件中定义的动态SQL节点,形成数据库能执行的sql语句。

SQL执行
SQL语句的执行涉及多个组件,包括MyBatis的四大神器,它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL的执行过程可以

用下面这幅图来表示

d06ababc345a49838402fe09ed21e5f4.png
MyBatis层级结构各个组件的介绍(这里只是简单介绍,具体介绍在后面): 

SqlSession: MyBatis核心API,主要用来执行命令,获取映射,管理事务。接收开发人员提供Statement Id 和参数.并返回操作结果
Executor: 执行器,是MyBatis调度的核心,负责SQL语句的生成以及查询缓存的维护
StatementHandler: 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler: 负责对用户传递的参数转换成JDBC Statement 所需要的参数
ResultSetHandler: 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler: 用于Java类型和jdbc类型之间的转换
MappedStatement: 动态SQL的封装
SqlSource: 表示从XML文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的SQL。
Configuration: MyBatis所有的配置信息都维持在Configuration对象之中
基础支持层

该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

(1)反射模块

Mybatis中的反射模块,对Java原生的反射进行了很好的封装,提供了简易的API,方便上层调用,并且对反射操作进行了一系列的优化,比如,缓存了类的元数据(MetaClass)和对象的元数据(MetaObject),提高了反射操作的性能。

(2)类型转换模块

Mybatis的别名机制,是为了简化配置文件的,该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能是实现JDBC类型与Java类型间的转换。该功能在SQL语句绑定实参和映射查询结果集时都会涉及。在SQL语句绑定实参时,会将数据有Java类型转换成JDBC类型;在映射结果集时,会将数据有JDBC类型转换成Java类型。

(3)日志模块

Java世界里,有很多优秀的日志框架,如Log4j、Log4j2、slf4j等。Mybatis除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架。

(4)资源加载模块

该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能。

(5) 解析器模块

该模块有两个主要功能:一个是封装了XPath,为Mybatis初始化时解析mybatis-config.xml配置文件以及映射配置文件提供支持;另一个为处理动态SQL语句中的占位符提供支持。

(6)数据源模块

在数据源模块中,Mybatis自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如,连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供ORM框架以及整个应用的性能都是非常重要的。

(7)事务管理模块

一般地,Mybatis与Spring框架集成,由Spring框架管理事务。但Mybatis自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现。

(8)缓存模块

Mybatis中有一级缓存和二级缓存,这两级缓存都依赖于缓存模块中的实现。但是,需要注意,这两级缓存与Mybatis以及整个应用是运行在同一个JVM中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用Redis、Memcache等缓存产品。

(9)Binding模块

在调用SqlSession相应方法执行数据库操作时,需要制定映射文件中定义的SQL节点,如果sql中出现了拼写错误,那就只能在运行时才能发现。

为了能尽早发现这种错误,Mybatis通过Binding模块将用户自定义的Mapper接口与映射文件关联起来,系统可以通过调用自定义Mapper接口中的方法执行相应的SQL语句完成数据库操作,从而避免上述问题。

注意,在开发中,我们只是创建了Mapper接口,而并没有编写实现类,这是因为Mybatis自动为Mapper接口创建了动态代理对象。

有时,自定义的Mapper接口可以完全代替映射配置文件,但比如动态SQL语句啊等,还是写在映射配置文件中更好。

相关文章:

  • 继承 - c++
  • Redis无法使用IP链接,只能通过localhost/127.0.0.1链接
  • 实时频谱 TFN 手持式频谱分析仪 RMT716A 9KHz-6.3GHz 高性能全功能
  • 计网 | 网络的两种服务 —— 虚电路和数据报服务
  • 百度知道APP心跳包分析-MD5字段(gzip + CRC32)
  • 数商云渠道商系统如何赋能医疗器械企业实现全渠道数字化管理,驱动高质发展?
  • 把数据库里的未付款订单改成已付款,会发生什么
  • 第18讲:MySQL中常用的日期函数以及基本使用
  • Qt内部的d指针和q指针:Q_DECLARE_PRIVATE是干嘛的
  • Nginx配置实例——反向代理
  • 全球时区查询易语言代码
  • 手把手带你从官网下载安装 Vivado
  • Vue3+TypeScript 自己搭建低代码平台【一篇文章精通系列】
  • java多个sheet页数据导出
  • ABAP数据库操作- 删除数据 DELETE
  • Consul Config 使用Git做版本控制的实现
  • DOM的那些事
  • jquery cookie
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 让你的分享飞起来——极光推出社会化分享组件
  • 运行时添加log4j2的appender
  • 7行Python代码的人脸识别
  • 阿里云移动端播放器高级功能介绍
  • ​Python 3 新特性:类型注解
  • ​TypeScript都不会用,也敢说会前端?
  • #define、const、typedef的差别
  • #QT(一种朴素的计算器实现方法)
  • (03)光刻——半导体电路的绘制
  • (rabbitmq的高级特性)消息可靠性
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (四)c52学习之旅-流水LED灯
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .CSS-hover 的解释
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • // an array of int
  • /proc/stat文件详解(翻译)
  • ??myeclipse+tomcat
  • @property @synthesize @dynamic 及相关属性作用探究
  • [2019.2.28]BZOJ4033 [HAOI2015]树上染色
  • [BUG]Datax写入数据到psql报不能序列化特殊字符
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [C#]猫叫人醒老鼠跑 C#的委托及事件
  • [C++提高编程](三):STL初识
  • [CF226E]Noble Knight's Path
  • [ERROR] Plugin 'InnoDB' init function returned error
  • [ffmpeg] av_opt_set 解析
  • [FUNC]判断窗口在哪一个屏幕上
  • [iOS]GCD(一)
  • [LeetCode][LCR190]加密运算——全加器的实现
  • [Linux] day07——查看及过滤文本