2.3 主键策略
在 2.2 中介绍了所有其他注解的作用和用法,由于主键策略比较复杂,单独用一节来阐述。
首先主键策略和数据库关系很大,有些数据库支持主键自增,而有些数据库只能通过序列来获得。
新增的@KeySql
注解用于替换 @GeneratedValue
注解,因此 @KeySql
能以更简单方式实现原来的功能,下面的示例都先使用 @KeySql
进行配置,然后在使用 @GeneratedValue
,大家可以自己选择。
2.3.1 JDBC 支持通过 getGeneratedKeys
方法取回主键的情况
这种情况首先需要数据库支持自增,其次数据库提供的 JDBC 支持 getGeneratedKeys
方法。
常见的如 MySql,SqlServer 支持这种模式。
这种情况下,配置主键策略最简单。
用法如下:
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
或:
@Id
@GeneratedValue(generator = "JDBC")
private Long id;
为了让大家容易理解这里配置和 MyBatis 写法的关系,大家可以看看对应生成的 XML 代码:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into country (id, countryname, countrycode)
values (#{id},#{countryname},#{countrycode})
</insert>
SqlServer 中使用时,需要设置 id 的
insertable=false
2.3.2 支持自增的数据库
支持自增的数据库列表如下:
- DB2:
VALUES IDENTITY_VAL_LOCAL()
- MYSQL:
SELECT LAST_INSERT_ID()
- SQLSERVER:
SELECT SCOPE_IDENTITY()
- CLOUDSCAPE:
VALUES IDENTITY_VAL_LOCAL()
- DERBY:
VALUES IDENTITY_VAL_LOCAL()
- HSQLDB:
CALL IDENTITY()
- SYBASE:
SELECT @@IDENTITY
- DB2_MF:
SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1
- INFORMIX:
select dbinfo('sqlca.sqlerrd1') from systables where tabid=1
这个列表只是列举了比较常见的数据库,其他类似的也支持。
列表数据库名字后面对应的 SQL 是插入后取 id 的 SQL 语句。
这类数据库主键策略配置示例如下:
@Id
//DEFAULT 需要配合 IDENTITY 参数(ORDER默认AFTER)
@KeySql(dialect = IdentityDialect.DEFAULT)
private Integer id;
//建议直接指定数据库
@Id
@KeySql(dialect = IdentityDialect.MYSQL)
private Integer id;
或:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@GeneratedValue
用法还要配合 IDENTITY 参数(以及 ORDER 参数),这个参数值需要配置为对应的数据库,就是上面列表中的名字,具体的配置方法看后面章节的内容。
这种配置对应的 XML 形式为:
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into country (id, countryname, countrycode)
values (#{id},#{countryname},#{countrycode})
</insert>
IDENTITY 参数以及 ORDER 参数会决定 selectKey
中的 SQL 和 order
属性的值(这里是 AFTER
)。
2.3.3 通过序列和任意 SQL 获取主键值
像 Oracle 中通过序列获取主键就属于这种情况,实际上 2.3.2 中的 SQL 也可以在这里配置。
除了类似序列获取值外,还可以是获取 UUID 的 SQL 语句,例如 select uuid()
。
2.3.3 和 2.3.2 的区别主要在于,2.3.2 是插入表之后才有 id 的值,2.3.3 是插入数据库前需要获取一个值作为主键。
在 Oracle 中,我们可以用下面的方式进行配置:
@Id
@KeySql(sql = "select SEQ_ID.nextval from dual", order = ORDER.BEFORE)
private Integer id;
或:
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY,
generator = "select SEQ_ID.nextval from dual")
private Integer id;
使用 @GeneratedValue
时也要配置一个 ORDER 全局参数,2.3.2 中提到的 AFTER
,在 2.3.3 中需要配置为 BEFORE
,具体配置方式看后面章节的内容。
这种配置对应的 XML 代码如下:
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select SEQ_ID.nextval from dual
</selectKey>
insert into country (id, countryname, countrycode)
values (#{id},#{countryname},#{countrycode})
</insert>
这种用法中,
values
中必须出现主键的值,否则就插不到数据库。
除了 Oracle 这种外,还有一种更普遍的用法,就是使用 UUID。
例如下面的配置:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY,generator = "select uuid()")
private String id;
注意 SQL 返回值类型和这里接收的类型(
String
)一致。
2.3.4 @KeySql
介绍
上面的例子中都列举了 @KeySql
方式的用法。下面全面的看看这个注解:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface KeySql {
/**
* 是否使用 JDBC 方式获取主键,优先级最高,设置为 true 后,不对其他配置校验
*
* @return
*/
boolean useGeneratedKeys() default false;
/**
* 优先级第二,根据配置的数据库类型取回主键,忽略其他配置
*
* @return
*/
IdentityDialect dialect() default IdentityDialect.DEFAULT;
/**
* 取主键的 SQL
*
* @return
*/
String sql() default "";
/**
* 和 sql 可以配合使用,默认使用全局配置中的 ORDER
*
* @return
*/
ORDER order() default ORDER.DEFAULT;
}
通过上面的注释,大家可以看到主要的 3 个参数的优先级,useGeneratedKeys
优先级最高,其次是 dialect
,最后是 sql
。其中 order
只对 sql
方式有效。
useGeneratedKeys
和 dialect
相当于预设的基本用法,和数据库以及驱动紧密相关。
sql
和 order
更灵活。