jdbc-plus简介

jdbc-plus是一款基于JdbcTemplate增强工具包,基于JdbcTemplate已实现分页、多租户、动态表名等插件,可与mybatis、mybatis-plus等混合使用,还可以十分简单的扩展自定义插件。

特性

  • 使用简单,对代码入侵很小,可与mybatis、mybatis-plus等混合使用
  • 已实现分页、多租户、动态表名等插件,还可以十分简单的扩展自定义插件
  • 免费开源,可任意使用修改代码
  • 只对ORM框架增强不做任何改变,当需要动态执行SQL不是很方面使用ORM框架执行SQL时,jdbc-plus就能发挥作用

插件(持续扩展中)

已内置以下插件,开箱即用,还可以自行扩展插件,扩展插件方法十分简单。

  • 分页插件:与PageHelper使用方法一致,还可以注册不支持的数据库
  • 多租户插件:与mybatis-plus多租户插件使用方法一致,理论上与mybatis-plus多租户插件支持度一样
  • 动态表名插件:与mybatis-plus动态表名插件使用方法一致
  • 更多插件:持续关注jdbc-plus仓库,仓库包含所有插件源代码以及使用示例

项目开源地址

https://github.com/deeround/jdbc-plus

快速开始

  1. 引入jdbc-plus-spring-boot-starter
<dependency>
<groupId>com.github.deeround</groupId>
<artifactId>jdbc-plus-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
  1. 注入需要使用的插件(需要哪个注入哪个,不需要的注释掉即可)
@Configuration
public class JdbcPlusConfig { /**
* TenantLineInterceptor是内置的多租户插件
*/
@Bean
@Order(1)
public IInterceptor tenantLineInterceptor() {
return new TenantLineInterceptor(new TenantLineHandler() {
/**
* 当前租户ID
*/
@Override
public Expression getTenantId() {
String currentTenantId = "test_tenant_1";//可以从请求上下文中获取(cookie、session、header等)
return new StringValue(currentTenantId);
} /**
* 租户字段名
*/
@Override
public String getTenantIdColumn() {
return "tenant_id";
} /**
* 根据表名判断是否忽略拼接多租户条件
*/
@Override
public boolean ignoreTable(String tableName) {
return TenantLineHandler.super.ignoreTable(tableName);
}
});
} /**
* DynamicTableNameInterceptor是内置的动态表名插件
*/
@Bean
@Order(2)
public IInterceptor dynamicTableNameInterceptor() {
return new DynamicTableNameInterceptor(new TableNameHandler() {
@Override
public String dynamicTableName(String sql, String tableName) {
if ("test_log".equals(tableName)) {
return tableName + "_" + LocalDateTime.now().getYear();
}
return tableName;
}
});
} /**
* PaginationInterceptor是内置的分页插件(分页插件一般情况放置最后)
*/
@Bean
@Order(9)
public IInterceptor paginationInterceptor() {
return new PaginationInterceptor();
} /**
* 自定义插件注入,注入位置按实际情况
*/
@Bean
@Order(0)
public IInterceptor myStatInterceptor() {
return new MyStatInterceptor();
}
}
  1. 正常使用JdbcTemplate执行SQL语句,代码零入侵,使用体验超棒
    @Autowired
JdbcTemplate jdbcTemplate; public void insert() {
this.jdbcTemplate.update("insert into test_user(id,name) values('1','wangwu')");
//最终执行SQL:insert into test_user(id,name,tenant_id) values('1','wangwu','test_tenant_1')
} public void delete() {
this.jdbcTemplate.update("delete from test_user where id='1'");
//最终执行SQL:delete from test_user where id='1' and tenant_id='test_tenant_1'
} public void update() {
this.jdbcTemplate.update("update test_user set name='lisi' where id='1'");
//最终执行SQL:update test_user set name='lisi' where id='1' and tenant_id='test_tenant_1'
} public List<Map<String, Object>> query() {
return this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user where tenant_id='test_tenant_1'
} public PageInfo<Map<String, Object>> page1() {
PageHelper.startPage(1, 2);
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user LIMIT 0,2
PageInfo<Map<String, Object>> page = new PageInfo<>(list);
//PageInfo对象包含了分页信息(总行数等)
return page;
}

