tk.mybatis如何扩展自己的通用mapper

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方法做插入的时候会自动过滤掉空值。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2021-06-10

解决springdataJPA对原生sql支持的问题

springdataJPA对原生sql支持问题 在项目中用到的是springdataJPA连接数据库进行操作,但是JPA中的hql语句不能够满足业务要求,因而需要用到原生sql 但是有一个问题: @Query(value = "SELECT ppd.* FROM zt_productionplandetails AS ppd \n" + " \tLEFT JOIN zt_salesplan sp ON sp.id=ppd.salesPlan_id \n" + &qu

解决mybatis中的mapper命名问题

mybatis mapper命名问题 mapper文件中id命名最好首字母小写,避免让mybatis认为是一个类 <!--获取供应商列表--> <resultMap id="ProviderList" type="Provider"> <result property="id" column="id"/> <result property="proCode" col

Spring Data JPA中 in 条件参数的传递方式

关于Spring Data JPA中自定义sql 条件的 in参数记录 此文做一个记录,以便以后观看,也希望正在遇到同样问题的同学能有所启发,如果你有更好的方法,或我的做法有问题,请告知下,非常感谢. 下面写两种简单做法,注意下地方: 在此我只记录HQL 中 in 后面参数问题,细节查询方法相关代码就略过,还请见谅 查询接口: /** ** paramString : 自定义 hql ** paramMap : hql中查询条件的参数 ** start:数据开始条数 ** max:最大数据条数

解决tk.mybatis中写自定义的mapper的问题

问题 使用tk.mybatis能满足大多数操作,但是想添加自己的查询方法时候今天遇到了坑,总结一下 官方教程 大致分两种 1. 使用纯接口注解方式时 在mapper接口中自定义方法上添加如@Select,@insert类似的注释,里边写相应的sql语句,如下 import org.apache.ibatis.annotations.Select; import tk.mybatis.mapper.common.Mapper; public interface CountryMapper exte

使用jpa原生sql@Query操作增删改查

jpa原生sql@Query操作增删改查 1.jpa 原生update的sql语句: 1.命名参数(推荐使用此方式):可以定义好参数名,赋值时使用@Param("参数 名"), 而不用管顺序. 为@Query注解传递参数的方式1:命名参数 /*@Query("delete from product where p.id=:id") List<Person> testQueryAnnotationParams2(@Param("status&qu

SpringBoot+MySQL+Jpa实现对数据库的增删改查和分页详解

一. 使用Springboot+Jpa实现对mysql数据库的增删改查和分页功能 JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. 使用Springboot和jpa对数据库进行操作时,能够大大减少我们的工作量,在jpa中,已经在底层封装好了增删查的功能和sql语句,可以使我们进行快速开发 二.项目过程和配置文件 1.applaction.properties文件配置

asp.net(C#) Xml操作(增删改查)练习

web.config配置: 复制代码 代码如下: <appSettings> <add key="xmlFile" value="xml/class.xml"/> </appSettings> <appSettings> <add key="xmlFile" value="xml/class.xml"/> </appSettings> 前台: 复制代码 代

Django-Model数据库操作(增删改查、连表结构)详解

一.数据库操作 1.创建model表 基本结构 from django.db import models class userinfo(models.Model): #如果没有models.AutoField,默认会创建一个id的自增列 name = models.CharField(max_length=30) email = models.EmailField() memo = models.TextField() 更多字段: 1.models.AutoField 自增列= int(11)

基于sqlalchemy对mysql实现增删改查操作

需求场景: 老大让我利用爬虫爬取的数据写到或更新到mysql数据库中,百度了两种方法 1 是使用pymysql连接mysql,通过操作原生的sql语句进行增删改查数据: 2 是使用sqlalchemy连接mysql,通过ORM模型建表并操作数据库,不需要写原生的sql语句,相对简单些: 以下就是本次使用sqlalchemy的经验之谈. 实现流程:连接数据库>通过模型类创建表>建立会话>执行创建表语句>通过会话进行增删改查 from sqlalchemy import exists,

iOS中FMDB数据库之增删改查使用实例

FMDB是一个轻量级的数据库,用于将网络资源存储在本地.FMDB 将 SQLite API 进行了很友好的封装,使用上非常方便,对于那些使用纯 Sqlite API 来进行数据库操作的 app,可以考虑将其迁移到基于 FMDB 上,这对于以后数据库相关功能的开发维护,可以提高不少效率. 什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API FMDB的优点 使用起来更加面向对象,省去了很多麻烦.冗余的C语言代码 对比苹果自带的Core

C#访问SQLServer增删改查代码实例

一个专门实现访问sql server数据库增删改查的操作代码,分享给大家,具体内容如下 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Data; usi

SpringBoot JPA实现增删改查、分页、排序、事务操作等功能示例

今天给大家介绍一下SpringBoot中JPA的一些常用操作,例如:增删改查.分页.排序.事务操作等功能. 下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex): public List<User> findByHeightAndSex(int height,char sex); // Or --- 等价于 SQL 中的 or 关键字,比如 findByHei

Python Sql数据库增删改查操作简单封装

本文实例为大家分享了如何利用Python对数据库的增删改查进行简单的封装,供大家参考,具体内容如下 1.insert import mysql.connector import os import codecs #设置数据库用户名和密码 user='root';#用户名 pwd='root';#密码 host='localhost';#ip地址 db='mysql';#所要操作数据库名字 charset='UTF-8' cnx = mysql.connector.connect(user=use

JPA之使用JPQL语句进行增删改查

JPA支持两种表达查询的方法来检索实体和来自数据库的其他持久化数据:查询语句(Java Persistence Query Language,JPQL)和条件API(criteria API).JPQL是独立于数据库的查询语句,其用于操作逻辑上的实体模型而非物理的数据模型.条件API是根据实体模型构建查询条件 1.Java持久化查询语句入门 复制代码 代码如下: List<Person> persons= entityManager.createQuery("select p fro

ThinkPHP5.1框架数据库链接和增删改查操作示例

本文实例讲述了ThinkPHP5.1框架数据库链接和增删改查操作.分享给大家供大家参考,具体如下: 一.数据库的链接方式 <?php namespace app\index\controller; use think\Db; class Demo { //1.全局配置 config/database.php配置 public function dbTest() { return Db::table('pzq_article') ->where('id','29') ->value('ti