HOME> 伊涅斯塔世界杯> 通用mapper使用指南

通用mapper使用指南

2025-11-07 10:12:52

通用mapper使用指南

一、概述

​ 通用 Mapper是一个可以实现任意 MyBatis 通用方法的框架,项目提供了常规的增删改查操作以及查询相关的单表操作。通用 Mapper 是为了解决 MyBatis 使用中 90% 的基本操作,使用它可以很方便的进行开发,可以节省开发人员大量的时间。

二、集成

Spring集成

第一步:添加依赖

org.mybatis

mybatis

版本号

org.mybatis

mybatis-spring

版本号

org.springframework

spring-context

版本号

org.springframework

spring-jdbc

版本号

tk.mybatis

mapper

版本号

第二步:开始集成

XML配置有以下两种方式,选其一

使用MapperScannerConfigurer

tk.mybatis.spring.mapper.MapperScannerConfigurer代替org.mybatis.spring.mapper.MapperScannerConfigurer

...

使用Configuration

如果某些第三方也需要特殊的 MapperScannerConfigurer 时,就不能用上面的方式进行配置了,此时使用以下方法

notEmpty=true

这里使用了 tk.mybatis.mapper.session.Configuration ,就是不能通过读取 mybatis-config.xml进行配置,直接使用 Spring setter 配置属性

使用java注解和代码集成方式有以下三种

使用@MapperScan的properties属性

@Configuration

@MapperScan(value = "tk.mybatis.mapper.annotation",

properties = {

"mappers=tk.mybatis.mapper.common.Mapper",

"notEmpty=true"

}

)

public class MyBatisConfigProperties {

}

使用@MapperScan的mapperHelperRef 属性

@Configuration

@MapperScan(value = "tk.mybatis.mapper.annotation", mapperHelperRef = "mapperHelper")

public static class MyBatisConfigRef {

//其他

@Bean

public MapperHelper mapperHelper() {

Config config = new Config();

List mappers = new ArrayList();

mappers.add(Mapper.class);

config.setMappers(mappers);

MapperHelper mapperHelper = new MapperHelper();

mapperHelper.setConfig(config);

return mapperHelper;

}

}

使用构造Configuration的方式

@Configuration

@MapperScan(value = "tk.mybatis.mapper.annotation")

public static class MyBatisConfig {

@Bean

public SqlSessionFactory sqlSessionFactory() throws Exception {

SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

sessionFactory.setDataSource(dataSource());

//tk.mybatis.mapper.session.Configuration

Configuration configuration = new Configuration();

//可以对 MapperHelper 进行配置后 set

configuration.setMapperHelper(new MapperHelper());

//设置为 tk 提供的 Configuration

sessionFactory.setConfiguration(configuration);

return sessionFactory.getObject();

}

}

spring boot集成

基于 starter 的自动配置

tk.mybatis

mapper-spring-boot-starter

版本号

在 starter 的逻辑中,如果你没有使用 @MapperScan 注解,你就需要在你的接口上增加 @Mapper注解,否则 MyBatis 无法判断扫描哪些接口。所以需要手动在所有接口上增加 @Mapper 注解

在yml中配置属性

mapper:

mappers:

- tk.mybatis.mapper.common.Mapper

notEmpty: true

基于MapperScan注解

@tk.mybatis.spring.annotation.MapperScan(basePackages = "扫描包")

@SpringBootApplication

public class SampleMapperApplication implements CommandLineRunner {

}

三、注解详解

@NameStyle注解(Mapper)

可以设置实体类字段与表字段的转换方式,可选值

public enum Style {

normal, //原值

camelhump, //驼峰转下划线,默认值

uppercase, //转换为大写

lowercase, //转换为小写

camelhumpAndUppercase, //驼峰转下划线大写形式

camelhumpAndLowercase, //驼峰转下划线小写形式

}

@Table注解(JPA)