多租户插件

  1. 注入多租户插件
    /**
* TenantLineInterceptor是内置的多租户插件插件
*/
@Bean
@Order(1)
public IInterceptor tenantLineInterceptor() {
return new TenantLineInterceptor(new TenantLineHandler() {
/**
* 当前租户ID
*/
@Override
public Expression getTenantId() {
String currentTenantId = "test_tenant_1";//可以从请求上下文中获取(cookie、session、header等)
return new StringValue(currentTenantId);
} /**
* 租户字段名
*/
@Override
public String getTenantIdColumn() {
return "tenant_id";
} /**
* 根据表名判断是否忽略拼接多租户条件
*/
@Override
public boolean ignoreTable(String tableName) {
return TenantLineHandler.super.ignoreTable(tableName);
}
});
}
  1. service层执行SQL时自动添加租户字段
    public void insert() {
this.jdbcTemplate.update("insert into test_user(id,name) values('1','wangwu')");
//最终执行SQL:insert into test_user(id,name,tenant_id) values('1','wangwu','test_tenant_1')
} public void delete() {
this.jdbcTemplate.update("delete from test_user");
//最终执行SQL:delete from test_user where tenant_id='test_tenant_1'
} public void update() {
this.jdbcTemplate.update("update test_user set name='lisi' where id='1'");
//最终执行SQL:update test_user set name='lisi' where id='1' and tenant_id='test_tenant_1'
} public List<Map<String, Object>> query() {
return this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user where tenant_id='test_tenant_1'
}

分页插件

  1. 注入分页插件
    /**
* PaginationInterceptor是内置的分页插件(分页插件一般情况放置最后)
*/
@Bean
@Order(9)
public IInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
  1. service层执行SQL时自动对SQL进行分页查询
    public PageInfo<Map<String, Object>> page1() {
PageHelper.startPage(1, 2);
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user LIMIT 0,2
PageInfo<Map<String, Object>> page = new PageInfo<>(list);
//PageInfo对象包含了分页信息(总行数等)
return page;
} public PageInfo<Map<String, Object>> page2() {
PageHelper.startPage(2, 2);
List<Map<String, Object>> list = this.jdbcTemplate.queryForList("select * from test_user");
//最终执行SQL:select * from test_user LIMIT 2,2
PageInfo<Map<String, Object>> page = new PageInfo<>(list);
//PageInfo对象包含了分页信息(总行数等)
return page;
}
  1. 自定义分页

当插件不支持的数据库分页,可以通过PageHelper.registerDialectAlias(String alias, Class clazz) 注册一个自己分页实现类即可,也可以覆盖已支持的数据库分页。

动态表名插件

  1. 注入分页插件
    /**
* DynamicTableNameInterceptor是内置的动态表名插件
*/
@Bean
@Order(2)
public IInterceptor dynamicTableNameInterceptor() {
return new DynamicTableNameInterceptor(new TableNameHandler() {
@Override
public String dynamicTableName(String sql, String tableName) {
if ("test_log".equals(tableName)) {
return tableName + "_" + LocalDateTime.now().getYear();
}
return tableName;
}
});
}
  1. service层执行SQL时自动对SQL进行分页查询
    public List<Map<String, Object>> getTestLogList() {
return this.jdbcTemplate.queryForList("select * from test_log");
//最终执行SQL:select * from test_log_2023
}

自定义插件

示例:写一个打印SQL语句、执行参数、以及执行SQL耗时的监控插件。

  1. 编写MyStatInterceptor插件
