一、什么是自增ID、UUID、雪花ID

1、自增ID

有序 ID、自增ID的主键是有序的增加的,每条记录都是放在一条记录的后面。

优点:有序的增加、查询速度快、写入效率高、节省磁盘的空间、易读、性能好

缺点:导入旧数据ID可能会重复,不适合分库分表


2、UUID

无序随机 ID,字符串类型,生成没有规律,唯一的 (不推荐使用,会导致性能低下,从中间插入数据数据会往后移,消耗性能)

优点:适用于分库分表、由Java代码生成,不需要查询就知道ID

缺点:数据量大的时候会消耗性能、生成位数较长


3、雪花ID

是由算法生成,它可以生成递增且有时间顺序的64位整数,适用于分布式项目

优点:性能好、有序、适用于分布式项目、由Java代码生成,不需要查询就知道ID、所有需要生成唯一标识的都可以使用雪花ID

缺点:生成位数较长,传递到前端接受,可能出现精度丢失,可以转换成字符串解决


二、使用 自增ID、UUID、雪花ID 

1、自增 ID 的使用
1)新建一个 Maven 项目,导入需要的依赖
<dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <!-- Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
 
        <!-- mysql 依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>
 
        <!-- Lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        
        <!-- junit 依赖:测试代码-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

         <dependencies>
        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.12</version>
        </dependency>

 
    </dependencies>
2)新建一张 User  表
create table userinfo(
    user_id int primary key auto_increment, -- 用户Id
    user_name varchar(20),                  -- 用户名称
    user_tel varchar(11)                    -- 用户电话
);
 
insert into userinfo(user_name, user_tel) value ('user1','12323451234'),('user2','12423454567');
 
select * from userinfo;
3)新建一个 User 实体类
@Data
public class User {

    /**
     * 定义实体时,基本类型选用包装类
     * 如果不使用包装类,初始化对象后会有一个默认值
     */
    private Integer uid;
    private String userName;
    private String tel;

}
4)在资源文件 resources 下创建 mybatis.xml 配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<!-- 这个是 mybatis 的核心配置文件-->
<configuration>
 
    <!--  typeAliases 用于配置实体类的别名  -->
    <typeAliases>
        <!-- 方式一:为每个实体类分别指定一个别名,使用时不区分大小写 -->
        <!-- <typeAlias type="edu.nf.ch02.entity.User" alias="user"/>-->
 
        <!-- 方式二:直接使用指定实体类的包,这样 mybatis 就会自动给这个包下所有的
                   实体类指定创建别名,别名就是类名,不包括包名,并且也是不区分大小写 -->
        <package name="edu.nf.ch01.entity"/>
 
    </typeAliases>
 
    <!-- 配置环境,主要是配置数据源,指定连接的数据库,default 指定使用默认的数据库环境的 id -->
    <environments default="mysql">
        <!-- 配置 MySQL 数据源的环境,id 通常指定数据库名称 -->
        <environment id="mysql">
            <!-- 事务管理使用 JDBC 的事务 -->
            <transactionManager type="JDBC"/>
            <!-- type指定为 pool, 数据源连接使用 mybatis 自带的连接池
                     目的是可以让连接复用-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/psm?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;timeZone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
 
        <!-- 还可以配置其他数据源的环境 -->
        <!--        <environment id="sqlserver">-->
 
        <!--        </environment>-->
    </environments>
 
    <!-- 指定 Mapper 映射配置文件 -->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>
5)在 uitl 包下,创建 MybatisUtil 类
 
/**
 * @Date 2023-09-07
 * @Author qiu
 */
public class MybatisUtils {
 
    /**
     * 声明 SqlSessionFactory, 用于创建 SqlSession 实例
     */
    private static SqlSessionFactory sqlSessionFactory;
 
    /**
     * 在静态代码块中初始化 SqlSessionFactory 工厂
     */
    static {
 
        // 通过查找 mybatis 的核心配置文件,先创建一个用于读取 xml 文件的输入流
        try {
            InputStream is = Resources.getResourceAsStream("mybatis.xml");
            // 将输入流传给创建的 SqlSessionFactoryBuilder 进行解析,并初始化
            // 整个 sqlSessionFactory 工厂对象
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            throw new RuntimeException("Init mybatis error", e);
        }
    }
 
    /**
     * SqlSession 是操作数据库的核心对象,相当于封装了整个 Connection
     * 不带参数的 openSession 方法,得到的 SqlSession 是需要我们手动提交事务的
     *
     * @return
     */
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
 
    /**
     * openSession 方法还可以传入一个 Boolean 类型的参数
     * 如果设置为 true,创建出来的 SqlSession 就会自动创建提交事务
     * false 一样是手动提交
     *
     * @param autoCommit
     * @return
     */
    public static SqlSession getSqlSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }
 
 
}
6)写 UserDao 接口的实现类
void saveUser(User user);
7)实现 UserDao 接口
 @Override
    public void saveUser(User user) {
        // 创建 sqlSession
        SqlSession sqlSession = MybatisUtiles.getSqlSession(true);
        try{
            sqlSession.getMapper(UserDao.class).saveUser(user);
        } catch (RuntimeException e){
            throw new RuntimeException(e);
        } finally {
            sqlSession.close();
        }

    }