作用:建立实体类和数据库表之间的对应关系。

默认规则: 实体类类名首字母小写作为表名。 Employee 类→employee 表

用法: 在@Table注解的 name属性中指定目标数据库表的表名

@Column注解(JPA)

作用:建立实体类字段和数据库表字段之间的对应关系。

默认规则:实体类字段:驼峰式命名。数据库表字段:使用“_”区分各个单词

用法:在@Column 注解的name 属性中指定目标字段的字段名

@ColumnType注解(Mapper)

主要用于枚举属性,其中column属性和 @Column 中的 name 作用相同,但是 @Column的优先级更高。除了 name 属性外,这个注解主要提供了 jdbcType 属性和 typeHandler 属性。

jdbcType 用于设置特殊数据库类型时指定数据库中的 jdbcType。

typeHandler 用于设置特殊类型处理器,常见的是枚举。

@Transient注解(JPA)

用于标记不与数据库表字段对应的实体类字段。对于类中的复杂对象,以及 Map,List 等属性不需要配置这个注解。

主键相关注解

主键策略

自增类型的

使用@Id配合@KeySql,偏向Mybatis写法

@Id

@KeySql(useGeneratedKeys = true)//或者@GeneratedValue(generator = "JDBC")

private Integer id;

使用@Id配合@KeySql,偏向JPA写法

@Id

@KeySql(dialect = IdentityDialect.DEFAULT)//或者直接指定数据库方言dialect = IdentityDialect.MYSQL

private Integer id;

使用@GeneratedValue

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

通过序列和任意 SQL 获取主键值

使用@Id配合@KeySql

@Id

@KeySql(sql = "select SEQ_ID.nextval from dual", order = ORDER.BEFORE)

private Integer id;

使用@GeneratedValue

@Id

@GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "select SEQ_ID.nextval from dual")

private Integer id;

@Verson注解(Mapper)

@Verson注解已经不常用,以下只是简单说明:

在使用乐观锁时,由于通用 Mapper 是内置的实现,不是通过拦截器方式实现的,如果版本不一致可能执行影响数为0,但不会抛出异常,所以在 Java6,7中使用时,你需要自己在调用方法后进行判断是否执行成功。

//Java8+可以使用默认方法判断自动抛出异常

public interface MyMapper extends Mapper {

default int deleteWithVersion(T t){

int result = delete(t);

if(result == 0){

throw new RuntimeException("删除失败!");

}

return result;

}

default int updateByPrimaryKeyWithVersion(Object t){

int result = updateByPrimaryKey(t);

if(result == 0){

throw new RuntimeException("更新失败!");

}

return result;

}

}

@RegisterMapper 注解

作用: 通用 Mapper 检测到该接口被继承时,会自动注册。否则需要配置扫描参数。

四、全局主键

第一步:实现GenId接口,实现主键生成策略

//用UUID实现主键生成策略

public class UUIDGenId implements GenId {

@Override

public String genId(String table, String column) {

return UUID.randomUUID().toString().replace("-","");

}

}

第二步:配置

public class User {

@Id

@KeySql(genId = UUIDGenId.class)

private String id;

private String name;

private String code;

public User() {

}

public User(String name, String code) {

this.name = name;

this.code = code;

}

//省略 setter 和 getter

}

如果你使用了@KeySql提供的其他方式,genId就不会生效,genId 是所有方式中优先级最低的

五、常用配置

指配置通用mapper的位置,以xml方式为例

valueXXX

mappers

​ 4.0 之后,增加了一个 @RegisterMapper 注解,通用 Mapper 中提供的所有接口都有这个注解,有了该注解后,通用 Mapper 会自动解析所有的接口,如果父接口(递归向上找到的最顶层)存在标记该注解的接口,就会自动注册上。因此 4.0 后使用通用 Mapper 提供的方法时,不需要在配置这个参数。当自己扩展通用接口时,建议加上该注解,否则就要配置 mappers 参数。