/**
* SQL监控插件
*/
@Slf4j
public class MyStatInterceptor implements IInterceptor {
/**
* 自定义插件是否支持
*/
@Override
public boolean supportMethod(final MethodInvocationInfo methodInfo) {
return IInterceptor.super.supportMethod(methodInfo);
} /**
* SQL执行前方法(主要用于对SQL进行修改)
*/
@Override
public void beforePrepare(final MethodInvocationInfo methodInfo, JdbcTemplate jdbcTemplate) {
log.info("执行SQL开始时间:{}", LocalDateTime.now());
log.info("原始SQL:{}", Arrays.toString(methodInfo.getBatchSql()));
log.info("调用方法名称:{}", methodInfo.getName());
log.info("调用方法入参:{}", Arrays.toString(methodInfo.getArgs())); methodInfo.putUserAttribute("startTime", LocalDateTime.now());
} /**
* SQL执行完成后方法(主要用于对返回值修改)
*
* @param result 原始返回对象
* @return 处理后的返回对象
*/
@Override
public Object beforeFinish(Object result, final MethodInvocationInfo methodInfo, JdbcTemplate jdbcTemplate) {
log.info("执行SQL结束时间:{}", LocalDateTime.now());
LocalDateTime startTime = (LocalDateTime) methodInfo.getUserAttribute("startTime");
log.info("执行SQL耗时:{}毫秒", Duration.between(startTime, LocalDateTime.now()).toMillis());
return result;
}
}
  1. 注入自定义插件
    /**
* 自定义插件注入,注入位置按实际情况
*/
@Bean
@Order(0)
public IInterceptor myStatInterceptor() {
return new MyStatInterceptor();
}
  1. 查看效果(查看打印日志)
c.g.d.j.p.s.config.MyStatInterceptor     : 原始SQL:select * from test_user
c.g.d.j.p.s.config.MyStatInterceptor : 入参:[select * from test_user]
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL开始时间:2023-04-23T16:35:58.151
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL结束时间:2023-04-23T16:35:58.655
c.g.d.j.p.s.config.MyStatInterceptor : 执行SQL耗时:503毫秒

JdbcTemplate支持

  • 所有batchUpdate、update、query、queryForList、queryForMap、queryForObject、queryForRowSet、queryForStream都支持(但是方法第一个入参必须是sql语句的才支持)
  • 所有query开头的方法都支持分页插件(但是只有返回类型是List<?>的才支持查询汇总信息)
  • 如果有其他方法需要支持的,可以联系作者
  • 也可以手动调用MethodActionRegister.register(Class<JdbcTemplate> clazz, MethodActionInfo actionInfo, String name, Class<?>... parameterTypes)方法进行注册使jdbc-plus支持该方法

