tk.mybatis如何扩展自己的通用mapper
作者:OUOll 发布时间:2022-02-16 04:10:17
tk.mybatis扩展自己的通用mapper
目的:tk.mybatis 提供的通用mapper,虽然使用方便,不过在有些sql还是不能满足我们的需要的,而且我们希望对于deleted语句进行管控(因为通用mapper提供的都是物理删除,有时我们对一些敏感数据只能进行逻辑删除),因此我们将基于原有的通用mapper进行自己的扩展
1 jar引入
<!-- 非springboot -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.4.0</version>
</dependency>
非springboot项目引这个包
<!-- springboot -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
springboot项目直接引这个包,本次我们将基于springboot进行开发,这里注意jar包版本,过低的版本将会出现问题,下面会介绍
2 编写配置类
package com.example.configuration;
import com.example.common.mapper.commonMapper.CommonMapper;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;
import java.util.Properties;
@Configuration
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class MyBatisConfiguration {
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
mapperScannerConfigurer.setBasePackage("com.example.dao.mapper");
Properties properties = new Properties();
properties.setProperty("mappers", CommonMapper.class.getName());
properties.setProperty("notEmpty", "false");
properties.setProperty("identity", "MYSQL");
properties.setProperty("order","BEFORE");
mapperScannerConfigurer.setProperties(properties);
return mapperScannerConfigurer;
}
}
这里要注意两点:
1>CommonMapper这个是我们自己定义的通用mapper,它放置一个单独的包下面。也就是说这个包下面只有这个一个mapper,否则我们在实用泛型时会出现类型转换的问题。(我在这里纠结了好久)
2>这里我们配置了自定义mapper的扫描路径。注意springboot项目一定要注意jar包版本,版本过低的话,在配置类中的定义是没有用的。还是扫描不到,我之前使用的版本1.2.3就会出现这个问题,更换更高的版本就可以解决了。
3 常规方法接口整合到自己的自定义Mapper接口上
因为我们使用的是mysql数据库,所以在使用通用功能的时候就选择性引入一些经常使用的方法,下面是自己定义的常用mapper类的整合(这里会抛弃一些不常用的类或不是mysql的方法类)。这些基类不要放在 BasePackage的路径下,这个路径下的mapper都是要指定明确的泛型类型的。
1 > 提供查询功能Mapper:
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.select.*;
import tk.mybatis.mapper.common.condition.SelectByConditionMapper;
import tk.mybatis.mapper.common.condition.SelectCountByConditionMapper;
import tk.mybatis.mapper.common.example.SelectByExampleMapper;
import tk.mybatis.mapper.common.ids.SelectByIdsMapper;
/**
* @desc 基础查询功能mapper
*
*/
public interface SelectMapper<T> extends Marker,
SelectOneMapper<T>,
tk.mybatis.mapper.common.base.select.SelectMapper<T>,
SelectAllMapper<T>,
SelectCountMapper<T>,
SelectByPrimaryKeyMapper<T>,
ExistsWithPrimaryKeyMapper<T>,
SelectByIdsMapper<T>,
SelectByConditionMapper<T>,
SelectCountByConditionMapper<T>,
SelectByExampleMapper<T> {
}
2>提供新增功能mapper
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.MySqlMapper;
import tk.mybatis.mapper.common.base.insert.InsertSelectiveMapper;
/**
* @desc 基础插入功能mapper
*/
public interface InsertMapper<T> extends Marker,
tk.mybatis.mapper.common.base.insert.InsertMapper<T>,
InsertSelectiveMapper<T>,
MySqlMapper<T>{
}
3>提供更新功能mapper
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeyMapper;
import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeySelectiveMapper;
import tk.mybatis.mapper.common.condition.UpdateByConditionMapper;
import tk.mybatis.mapper.common.condition.UpdateByConditionSelectiveMapper;
import tk.mybatis.mapper.common.example.UpdateByExampleSelectiveMapper;
/**
* @desc 基础更新功能mapper
*
*/
public interface UpdateMapper<T> extends Marker,
UpdateByPrimaryKeyMapper<T>,
UpdateByPrimaryKeySelectiveMapper<T>,
UpdateByConditionMapper<T>,
UpdateByConditionSelectiveMapper<T>,
UpdateByExampleSelectiveMapper<T> {
}
4>提供删除功能mapper
package com.example.common.mapper.basics;
import com.example.common.mapper.defined.DeleteShamByIdsMapper;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.delete.DeleteByPrimaryKeyMapper;
import tk.mybatis.mapper.common.condition.DeleteByConditionMapper;
import tk.mybatis.mapper.common.ids.DeleteByIdsMapper;
/**
* @desc 基础删除功能mapper
*
*/
public interface DeleteMapper<T> extends Marker,
tk.mybatis.mapper.common.base.delete.DeleteMapper<T>,
DeleteByPrimaryKeyMapper<T>,
DeleteByConditionMapper<T>,
DeleteByIdsMapper<T>{
}
5>提供增删改查的基类 CommonMapper
package com.example.common.mapper.commonMapper;
import com.example.common.mapper.basics.DeleteMapper;
import com.example.common.mapper.basics.InsertMapper;
import com.example.common.mapper.basics.SelectMapper;
import com.example.common.mapper.basics.UpdateMapper;
/**
* @param <T>
* @desc 基础增删改查功能mapper
*/
public interface CommonMapper<T> extends
DeleteMapper<T>,
InsertMapper<T>,
SelectMapper<T>,
UpdateMapper<T> {
}
6>实体对象
package com.example.dao.entity;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
@Table(name="user")
@Data
public class User implements Serializable {
@Id
private Integer id;
private String trueName;
private String userName;
private Integer isDeleted;
}
7>让自定义mapper继承CommenMapper,就可以使用这些语句了
package com.example.dao.mapper;
import com.example.common.mapper.commonMapper.CommonMapper;
import com.example.dao.entity.User;
public interface UserMapper extends CommonMapper<User> {
}
接下来我们会对 CommonMapper中提供删除方法的DeleteMapper进行增强,来给他添加逻辑删除的语句
1>我们自定义的Provider需要去继承 MapperTemplate并覆写构造方法
这个是源码中的批量删除
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package tk.mybatis.mapper.provider;
import java.util.Set;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.MapperTemplate;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
public class IdsProvider extends MapperTemplate {
public IdsProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String deleteByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.deleteFromTable(entityClass, this.tableName(entityClass)));
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("继承 deleteByIds 方法的实体类[" + entityClass.getCanonicalName() + "]中必须只有一个带有 @Id 注解的字段");
}
}
public String selectByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
this.setResultType(ms, entityClass);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.selectAllColumns(entityClass));
sql.append(SqlHelper.fromTable(entityClass, this.tableName(entityClass)));
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("继承 selectByIds 方法的实体类[" + entityClass.getCanonicalName() + "]中必须只有一个带有 @Id 注解的字段");
}
}
}
接下来我们对IdsProvider 进行扩展使他支持批量逻辑删除
1>创建IdsProvider的子类
package com.example.common.mapper.defined;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
import tk.mybatis.mapper.provider.IdsProvider;
import java.util.Set;
public class IdsProviderDefined extends IdsProvider {
public IdsProviderDefined(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String deleteShamByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.updateTable(entityClass, this.tableName(entityClass)));
sql.append("<set> is_deleted='1' </set>");
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("继承 deleteByIds 方法的实体类[" + entityClass.getCanonicalName() + "]中必须只有一个带有 @Id 注解的字段");
}
}
}
deleteShamByIds就是我们门批量逻辑删除的方法,这个方法其实就是再拼装sql,tk中提供了很多帮助我们拼装sql,和参数的类(SqlHelper,EntityHelper),我们可以使用
2>创建DeleteShamByIdsMapper接口
package com.example.common.mapper.defined;
import org.apache.ibatis.annotations.DeleteProvider;
public interface DeleteShamByIdsMapper<T> {
@DeleteProvider(
type = IdsProviderDefined.class,
method = "dynamicSQL"
)
int deleteShamByIds(String var1);
//这里的抽象方法的名称必须和IdsProviderDefined 中的方法一致
}
3>在DeleteMapper中继承我们自定义的逻辑删除接口DeleteShamByIdsMapper
package com.example.common.mapper.basics;
import com.example.common.mapper.defined.DeleteShamByIdsMapper;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.delete.DeleteByPrimaryKeyMapper;
import tk.mybatis.mapper.common.condition.DeleteByConditionMapper;
import tk.mybatis.mapper.common.ids.DeleteByIdsMapper;
/**
* @desc 基础删除功能mapper
*
*/
public interface DeleteMapper<T> extends Marker,
tk.mybatis.mapper.common.base.delete.DeleteMapper<T>,
DeleteByPrimaryKeyMapper<T>,
DeleteByConditionMapper<T>,
DeleteByIdsMapper<T>,
DeleteShamByIdsMapper<T>{
}
到这里我们自定义的批量逻辑删除就定义好了,我们可以通过这种方式扩展通用mapper。
TK mybatis插件通用mapper与oracle的几个坑
最近公司有几个项目的数据库用的oracle,有段时间没用,然后果断就掉坑里面了,记录几个比较有代表性的。
1:关于oracle的批量数据插入,我用TK通用mapper的insertList方法自动拼接出来的sql是这样的
insert into table(column1,column2)values
(value1,value2),
(value3,value4)。。。
咋一看没啥问题啊,然后就一直报 sql未正确结束,出于对自己sql的过于自信,导致一直都在思考是否是参数问题,浪费了好几个小时,后来把sql拼好直接丢到数据库跑了跑才发现,这种sql确实是不能在oracle跑的,oracle批量插入的语法应该是:
INSERT ALL
INTO A(field_1,field_2) VALUES (value_1,value_2)
INTO A(field_1,field_2) VALUES (value_3,value_4)
INTO A(field_1,field_2) VALUES (value_5,value_6)
粗略的瞅了下TK的代码,貌似确实没有校验数据库驱动就直接开始拼sql了
public String insertList(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.insertIntoTable(entityClass, this.tableName(entityClass)));
sql.append(SqlHelper.insertColumns(entityClass, true, false, false));
sql.append(" VALUES ");
sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\" >");
sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
Iterator var5 = columnList.iterator();
while(var5.hasNext()) {
EntityColumn column = (EntityColumn)var5.next();
if (!column.isId() && column.isInsertable()) {
sql.append(column.getColumnHolder("record") + ",");
}
}
sql.append("</trim>");
sql.append("</foreach>");
return sql.toString();
}
不过我觉得这种插件肯定是有兼容方案的,并且使用了这个插件跟使用源生mybatis并不冲突,所以实际对开发并没有影响,算是复习一下oracle的基础并提醒下自己,程序员还是老老实实先run起来,自以为是的经验有时候也很坑的。
2:关于oracle的比较符号(<> =)和null
先复习一下
一般数据库查询某某字段为空或者不为空的时候都是用is null或者is not null
如果查询条件使用=null或者<>null是查不出来数据的
但是赋值却可以用=null如
update BUS_PLATFORM.bus_trans set return_code = null where return_msg= 'tac验证通过';
这个是可以执行成功的,不过这个都是表外的值操作,那么当表内的数据有null时,又有啥坑呢,比如
select * from table1 where name <>'zhansan'
查询表中name字段不为zhangsan的数据,当表中有name为null的数据时,这些数据也是查不出来的.比如表中有100条数据,一条数据的name值为zhangsan ,九条数据的name值为null,那么这条sql只能查出90条数据,所以如果确切的需求要查询name值不为zhangsan的数据且包括name值为null的数据,sql应该为
select * from table1 where name <>'zhansan' or name is null
3:这个是mybatis的一个规范,基本是凑数的,不过也坑过我
先上内容:mybatis插入null值时需要指定该值的类型(jdbctype),不然会报错。
因为不指定类型的话mybatis会自己去适配匹配的数据库字段类型,null适配不了。搞不定就报错,很稳。不过一般使用xml配置sql的话一般都不会有这个问题,写的时候顺手都会写上。这个也是我使用TK的时候暴露出来的问题,在使用insert方法的时候有null就挂了,不过TK有个insertSelective方法做插入的时候会自动过滤掉空值。
来源:https://blog.csdn.net/OUOll/article/details/88709422