IDENTITY

​ 取回主键的方式,可以配置的值为所列的数据库类型,例如MYSQL: SELECT LAST_INSERT_ID() ,配置为IDENTITY=MYSQL

ORDER

​ 用于配置何时获取主键中的order属性,可选值为BEFORE和AFTER

catalog、schema

​ 数据库的catalog,如果设置该值,查询的时候表名会带catalog设置的前缀

​ schema同catalog,catalog优先级高于schema

enumAsSimpleType

​ 用于配置是否将枚举类型当成基本类型对待。默认 simpleType 会忽略枚举类型,使用 enumAsSimpleType 配置后会把枚举按简单类型处理,需要自己配置好 typeHandler。

​ 配置方式如下:enumAsSimpleType=true

checkExampleEntityClass

​ 用于校验通用 Example 构造参数 entityClass 是否和当前调用的 Mapper类型一致,默认 false 。配置该字段为 true 后就会对不匹配的情况进行校验。

safeDelete、safeUpdate

​ 配置为 true 后,delete 和 deleteByExample 都必须设置查询条件才能删除,否则会抛出异常

​ 配置为 true 后,updateByExample 和 updateByExampleSelective 都必须设置查询条件才能更新,否则会抛出异常

六、代码生成器

第一步:引入MBG依赖

org.mybatis.generator

mybatis-generator-core

版本号

test

第二步:配置generatorConfig.xml

connectionURL="jdbc:mysql://192.168.10.191:3306/test"

userId="root"

password="123456">

enableCountByExample="false"

enableUpdateByExample="true"

enableDeleteByExample="true"

enableSelectByExample="true"

selectByExampleQueryId="true">

第三步:以java代码的方式运行MBG

public class MybatisGenerator{

public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {

InputStream configFile = MybatisGenerator.class.getResourceAsStream("/generatorConfig.xml");

System.out.println(configFile);

List warnings = new ArrayList();

boolean overwrite = true;

ConfigurationParser cp = new ConfigurationParser(warnings);

Configuration config = cp.parseConfiguration(configFile);

DefaultShellCallback callback = new DefaultShellCallback(overwrite);

MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);

ProgressCallback progress = new VerboseProgressCallback();

myBatisGenerator.generate(progress);

}

}

七、扩展通用接口

以自定义的SelectAll为例,演示创建通用接口步骤。

第一步:创建接口,接口使用注解@RegisterMapper使通用mapper扫描,使用注解@SelectProvider指定驱动类,method = "dynamicSQL"为固定写法。

@RegisterMapper

public interface SelectAllMapper {

// 查询全部结果

@SelectProvider(type = MySelectProvider.class, method = "dynamicSQL")

List selectAll();

}

第二步:实现驱动类,注意驱动类继承MapperTemplate,创建的方法名需要与接口的方法名同名。

public class MySelectProvider extends MapperTemplate {

public BaseSelectProvider(Class mapperClass, MapperHelper mapperHelper) {

super(mapperClass, mapperHelper);

}

// 查询全部结果

public String selectAll(MappedStatement ms) {

final Class entityClass = getEntityClass(ms);

//修改返回值类型为实体类型

setResultType(ms, entityClass);

StringBuilder sql = new StringBuilder();

sql.append(SqlHelper.selectAllColumns(entityClass));

sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));

sql.append(SqlHelper.orderByDefault(entityClass));

return sql.toString();

}

}

注:可映射驱动注解类型

@SelectProvider —— 查询驱动类型,返回值为其查询结果集合

@UpdateProvider —— 更新驱动类型,返回值为影响记录行数

@InsertProvider —— 插入驱动类型,返回值为影响记录行数,主键填充在传入的参数中

@DeleteProvider —— 删除驱动类型,返回值为影响记录行数

注:对开发自定义有帮助的类