支持的方法明细(里面注释内容就是支持的JdbcTemlate方法):

    /**
* int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)
*/
BATCHUPDATE_SQL_PSS, /**
* int[][] batchUpdate(String sql, final Collection<T> batchArgs, final int batchSize,
* final ParameterizedPreparedStatementSetter<T> pss)
*/
BATCHUPDATE_SQL_BATCHARGS_BATCHSIZE_PSS, /**
* int[] batchUpdate(String sql, List<Object[]> batchArgs)
*/
BATCHUPDATE_SQL_BATCHARGS, /**
* int[] batchUpdate(String sql, List<Object[]> batchArgs, final int[] argTypes)
*/
BATCHUPDATE_SQL_BATCHARGS_ARGTYPES, /**
* int[] batchUpdate(final String... sql)
*/
BATCHUPDATE_SQL, /**
* int update(final String sql)
*/
UPDATE_SQL, /**
* int update(String sql, @Nullable Object... args)
*/
UPDATE_SQL_ARGS, /**
* update(String sql, Object[] args, int[] argTypes)
*/
UPDATE_SQL_ARGS_ARGTYPES, /**
* int update(String sql, @Nullable PreparedStatementSetter pss)
*/
UPDATE_SQL_PSS, /**
* T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse)
*/
QUERY_SQL_ARGS_ARGTYPES_RSE, /**
* void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch)
*/
QUERY_SQL_ARGS_ARGTYPES_RCH, /**
* List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper)
*/
QUERY_SQL_ARGS_ARGTYPES_ROWMAPPER, /**
* T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse)
*/
QUERY_SQL_ARGS_RSE, /**
* void query(String sql, @Nullable Object[] args, RowCallbackHandler rch)
*/
QUERY_SQL_ARGS_RCH, /**
* List<T> query(String sql, @Nullable Object[] args, RowMapper<T> rowMapper)
*/
QUERY_SQL_ARGS_ROWMAPPER, /**
* T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse)
*/
QUERY_SQL_PSS_RSE, /**
* void query(String sql, @Nullable PreparedStatementSetter pss, RowCallbackHandler rch)
*/
QUERY_SQL_PSS_RCH, /**
* List<T> query(String sql, @Nullable PreparedStatementSetter pss, RowMapper<T> rowMapper)
*/
QUERY_SQL_PSS_ROWMAPPER, /**
* T query(final String sql, final ResultSetExtractor<T> rse)
*/
QUERY_SQL_RSE, /**
* T query(String sql, ResultSetExtractor<T> rse, @Nullable Object... args)
*/
QUERY_SQL_RSE_ARGS, /**
* void query(String sql, RowCallbackHandler rch)
*/
QUERY_SQL_RCH, /**
* void query(String sql, RowCallbackHandler rch, @Nullable Object... args)
*/
QUERY_SQL_RCH_ARGS, /**
* List<T> query(String sql, RowMapper<T> rowMapper)
*/
QUERY_SQL_ROWMAPPER, /**
* List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
*/
QUERY_SQL_ROWMAPPER_ARGS, /**
* List<Map<String, Object>> queryForList(String sql)
*/
QUERYFORLIST_SQL, /**
* List<T> queryForList(String sql, Class<T> elementType)
*/
QUERYFORLIST_SQL_ELEMENTTYPE, /**
* List<T> queryForList(String sql, Class<T> elementType, @Nullable Object... args)
*/
QUERYFORLIST_SQL_ELEMENTTYPE_ARGS, /**
* List<Map<String, Object>> queryForList(String sql, @Nullable Object... args)
*/
QUERYFORLIST_SQL_ARGS, /**
* List<T> queryForList(String sql, @Nullable Object[] args, Class<T> elementType)
*/
QUERYFORLIST_SQL_ARGS_ELEMENTTYPE, /**
* List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes)
*/
QUERYFORLIST_SQL_ARGS_ARGTYPES, /**
* List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType)
*/
QUERYFORLIST_SQL_ARGS_ARGTYPES_ELEMENTTYPE, /**
* Map<String, Object> queryForMap(String sql)
*/
QUERYFORMAP_SQL, /**
* Map<String, Object> queryForMap(String sql, @Nullable Object... args)
*/
QUERYFORMAP_SQL_ARGS, /**
* Map<String, Object> queryForMap(String sql, Object[] args, int[] argTypes)
*/
QUERYFORMAP_SQL_ARGS_ARGTYPES, /**
* T queryForObject(String sql, Class<T> requiredType)
*/
QUERYFOROBJECT_SQL_REQUIREDTYPE, /**
* T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args)
*/
QUERYFOROBJECT_SQL_REQUIREDTYPE_ARGS, /**
* T queryForObject(String sql, @Nullable Object[] args, Class<T> requiredType)
*/
QUERYFOROBJECT_SQL_ARGS_REQUIREDTYPE, /**
* T queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType)
*/
QUERYFOROBJECT_SQL_ARGS_ARGTYPES_REQUIREDTYPE, /**
* T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper)
*/
QUERYFOROBJECT_SQL_ARGS_ARGTYPES_ROWMAPPER, /**
* T queryForObject(String sql, @Nullable Object[] args, RowMapper<T> rowMapper)
*/
QUERYFOROBJECT_SQL_ARGS_ROWMAPPER, /**
* T queryForObject(String sql, RowMapper<T> rowMapper)
*/
QUERYFOROBJECT_SQL_ROWMAPPER, /**
* T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
*/
QUERYFOROBJECT_SQL_ROWMAPPER_ARGS, /**
* SqlRowSet queryForRowSet(String sql)
*/
QUERYFORROWSET_SQL, /**
* SqlRowSet queryForRowSet(String sql, @Nullable Object... args)
*/
QUERYFORROWSET_SQL_ARGS, /**
* SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes)
*/
QUERYFORROWSET_SQL_ARGS_ARGTYPES, /**
* Stream<T> queryForStream(String sql, @Nullable PreparedStatementSetter pss, RowMapper<T> rowMapper)
*/
QUERYFORSTREAM_SQL_PSS_ROWMAPPER, /**
* Stream<T> queryForStream(String sql, RowMapper<T> rowMapper)
*/
QUERYFORSTREAM_SQL_ROWMAPPER, /**
* Stream<T> queryForStream(String sql, RowMapper<T> rowMapper, @Nullable Object... args)
*/
QUERYFORSTREAM_SQL_ROWMAPPER_ARGS,

