通用 Mapper 中,默认情况下是将实体类字段按照驼峰转下划线小写形式的表名列名进行转换。
实体类的 userName 可以映射到表的 user_name 上。
如果想要修改默认的转换方式,可在properties配置中,修改 style 全局配置。
数据库映射主要涉及到一些注解和全局配置。
通用 Mapper 默认使用了几个简单的注解,其他 JPA 的注解默认并不支持,但是如果你开发自己的通用方法,你可以使用 JPA 注解或者引入自己的注解。
@NameStyle
注解(Mapper)
这个注解可以在类上进行配置,优先级高于对应的 style
全局配置。
注解支持以下几个选项:
normal, //原值
camelhump, //驼峰转下划线
uppercase, //转换为大写
lowercase, //转换为小写
camelhumpAndUppercase, //驼峰转下划线大写形式
camelhumpAndLowercase, //驼峰转下划线小写形式
使用时,直接在类上配置即可,例如:
@NameStyle(Style.camelhumpAndUppercase)
public class Country
配置该注解后,对该类和其中的字段进行转换时,会将形如 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 中。
updateable
对提供的 update
方法有效,设置为 false
后不会出现在 SQL 中。
配置示例如:
@Column(name = "user_name")
private String name;
除了直接映射 name
到 user_name
这种用法外,在使用关键字的情况,还会有下面的用法:
@Column(name = "`order`")
private String order;
对于关键字这种情况,通用 Mapper 支持自动转换,可以对 wrapKeyword 配置。
@ColumnType
注解(Mapper)
这个注解提供的 column
属性和 @Column
中的 name
作用相同。但是 @Column
的优先级更高。
除了 name
属性外,这个注解主要提供了 jdbcType
属性和 typeHandler
属性。
jdbcType
用于设置特殊数据库类型时指定数据库中的 jdbcType
。
typeHandler
用于设置特殊类型处理器,常见的是枚举。具体解析参考:https://mp.csdn.net/console/editor/html/94832834
用法示例如下:
@ColumnType(
column = "countryname",
jdbcType = JdbcType.VARCHAR,
typeHandler = StringTypeHandler.class)
private String countryname;
@Transient
注解(JPA)
一般情况下,实体中的字段和数据库表中的字段是一一对应的,但是也有很多情况我们会在实体中增加一些额外的属性,这种情况下,就需要使用 @Transient
注解来告诉通用 Mapper 这不是表中的字段。
默认情况下,只有简单类型会被自动认为是表中的字段(可以通过配置中的 useSimpleType
控制)。
这里的简单类型不包含 Java 中的8种基本类型:
byte,short,int,long,float,double,char,boolean
这是因为在类中,基本类型会有默认值,而 MyBatis 中经常会需要判断属性值是否为空,所以不要在类中使用基本类型,否则会遇到莫名其妙的错误。
对于类中的复杂对象,以及 Map
,List
等属性不需要配置这个注解。
对于枚举类型作为数据库字段的情况,需要看配置中的 enumAsSimpleType
参数。
配置示例:
@Transient
private String otherThings; //非数据库表中字段
@Id
注解(JPA)
上面几个注解都涉及到映射。 @Id
注解和映射无关,它是一个特殊的标记,用于标识数据库中的主键字段。
正常情况下,一个实体类中至少需要一个标记 @Id
注解的字段,存在联合主键时可以标记多个。
主键最好是数字(自增),不要使用联合主键,可以使用唯一约束来控制
如果表中没有主键,类中就可以不标记。
当类中没有存在标记 @Id
注解的字段时,你可以理解为类中的所有字段是联合主键。使用所有的 ByPrimaryKey
相关的方法时,有 where
条件的地方,会将所有列作为条件。
配置示例:
@Id
private Integer id;
或者联合主键:
@Id
private Integer userId;
@Id
private Integer roleId;
@KeySql
注解
主键策略注解,用于配置如何生成主键。
这是通用 Mapper 的自定义注解,改注解的目的就是替换 @GeneratedValue
注解。
关于该注解的用法可以查看 :https://mp.csdn.net/console/editor/html/81041141。
@GeneratedValue
注解(JPA)
主键策略注解,用于配置如何生成主键。
关于该注解的用法可以查看 :https://mp.csdn.net/console/editor/html/81041141。
推荐使用上面的 @KeySql
注解。
@Version
注解(Mapper)
@Version
是实现乐观锁的一个注解,乐观锁实现中,要求一个实体类中只能有一个乐观锁字段。
想要使用乐观锁,只需要在实体中,给乐观锁字段增加 @tk.mybatis.mapper.annotation.Version
注解。
public class User {
private Long id;
private String name;
@Version
private Integer version;
}
@Version
注解有一个 nextVersion
属性,默认值为默认的实现,默认实现如下:
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 是内置的实现,不是通过 拦截器 方式实现的,因此当执行上面支持的方法时,如果版本不一致,那么执行结果影响的行数可能就是 0。这种情况下也不会报错!
所以在 Java6,7中使用时,你需要自己在调用方法后进行判断是否执行成功。
在 Java8+ 中,可以通过默认方法来增加能够自动报错(抛异常)的方法,例如:
public interface MyMapper<T> extends Mapper<T> {
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 中最常见的一个错误而增加的标记注解,该注解仅用于开发的通用接口,不是实体类上使用的,这里和其他注解一起介绍了。4.0 版本提供的所有通用接口上都标记了该注解,因此自带的通用接口时,不需要配置 mappers
参数,该注解的具体用法查询:https://mp.csdn.net/console/editor/html/80976552
自定义注解
在通用 Mapper 中,可以通过 EntityHelper.getColumns(entityClass)
方法来获取实体类的全部信息。
在 EntityColumn
中,通过下面的代码可以获取字段上的任意注解。
//判断是否有某个注解
boolean hasVersion = column.getEntityField().isAnnotationPresent(Version.class)
//通过下面的代码可以获取注解信息
Version version = column.getEntityField().getAnnotation(Version.class);
通过这种方式,在实现自己的通用方式时,可以根据需要来增加额外的注解来实现一些其他的用途。