MapperTemplate —— 通用Mapper模板类,扩展通用Mapper时需要继承该类

EntityHelper —— 实体类工具类,处理实体和数据库表以及字段关键的一个类

FieldHelper —— 类字段工具类

SqlHelper —— 拼常用SQL的工具类

MapperHelper —— 获取通用mapper的所有配置

自定义Insert Ignore插入

@RegisterMapper

public interface InsertIgnoreSelectiveMapper {

@InsertProvider(type = InsertIgnoreSelectiveProvider.class, method = "dynamicSQL")

int insertIgnoreSelective(T record);

}

public class InsertIgnoreSelectiveProvider extends MapperTemplate {

public InsertIgnoreSelectiveProvider(Class mapperClass, MapperHelper mapperHelper) {

super(mapperClass, mapperHelper);

}

public String insertIgnoreSelective(MappedStatement ms) {

Class entityClass = getEntityClass(ms);

StringBuilder sql = new StringBuilder();

//获取全部列

Set columnList = EntityHelper.getColumns(entityClass);

EntityColumn logicDeleteColumn = SqlHelper.getLogicDeleteColumn(entityClass);

processKey(sql, entityClass, ms, columnList);

sql.append("INSERT IGNORE INTO ");

sql.append(SqlHelper.getDynamicTableName(entityClass, tableName(entityClass)));

sql.append(" ");

sql.append("");

for (EntityColumn column : columnList) {

if (!column.isInsertable()) {

continue;

}

if (column.isIdentity()) {

sql.append(column.getColumn()).append(",");

} else {

if (logicDeleteColumn != null && logicDeleteColumn == column) {

sql.append(column.getColumn()).append(",");

continue;

}

sql.append(SqlHelper.getIfNotNull(column, column.getColumn() + ",", isNotEmpty()));

}

}

sql.append("");

sql.append("");

for (EntityColumn column : columnList) {

if (!column.isInsertable()) {

continue;

}

if (logicDeleteColumn != null && logicDeleteColumn == column) {

sql.append(SqlHelper.getLogicDeletedValue(column, false)).append(",");

continue;

}

//优先使用传入的属性值,当原属性property!=null时,用原属性

//自增的情况下,如果默认有值,就会备份到property_cache中,所以这里需要先判断备份的值是否存在

if (column.isIdentity()) {

sql.append(SqlHelper.getIfCacheNotNull(column, column.getColumnHolder(null, "_cache", ",")));

} else {

//其他情况值仍然存在原property中

sql.append(SqlHelper.getIfNotNull(column, column.getColumnHolder(null, null, ","), isNotEmpty()));

}

//当属性为null时,如果存在主键策略,会自动获取值,如果不存在,则使用null

//序列的情况

if (column.isIdentity()) {

sql.append(SqlHelper.getIfCacheIsNull(column, column.getColumnHolder() + ","));

}

}

sql.append("");

return sql.toString();

}

private void processKey(StringBuilder sql, Class entityClass, MappedStatement ms, Set columnList){

//Identity列只能有一个

Boolean hasIdentityKey = false;

//先处理cache或bind节点

for (EntityColumn column : columnList) {

if (column.isIdentity()) {

//这种情况下,如果原先的字段有值,需要先缓存起来,否则就一定会使用自动增长

//这是一个bind节点

sql.append(SqlHelper.getBindCache(column));

//如果是Identity列,就需要插入selectKey

//如果已经存在Identity列,抛出异常

if (hasIdentityKey) {

//jdbc类型只需要添加一次

if (column.getGenerator() != null && "JDBC".equals(column.getGenerator())) {

continue;

}

throw new MapperException(ms.getId() + "对应的实体类" + entityClass.getCanonicalName() + "中包含多个MySql的自动增长列,最多只能有一个!");

}

//插入selectKey

SelectKeyHelper.newSelectKeyMappedStatement(ms, column, entityClass, isBEFORE(), getIDENTITY(column));

hasIdentityKey = true;

} else if(column.getGenIdClass() != null){

sql.append("

sql.append("_parameter").append(", '").append(column.getProperty()).append("'");

sql.append(", @").append(column.getGenIdClass().getCanonicalName()).append("@class");

sql.append(", '").append(tableName(entityClass)).append("'");

sql.append(", '").append(column.getColumn()).append("')");

sql.append("\"/>");

}

}

}

}

八、Example 用法

一、使用MBG生成的Example子类

CountryExample example = new CountryExample();

example.createCriteria().andCountrynameLike("A%");

example.or().andIdGreaterThan(100);

example.setDistinct(true);

int count = mapper.deleteByExample(example);

二、使用原始的Example类

Example example = new Example(Country.class);

example.setForUpdate(true);

example.createCriteria().andGreaterThan("id", 100).andLessThan("id",151);

example.or().andLessThan("id", 41);

List countries = mapper.selectByExample(example);

三、Example.builder 方式

Example example = Example.builder(Country.class)

.select("countryname")

.where(Sqls.custom().andGreaterThan("id", 100))

.orderByAsc("countrycode")

.forUpdate()

.build();

List countries = mapper.selectByExample(example);

四、Weekend 方式

List selectByWeekendSql = mapper.selectByExample(

Example.Builder(Country.class)

.where(WeekendSqls.custom()

.andLike(Country::getCountryname, "%a%")

.andGreaterThan(Country::getCountrycode, "123"))

.build());

九、TypeHandler用法

例如:数据库存放的用户地址信息为Hebei/Shijiazhuang、Hebei/Handan

第一步:创建实体类

public class Address implements Serializable {

private static final long serialVersionUID = 1L;

private String province;

private String city;

// 省略Getter、Setter方法。。。

@Override

public String toString() {

StringBuilder builder = new StringBuilder();

if(province != null && province.length() > 0){

builder.append(province);

}

if(city != null && city.length() > 0){

builder.append("/").append(city);

}

return builder.toString();

}

}

public class User implements Serializable {

private static final long serialVersionUID = 1L;

@Id

private Integer id;

private String name;

@ColumnType(typeHandler = AddressTypeHandler.class) //指定TypeHandler

private Address address; //实体类对应的地址

}

第二步:创建对应的TypeHandler

public class AddressTypeHandler extends BaseTypeHandler

{

@Override

public void setNonNullParameter(PreparedStatement ps, int i, Address parameter,

JdbcType jdbcType) throws SQLException {

ps.setString(i, parameter.toString());

}

private Address convertToAddress(String addressStr){

if(addressStr == null || addressStr.length() == 0){

return null;

}

String[] strings = addressStr.split("/");

Address address = new Address();

if(strings.length > 0 && strings[0].length() > 0){

address.setProvince(strings[0]);

}

if(strings.length > 1 && strings[1].length() > 0){

address.setCity(strings[1]);

}

return address;

}

@Override

public Address getNullableResult(ResultSet rs, String columnName) throws SQLException {

return convertToAddress(rs.getString(columnName));

}

@Override

public Address getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

return convertToAddress(rs.getString(columnIndex));

}

@Override

public Address getNullableResult(CallableStatement cs, int columnIndex)

throws SQLException {

return convertToAddress(cs.getString(columnIndex));

}

}

注:上述为局部使用方法、也可以在mybatis-config.xml中配置全局的类型处理器

并在实体类中配置@Column使mybtais将复杂类型作为普通类型向数据库对应字段进行映射

@Table(name = "user")

public class User implements Serializable {

private static final long serialVersionUID = 1L;

@Id

private Integer id;

private String name;

@Column

private Address address;

//省略 setter 和 getter

}

元旦为什么定在那一天?

北京这座全新摩天轮“开转”!每个吊厢可乘坐6人

最新发表 newmodule
友情链接 newmodule