联系我

方式一:GitHub上提Issue

方式二:发送邮件(GitHub上有邮箱地址)

鸣谢

欢迎各路好汉一起来参与完善 jdbc-plus,感兴趣的可以在github点个 ,有任何问题和建议欢迎提交 Issue !

感谢以下开源项目:

  • mybatis-plus

  • Mybatis-PageHelper

jdbc-plus是一款基于JdbcTemplate增强工具包,基于JdbcTemplate已实现分页、多租户、动态表名等插件,可与mybatis、mybatis-plus等混合使用的更多相关文章

  1. KC705E 增强版 基于FMC接口的Xilinx Kintex-7 FPGA K7 XC7K325T PCIeX8 接口卡

    KC705E 增强版 基于FMC接口的Xilinx Kintex-7 FPGA K7 XC7K325T PCIeX8 接口卡 一.板卡概述 本板卡基于Xilinx公司的FPGAXC7K325T-2FF ...

  2. 270-VC709E 增强版 基于FMC接口的Xilinx Vertex-7 FPGA V7 XC7VX690T PCIeX8 接口卡

    VC709E 增强版 基于FMC接口的Xilinx Vertex-7 FPGA V7 XC7VX690T PCIeX8 接口卡 一.板卡概述       本板卡基于Xilinx公司的FPGA XC7V ...

  3. 提高工作效率的神器:基于前端表格实现Chrome Excel扩展插件

    Chrome插件,官方名称extensions(扩展程序):为了方便理解,以下都称为插件. 我们开发的插件需要在浏览器里面运行,打开浏览器,通过右上角的三个点(自定义及控制)-更多工具-拓展程序-打开 ...

  4. ExtJS4.2学习(13)基于表格的扩展插件---rowEditing

    鸣谢:http://www.shuyangyang.com.cn/jishuliangongfang/qianduanjishu/2013-11-24/182.html --------------- ...

  5. 基于 HtmlHelper 的自定义扩展Container

    基于 HtmlHelper 的自定义扩展Container Intro 基于 asp.net mvc 的权限控制系统的一部分,适用于对UI层数据呈现的控制,基于 HtmlHelper 的扩展组件 Co ...

  6. Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较

    本篇博文用一个稍复杂点的案例来对比一下基于XML配置与基于AspectJ注解配置的AOP编程的不同. 相关引入包等Spring  AOP编程准备,请参考小编的其他博文,这里不再赘述. 案例要求: 写一 ...

  7. 基于AOP的插件化(扩展)方案

    在项目迭代开发中经常会遇到对已有功能的改造需求,尽管我们可能已经预留了扩展点,并且尝试通过接口或扩展类完成此类任务.可是,仍然有很多难以预料的场景无法通过上述方式解决.修改原有代码当然能够做到,但是这 ...

  8. 推荐一款VS2008代码增强插件——MetalScroll

    时光如水,岁月如歌.虽然现在已经是2013年底马上就要步入2014了,但还是有很多人在使用VS2008开发项目,今天要推荐一款VS2008(同时支持VS2005,但不支持VS2010)代码增强插件给仍 ...

  9. Cmder | 一款命令行增强工具

    文章目录 什么是cmder 安装cmder 让cmder便于使用 将cmder添加到右键菜单中 在设置中添加语言环境 设置默认使用cmd.PowerShell还是bash 调节背景的透明度 添加 ll ...

  10. KC705E增强版基于FMC接口的 Kintex-7 XC7K325T PCIeX8 接口卡

    一.板卡概述 本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片,pin_to_pin兼容FPGAXC7K410T-2FFG900 ,支持PCIeX8.64bit DDR3容量 ...

