业务场景
主键回填听上去很高级,不妨用一个业务场景来引入概念吧。
在增加操作中,如果设置了主键id是自增的,那么插入数据时id的值一般写null,因为存入数据库时id都会自动+1,看似操作没毛病,非常完美。
但是在购物交易的添加模块这可不兴这样,细想一下在上网买东西时,用户付完钱之后一般都会返回一个订单编号,其实这个订单编号在数据库中就是主键,但是因为在插入数据时id的值写了null,所以订单编号也为null,也就是说返回不了订单编号的数据。
那还是老老实实挨个挨个在插入数据的时候写id呗,如果是批量添加数据呢,面对千万级的插入数据,这显然是不科学滴~
那要不先插入数据再进行查询操作吧,保证能查到数据,emm…有点麻烦,可以但没必要,有需求就有解决方案,开发者早就想到了,我们只要坐享其成就行啦~
主键回填就是用来干这个的,讲完这个业务场景在来思考一下这个专有名词,“主键”+“回填”,谜底就在谜面上,作用就是把插入数据的null主键给填回去,完美实现插入数据为null主键的同时可以查询到主键信息的需求~~
主键回填是什么?
主键回填一般用于增加操作中,把插入数据时插入为null的主键id数据填回去,存入到java对象和主键对应的属性中(数据库主键字段为id则回填的是实体类的id属性),实现添加+查询主键一步到位。
主键回填的用法
有两种,一种是自增主键,一种是非自增主键。
1、自增主键(int整数类型)
最常见的一种,平常我们的主键都是设置为自增,自增为整数类型的,每添加一个数据到数据库中,数据库就会根据自增规则给主键+1
完成主键回填的过程中,映射文件使用到的标签、函数:
1、last_insert_id():mysql 中的函数,作用是获取最近一次插入语句(insert)的自增字段值,即获取最后插入的自增id值
select last_insert_id():表示查询最后一次插入语句(insert)的自增字段值
2、< selectKey > sql语句 < /selectKey >:选择主键标签,嵌套在插入语句中,内部属性如下
(1)order:sql语句在插入语句中的执行顺序,order="AFTER"在这里表示先执行完插入语句后再执行select last_insert_id()语句,因为测试类中插入id属性为null,如果不先执行插入语句,select last_insert_id()语句将查询不到最后的插入语句。
(2)resultType:sql语句执行后的结果类型,resultType="int"在这里表示结果类型为整数类型。
(3)keyProperty:sql语句执行后回填到哪个属性,keyProperty="id"在这里表示通过执行select last_insert_id()把查询到的id值回填到测试类中插入数据的那个空的id属性值。
<selectKey order="AFTER" resultType="int" keyProperty="id">
select last_insert_id()
</selectKey>
话不多说,上代码!!!
使用步骤
1、在mybatis_shine数据库中新建一个t_user表,字段如下
create database mybatis_shine default charset =utf8;
create table t_user(
id int primary key auto_increment,
username varchar(50),
password varchar(50),
gender tinyint,
regist_time datetime
)default charset =utf8;
2、新建实体类User类
public class User {
private Integer id;
private String username;
private String password;
private Boolean gender;
private Date registTime;
//构造函数
public User() {
}
public User(Integer id, String username, String password, Boolean gender, Date registTime) {
this.id = id;
this.username = username;
this.password = password;
this.gender = gender;
this.registTime = registTime;
}
//get、set方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getGender() {
return gender;
}
public void setGender(Boolean gender) {
this.gender = gender;
}
public Date getRegistTime() {
return registTime;
}
public void setRegistTime(Date registTime) {
this.registTime = registTime;
}
//重写toString()方法,方便测试打印
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", gender=" + gender +
", registTime=" + registTime +
'}';
}
}
3、新建UserDao接口
public interface UserDao {
//添加操作接口
Integer insertUser(User user);
}
4、新建UserMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxxx.mybatis.dao.UserDao">
<insert id="insertUser" parameterType="User">
<selectKey order="AFTER" resultType="int" keyProperty="id">
select last_insert_id()
</selectKey>
insert into t_user
values (#{id} , #{username} , #{password} , #{gender} , #{registTime})
</insert>
5、测试类
public class TestMyBatis {
public static void main(String[] args) throws Exception{
//mybatis
//1、加载配置文件,获得读取配置文件的流对象
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
//2、构建 SqlSessionFactory,SqlSession连接对象的工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3、通过SqlSessionFactory工厂获得连接对象sqlSession,
SqlSession sqlSession = sqlSessionFactory.openSession();
//4、通过连接对象sqlSession获得dao对应实现类的对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
//5、调用UserDao接口中的insertUser方法并打印,添加时主键为null
User user = new User(null,"小宏","520",true,new Date());
mapper.insertUser(user);
System.out.println(user);
//6、提交事务,增删改需要
sqlSession.commit();
sqlSession.rollback();
//7、释放资源
sqlSession.close();
6、检验成果
让我们康康数据库,yeah~插入数据成功了!!!
----------分割线------------
再康康打印结果,可以看到先执行插入语句再执行select last_insert_id()语句,插入时主键id为null,但是返回结果时返回了id的数据,amazing╰( ̄▽ ̄)╭
2、非自增主键(UUID生成varchar字符串类型)
首先说一下为什么会有非自增主键吧,我们都知道自增主键唯一且自增,但是也会有亿点点小弊端的,比如说,自增主键只能保证在该系统的主键唯一,当该系统需要和新系统集成,双向同步导入数据时,很难保证原系统的id不发生主键冲突吧,那怎么办呢?不用担心,我们可以用mysql的UUID函数生成一个有效长度为32位字符串类型的全球唯一的识别码,先科普一下什么叫UUID吧。
UUID:是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
优点:出现数据拆分,合并存储数据的时候,能达到全局的唯一性
完成主键回填的过程中,映射文件使用到的标签、函数:
1、uuid():mysql的一个函数,生成一个有效长度为32位字符串类型的全球唯一的识别码,为什么说是有效长度32位,因为它的总长度为36位,其中有4位是“-”,以“-”为区分分为五组数组,前三组数字从时间戳中生成,第四组数字暂时保持时间戳的唯一性,第五组数字是一个 IEEE 802 节点标点值,保证空间唯一。使用 UUID() 函数,可以生成时间、空间上都独一无二的值。
2、replace(uuid(),’-’,’’):可以消除UUID值的“-”,采用替代的方式,将一个空值的“”替代了“-”,从而生成总长度为32位,有效长度也为32位的UUID值。
select replace(uuid(),’-’,’’):查询生成32位字符串类型的UUID值
3、< selectKey > sql语句 < /selectKey >:选择主键标签,嵌套在插入语句中,内部属性如下
(1)order:sql语句在插入语句中的执行顺序,order="BEFORE"在这里表示先执行select replace(uuid(),’-’,’’)语句后再执行插入语句,因为非自增主键id为null时无法执行插入语句,测试类的id属性不能插入null值,必须先生成uuid主键才能进行插入语句。
(2)resultType:sql语句执行后的结果类型,resultType="string"在这里表示结果类型为字符串类型。
(3)keyProperty:sql语句执行后回填到哪个属性,keyProperty="id"在这里表示通过执行select replace(uuid(),’-’,’’)生成id值回填到测试类中插入数据的那个空的id属性值。
<!--生成带“-”的UUID值,总长度36位,有效长度32位-->
<selectKey order="BEFORE" resultType="string" keyProperty="id">
select uuid()
</selectKey>
<!--把uuid的值的“-”替换成“”,总长度32位,有效长度32位-->
<selectKey order="BEFORE" resultType="string" keyProperty="id">
select replace(uuid(),'-','')
</selectKey>
多说无益,直接上代码!!!
使用步骤
1、在mybatis_shine数据库中新建一个t_student表,字段如下
create table t_student(
id varchar(32) primary key,
name varchar(50),
gender tinyint
)default charset =utf8;
2、新建实体类Student类
public class Student {
private String id;
private String name;
private Boolean gender;
public Student() {
}
public Student(String id, String name, Boolean gender) {
this.id = id;
this.name = name;
this.gender = gender;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getGender() {
return gender;
}
public void setGender(Boolean gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", gender=" + gender +
'}';
}
}
3、新建StudentDao接口
public interface StudentDao {
Integer insertStudent(Student student);
}
4、新建StudentMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxxx.mybatis.dao.StudentDao">
<insert id="insertStudent" parameterType="Student">
<selectKey order="BEFORE" resultType="string" keyProperty="id">
select replace(uuid(),'-','')
</selectKey>
insert into t_student
values (#{id} , #{name} , #{gender} )
</insert>
</mapper>
5、测试类
public class TestMyBatis {
public static void main(String[] args) throws Exception{
//mybatis
//1、加载配置文件,获得读取配置文件的流对象
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
//2、构建 SqlSessionFactory,SqlSession连接对象的工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//3、通过SqlSessionFactory工厂获得连接对象sqlSession,
SqlSession sqlSession = sqlSessionFactory.openSession();
//4、通过连接对象sqlSession获得dao对应实现类的对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
//5、调用StudentDao接口中的insertStudent方法并打印
Student student = new Student(null,"小伶",false);
studentmapper.insertStudent(student);
System.out.println(student);
//6、提交事务,增删改需要
sqlSession.commit();
sqlSession.rollback();
//7、释放资源
sqlSession.close();
6、检验成果
先康康数据库,yeah yeah~插入数据又成功了!!!
最后一步,康康打印结果,先执行select replace(uuid(),’-’,’’)语句,再执行插入语句,数据全部都返回成功!!! \( ̄︶ ̄)/
第一次记录,完结撒花**ヽ(°▽°)ノ*
文章评论