官方文档地址:https://github.com/abel533/Mapper
依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>版本号</version>
</dependency>
对象关系映射
简单映射
数据库表的字段名和实体类的属性名相同,这时候只需要给主键字段添加 @Id
标识即可
复杂映射
通常情况下,数据库表的字段名和实体类的属性名并不完全相同,数据库表的字段名用下划线分隔单词,而Java实体类使用驼峰命名法,
通用 Mapper 中,默认情况下是将实体类字段按照驼峰转下划线形式的表名列名进行转换。
比如数据库字段名为 user_name,映射到实体类属性名为 userName
注解
通用 Mapper 使用了 JPA 的注解和自己定义的注解做对象关系映射,下面对这些注解一一介绍
@NameStyle
注解(Mapper)
这个注解作用在实体类上,优先级高于对应的 style
全局配置。
注解支持以下几个选项:
- normal:原值
- camelhump:驼峰转下划线
- uppercase:转换为大写
- lowercase:转换为小写
- camelhumpAndUppercase:驼峰转下划线大写形式
- camelhumpAndLowercase:驼峰转下划线小写形式
使用时直接在类上配置即可,例如:
@NameStyle(Style.camelhumpAndUppercase)
public class Country
配置该注解后,通用 Mapper 在动态生成 sql 的时候,会将形如 userName
的属性转换为表中的 USER_NAME
字段
@Table
注解(JPA)
@Table
注解可以配置 name
,catalog
和schema
三个属性,配置 name
属性后,直接使用提供的表名,不再根据实体类名进行转换。
其他两个配置中,同时配置时,catalog
优先级高于 schema
,也就是只有 catalog
会生效。
配置示例如下:
@Table(name = "sys_user")
public class User
将 User
实体映射到 sys_user
表。
@column
注解(JPA)
@Column
注解支持 name
,insertable
和 updateable
三个属性。
name
配置映射的列名insertable
对提供的insert
方法有效,如果设置false
就不会出现在 SQL 中upodateable
对提供的update
方法有效,设置为false
后不会出现在 SQL 中
当使用关键词时,还会有下面的用法:
@Column(name = "`order`")
private String order;
@ColumnType
注解(Mapper)
该注解是通用 Mapper 用来代替 @Column
注解的,所以它的功能会更强大,推荐直接只用该注解。
且在通用 Mapper 中,该注解的优先级也比 @Column
要高。
除了支持 @Column
中的三个属性外,该注解还提供了 jdbcType
属性和 typeHandler
属性。
jdbcType
用于设置特殊数据库类型时指定数据库中的 jdbcType
。
typeHandler
用于设置特殊类型处理器,常见的是枚举。
通用 Mapper 提供的 CRUD 方法是没有 XML 文件的,所以就不能用在 XML 中为字段配置类型处理器方法配置,所以通用 Mapper 提供了这两个属性来自定义类型处理器。
用法如下:
@ColumnType(
column = "countryname",
jdbcType = JdbcType.VARCHAR,
typeHandler = StringTypeHandler.class)
private String countryname;
@Transient
注解(JPA)
一般情况下,实体中的字段和数据库表中的字段是一一对应的,但是也有很多情况我们会在实体中增加一些额外的属性,
这种情况下,就需要使用 @Transient
注解来告诉通用 Mapper 这不是表中的字段,在生成 SQL 是就不会把这些字段添加进去。
默认情况下,只有简单类型会被自动认为是表中的字段(可以通过配置中的 useSimpleType
控制)。
这里的简单类型不包含 Java 中的8种基本类型:
byte,short,int,long,float,double,char,boolean
这是因为在类中,基本类型会有默认值,而 MyBatis 中经常会需要判断属性值是否为空,所以不要在类中使用基本类型,否则会遇到莫名其妙的错误。
对于类中的复杂对象,以及 Map
,List
等属性不需要配置这个注解。
对于枚举类型作为数据库字段的情况下,需要看配置中的 enumAsSimpleType
参数。
配置示例:
@Transient
private String otherThings; //非数据库表中字段
我感觉这个注解会用不到,因为实体类有 PO,TO,VO,DTO 之分,各司其职,存在不是数据表中字段的属性的情况下,该实体类不能作为和数据库表映射的实体类,
而是其他的比如返回给前端的实体类 VO,通过 BeanUtils.copyProperties
方法在不同类型的实体类之间复制属性。
@Id
注解(JPA)
上面几个注解都涉及到映射,@Id
注解和映射无关,它是一个特殊的标记,用于标识数据库中的关键字段。
正常情况下,一个实体类中至少需要一个标记 @Id
注解的字段,存在关联主键时可以标记多个。
如果表中没有主键,类中就可以不标记。
当类中没有标记 @Id
注解的字段时,通用 Mapper 会理解为类中的所有字段是联合主键。使用所有的 ByPrimaryKey
相关的方法时,有 where
条件的地方,会将所有列作为条件。
配置示例:
@Id
private Integer id;
或者联合注解:
@Id
private Integer userId;
@Id
private Integer roleId;
@KeySql
注解(Mapper)
主键策略主机,用于配置如何生成注解。
这是通用 Mapper 的自定义注解,该注解的目的就是替换 @GeneratedValue
注解。
首先主键策略和数据库关系很大,有些数据库支持主键自增,而有些数据库只能通过序列来获得。
这里介绍支持主键自增的 Mysql 数据库,更详细的内容请看官网。
这种情况下,配置主键策略最简单。
用法如下:
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
对应的 XML 代码:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into country (id, countryname, countrycode)
values (#{id},#{countryname},#{countrycode})
</insert>
@KeySql
注解源码:
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface KeySql {
/**
* 是否使用 JDBC 方式获取主键,优先级最高,设置为 true 后,不对其他配置校验
*
* @return
*/
boolean useGeneratedKeys() default false;
/**
* 优先级第二,根据配置的数据库类型取回主键,忽略其他配置
*
* @return
*/
IdentityDialect dialect() default IdentityDialect.NULL;
/**
* 取主键的 SQL
*
* @return
*/
String sql() default "";
/**
* 生成 SQL,初始化时执行,优先级低于 sql
*
* @return
*/
Class<? extends GenSql> genSql() default GenSql.NULL.class;
/**
* 和 sql 可以配合使用,默认使用全局配置中的 ORDER
*
* @return
*/
ORDER order() default ORDER.DEFAULT;
/**
* Java 方式生成主键,可以和发号器一类的服务配合使用
*
* @return
*/
Class<? extends GenId> genId() default GenId.NULL.class;
}
通过上面的注解,大家可以看到主要的 3 个参数的优先级,useGenerateKeys
优先级最高,其次是 dialect
,最后是 sql
。
其中 order
只对 sql
方式有效。
全局主键
使用 genId
属性可以实现自定义主键生成的方式,也就是全局主键。
由源码可以看出该属性是一个 Class 类型,且必须是 GenId 接口的实现类,我们看一下 GenId 接口的源码:
public interface GenId<T> {
class NULL implements GenId {
@Override
public Object genId(String table, String column) {
throw new UnsupportedOperationException();
}
}
T genId(String table, String column);
}
通过接口方法可以看出,在生成 Id 时,可以得到当前的表名和列名,我们可以使用这两个信息生成和表字段关联的 Id 信息,
也可以完全不使用这两个信息,生成全局的 Id。
使用 UUID 方式时,首先我们需要提供一个能产生 UUID 的实现类:
public class UUIdGenId implements GenId<String> {
@Override
public String genId(String table, String column) {
return UUID.randomUUID().toString();
}
}
我们只需要在注解中配置 @KeySql(genId = UUIDGenId.class)
即可,需要注意的是,如果你使用了 @keySql
提供的其他方式,
genId
就不会生效,genId
是所有方式中优先级最低的。
@Version
注解(Mapper)
@Version
是实现乐观锁的一个注解。
乐观锁实现中,要求一个实现类中只能有一个乐观锁字段。
想要使用乐观锁,只需要在实体类中,给乐观锁字段增加 @tk.mybatis.mapper.annotation.Version
注解。
例如:
public class User {
private Long id;
private String name;
//...
@Version
private Integer version;
//setter and getter
}
@Version
注解有一个 nextVersion
属性,默认值为默认的实现,默认实现如下:
package tk.mybatis.mapper.version;
import java.sql.Timestamp;
/**
* @author liuzh
* @since 3.5.0
*/
public class DefaultNextVersion implements NextVersion {
@Override
public Object nextVersion(Object current) throws VersionException {
if (current == null) {
throw new VersionException("当前版本号为空!");
}
if (current instanceof Integer) {
return (Integer) current + 1;
} else if (current instanceof Long) {
return (Long) current + 1L;
} else if (current instanceof Timestamp) {
return new Timestamp(System.currentTimeMillis());
} else {
throw new VersionException("默认的 NextVersion 只支持 Integer, Long" +
" 和 java.sql.Timestamp 类型的版本号,如果有需要请自行扩展!");
}
}
}
默认实现支持 Integer
、Long
和 java.sql.Timestamp
,如果默认实现不能满足自己的需要,可以实现自己的方法,在配置注解时指定自己的实现即可。
支持的方法:
- delete
- deleteByPrimaryKey
- updateByPrimaryKey
- updateByPrimaryKeySelective
- updateByExample
- updateByExampleSelective
这些方法在执行时会更新乐观锁字段的值或者使用乐观锁的值作为查询条件。
总结
该篇博客内容抄的通用Mapper官方的内容,只抄了常用的内容,并对内容排版做了修改。
博客的目的主要是为了加深记忆,文章里面也添加了一些自己的一些看法。介绍的注解只有 8 个,这 8 个是常用注解,也看出通用Mapper的使用非常简单。