随机推荐

  1. 循序渐进的掌握uni-app,两个小时完成一个简单项目——新闻App、新闻小程序

    效果图 一.创建项目 uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条/ ...

  2. VScode连接GPU服务器进行深度学习

    VScode连接GPU服务器进行深度学习 ​ 最近用台式机跑一些小的深度学习项目,发现越来越慢了,由于一些原因,有时候需要我进行现场作业但是我的笔记本是轻薄本(Thinkpad YYDS)不带显卡,百 ...

  3. C# 获取系统已安装的.NET版本

    获取系统已安装的.NET版本,来确定当前应用可运行的环境. 微软已经有相应的完整文档,请参考:确定已安装的 .NET Framework 版本 - .NET Framework | Microsoft ...

  4. 如何实现Spring中服务关闭时对象销毁执行代码

    spring提供了两种方式用于实现对象销毁时去执行操作 1.实现DisposableBean接口的destroy 2.在bean类的方法上增加@PreDestroy方法,那么这个方法会在Disposa ...

  5. Web服务器压力测试工具 - HULK

    HULK是一种web的拒绝服务攻击工具.它能够在web服务器上产生许多单一的伪造流量,能绕开引擎的缓存,因此能够直接攻击服务器的资源池.hulk的特别之处在于:对于每一个请求都是独特的,能够绕开引擎的 ...

  6. Prism Sample 12-UsingCompositeCommands

    本例中,主页是一个按钮,绑定了一个复合命令,然后下面一个TabControl <Grid> <Grid.RowDefinitions> <RowDefinition He ...

  7. 如何利用Requestly提升前端开发与测试的效率,让你事半功倍?

    痛点 前端测试 在进行前端页面开发或者测试的时候,我们会遇到这一类场景: 在开发阶段,前端想通过调用真实的接口返回响应 在开发或者生产阶段需要验证前端页面的一些 异常场景 或者 临界值 时 在测试阶段 ...

  8. #Python 利用pivot_table,数据透视表进行数据分析

    前面我们分享了,利用python进行数据合并和连接,但是工作中,我们往往需要对数据进一步的聚合或者运算,以求最后的数据结果. 今天我们就来学习一下利用pandas模块,对数据集进行数据透视分析. pi ...

  9. #Powerbi 利用EOMONTH,计算月度时间进度

    在POWERBI中计算本月的时间进度 许多小伙伴在做看板时,往往会遇到需要时间进度的计算,来配合业绩达成的进度,进行比较分析.那么如何在BI做通过公式进行这个数据的求值呢? 首先,我们需要测算两个值, ...

  10. 2021-03-25:如何把长url转换为短url?

    2021-03-25:如何把长url转换为短url? 福大大 答案2021-03-25: 1.长url和短url一一映射.想采用某种算法,把长url和短url做一一映射.后来发现,这种方法是行不通的. ...