8)在 resource 配置文件中新建一个文件夹 mapper ,在mapper里再创建一个 UserInfoMapper.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">
<!-- 指定 Dao 完整的类名 -->
<mapper namespace="edu.nf.ch02.dao.UserDao">

    <!-- useGeneratedKeys 设置为 true 表示要获取自增长的 key,
            并将取出来的 id 保存到实体的 uid 字段中 -->
    <insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="uid">
        insert into userinfo(user_name, user_tel)
        values (#{userName}, #{tel});
    </insert>

</mapper>

注意:自增ID的使用就是在mybatis的配置文件中配置 useGeneratedKeys="true" ,并且把获取自增长的 key, 并将取出来的 id 保存到实体的 uid 字段中,这个 uid 就是 User 实体类中的 uid.

9)单元测试
    @Test
    public void testSaveUser(){
        User user = new User();
        // 已经开启了自增ID,就不需要设置id的值了
//        user.setUid(1);
        user.setUserName("User2");
        user.setTel("12334567890");
        new UserDaoImpl().saveUser(user);
        System.out.println(user.getUid());
        System.out.println(user);
    }

结果

可以看到,我们的 id 自有序的递增的,因为我清空了表,之前是有数据的,所有是从37开始的。


2、UUID
1)新建一个工具类 UUIDUtiles

/**
 * @Date 2023-09-11
 * @Author qiu
 */
public class UUIDUtiles {

    public static String createUUID(){
        String uid = UUID.randomUUID().toString();
        // 取出 UUID 的 “-”,替换为 “”
        // 3bcdddfc-523d-457f-a1ca-7378e2c78301 原本生成的id是有 ’-‘的
        return uid.replace("-","");
    }


    public static void main(String[] args) {
        System.out.println(createUUID());
    }
}
2)新建一张表
create table stu_info(
    stu_id char(32),
    stu_name varchar(20)
)

 因为生成的ID是32位的且是字符串类型,所以需要重新建一张表,stu_id 的类型是char位长是 32

 3)新建一个 Student 实体类
@Data
public class Student {

    private String stuId;
    private String stuName;
}
4) 定义 dao 接口的方法
 void saveStu(Student student);
5)实现 dao 接口的方法
 @Override
    public void saveStu(Student student) {
        // 注意:getSession 设置为 true 表示自动提交事务
        SqlSession sqlSession = MybatisUtiles.getSqlSession(true);
        try {
            sqlSession.getMapper(StuDao.class).saveStu(student);
        } catch (Exception e){
            throw new RuntimeException(e);
        } finally {
            sqlSession.close();
        }
    }
6)编写配置文件
<?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="edu.nf.ch02.dao.StuDao">
  
    <insert id="saveStu" parameterType="edu.nf.ch02.entity.Student">
        insert into stu_info(stu_id,stu_name) values(#{stuId},#{stuName});
    </insert>

   

</mapper>

注意:因为是使用Java代码生成的id,所以不需要设置useGeneratedKeys="true" keyProperty="uid"  

7)单元测试
   @Test
    public void testSaveStu(){
        Student student = new Student();
        student.setStuId(UUIDUtiles.createUUID());
        student.setStuName("li");
        new StuDaoImpl().saveStu(student);
        System.out.println(student.getStuId());
    }

结果

可以看到每一个生成的 ID 都是不一样的,大家可以执行多次查看结果 

 3、雪花 ID
1)新建一个 SnowFlakeUtiles 工具类
/**
 * @Date 2023-09-11
 * @Author qiu
 */
public class SnowFlakeUtiles {

    public static long create() {

        //生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
        return IdUtil.getSnowflakeNextId();
    }


    public static void main(String[] args) {
        System.out.println(create());
    }

}
 2)单元测试(使用上面的方法和表)
   @Test
    public void testSaveStu(){
        Student student = new Student();
        student.setStuId(String.valueOf(SnowFlakeUtiles.create()));
        student.setStuName("li2");
        new StuDaoImpl().saveStu(student);
        System.out.println(student.getStuId());
    }

结果

可以看到前面几位数字都是一样的,体现它的有序性,是不是比UUID更加易读易懂 

 三、结语

当数据非常大的时候会进行分库分表, 此时自增的 id 会带来业务上的问题(不同的表会产生相同的 id 值) 因此可以考虑使用雪花 id,生成雪花 id 可以使用开源的工具类生成,(例如:hutool) 雪花 id 是一个长度 64 位的 long 类型的 id。如果是小项目可以考虑使用自增id,中大型以上的项目就可以使用 UUID 和 雪花ID。

 项目完整的案例我上传到了 gitee上地址:

https://gitee.com/qiu-feng1/mybatis-study.git

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