猜你喜欢
- 在Android开发中,有时我们需要对一个对象的集合按照某一个字段进行排序,Beanpublic class Student { priva
- TypeScript简介:TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,而且本质上向这个语
- 前一篇博文介绍了JDK logging基础知识 博文中也提到LogManager,本章主要阐述怎么完全定制化LogManager来
- 1.使用usb口输入的扫描枪,这里实现使用了winform首先创建一个CS文件using System;using System.Colle
- 本文实例讲述了C#在RichTextBox中显示不同颜色文字的方法。分享给大家供大家参考。具体实现方法如下:#region 日志记录、支持其
- 1 问题手写一个程序,完成List集合对象的逆序遍历2 方法创建List接口的多态对象向创建好list集合添加元素使用hasPrevious
- Spring AOP对嵌套方法不起作用今天在调研系统操作记录日志时,好多教程都是借助于Spring AOP机制来实现。于是也采用这种方法来实
- 一、稀疏数组1、什么是稀疏数组当一个数组中大部分元素为0,或者为同一个值的数组时,可以用稀疏数组来保存该数组。稀疏数组,记录一共有几行几列,
- 前言最近在学习Android开发,发现确实有太多东西需要去整理,去学习。慢慢来吧,任何东东的深入学习都是不简单的。今天稍微整理下Spanna
- 指纹识别作为最新兴起的用户身份验证机制,已经被越来越多的应用程序所采用,相比传统的密码九宫格等验证方法,指纹识别更加安全,如今越来越多的安卓
- 1 运算符1.1 概述运算符 用于连接 表达式 的 操作数,并对操作数执行运算。例如,表达式num1+num2,其操作数是num1和num2
- 我有一个文本输入对话框,当我点击对话框上的“是”按钮,它会验证输入,然后关闭对话框。但是,如果输入错误,我想停留在同一个对话框中.。每一次,
- 在Android系统中提供了多种存储技术.通过这些存储技术可以将数据存储在各种存储介质上.比如sharedpreferences可以将数据保
- 目录前言1、创建一个控制台应用程序2、编写测试代码并分析3、总结前言对于C#里面的Foreach学过 语言的人都知道怎么用,但是其原理相信很
- 场景网站智能问答场景,需要对多个分类查询,结果聚合展示由于每种分类都有自己的业务逻辑,有的需要查询数据库中间库,有的需要查询elastics
- 前言此节假日为严格按照国家要求的双休和法定节假日并且包含节假日的补班信息,大家可根据自己的需求自定义处理哦。以下为Maven配置,是程序用到
- 高快省的排序算法有没有既不浪费空间又可以快一点的排序算法呢?那就是“快速排序”啦!光听这个名字是不是就觉得很高端呢。假设我们现在对“6 1
- 目录基本用法基于接口的 * 基于类的 * 异步函数拦截Autofac 集成基于接口的 * 基于类的 * 异步函数拦截Castle 是 200
- Bluetooth结构1、JAVA层frameworks/base/core/java/android/bluetooth/包含了bluet
- 这是主要使用到的jar 文件是:spring mvc +apache common-fileuplad第一步:web.xml 文件。【重点是