【Spring Jpa总结】@GeneratedValue注解介

2022-05-23  本文已影响0人  伊丽莎白2015

在使用Spring Jpa的时候,除了可以使用Repository提供的一系列自定义的方法(如findBy*)之外,在Entity层使用了很多Java Persistence API相关的注解。比如熟知的@Entity, @Table, @GeneratedValue等。

关于Java Persistence API,官网:https://jakarta.ee/specifications/persistence/

本文尝试解答@GeneratedValue用法,以及与@SequenceGenerator@GenericGenerator的区别。

文章内容

1. @GeneratedValue介绍

基于Java persistence API v2.2版本的在线Java文档:https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/generatedvalue
@GeneratedValue注解的主要作用是:声明主键的生成策略。很自然的,它需要和@Id结合使用。
这个注解有两个参数:

1. strategy:
2. generator:

主键生成器的名称,与@SequenceGenerator@GenericGenerator等注解结合使用。


2. 示例

2.1 示例-1:MySQL - strategy=IDENTITY 主键设置自增长
@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    ...
}

这时候在测试的时候,就算set了一个id,也没有用,存入的时候还是会算数据库自增的id来。
MySQL主健设置的是自增长,如果在实体类里不加@GeneratedValue,在save的时候,id也能自动存入。但有个问题,就是这时候不小心在程序里set了这个实体的id,那么就不会自动增长了,就会直接将set的这个id存进去了。

总结,如果使用的是自增长,那么还是要声明strategy = GenerationType.IDENTITY比较好。

2.2 示例-2:Oracle - strategy=SEQUENCE

首先在Oracle中定义sequence:
参考:ORACLE SEQUENCE 详解

create sequence COURSE_SEQ;

然后结合使用@SequenceGenerator注解和@GeneratedValuegenerator

关于@SequenceGenerator的在线文档:
https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/sequencegenerator,它包含很多属性,其中示例有用到的:

@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @SequenceGenerator(name = "courseSeq", sequenceName = "COURSE_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "courseSeq")
    private long id;
    ...
}
2.3 示例-3: MySQL - strategy=AUTO 主键不自增

strategy=AUTO的意思是主键由程序控制。默认策略就是AUTO,所以在Entity中可以不用定义@GeneratedValue注解,也是可以工作的。

@Entity
@Table(name = "COURSE")
public class Course {

    @Id
    private long id;
    ...
}

测试:
我利用Hazelcast来生成分布式ID(当然用Redis或是UUID都可以):

@SpringBootTest
public class CourseRepositoryTest {

    @Autowired
    private CourseRepository courseRepository;

    @Autowired
    private HazelcastInstance hazelcastInstance;

    @Test
    public void test() {
        Course course = new Course();
        course.setId(hazelcastInstance.getFlakeIdGenerator("courseId").newId());
        course.setName("Test");
        course.setStatus(StatusEnum.Active);
        courseRepository.save(course);
    }
}
运行两遍,结果: 插入了两条

可以看到ID是set了什么,它就save什么。

2.4 示例-4: 结合使用@GenericGenerator与@GeneratedValue的generator

第2.3的示例,每次id需要手动set,感觉也不是很方便,改进的办法是结合使用@GenericGenerator@GeneratedValuegenerator

注:@GenericGenerator注解不在Java Persistence API中,而是hibernate包中的注解。它的主要作用是用来指定一个ID的生成器的自定义类。

以下是示例:

@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GenericGenerator(name = "testGenerator", strategy = "com.util.IdGenerator")
    @GeneratedValue(generator = "testGenerator")
    private long id;
    ...
}

Generator实现类:需要实现IdentifierGenerator的generate方法。我依旧选择Hazelcast来生成分布式ID:

public class IdGenerator implements IdentifierGenerator {

    @Override
    public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
        HazelcastInstance hazelcastInstance = ApplicationProvider.getBean(HazelcastInstance.class);
        return hazelcastInstance.getFlakeIdGenerator("courseId").newId();
    }
}

测试,可以看到这时候就不需要set id了。程序会自动调用IdGenerator的generate方法在save前把id给设置上。测试结果跟#2.3类似。

    @Test
    public void test() {
        Course course = new Course();
        course.setName("Test");
        course.setStatus(StatusEnum.Active);
        courseRepository.save(course);
    }
2.5 示例-5,@GenericGenerator已经存在的内置类

根据文章Difference between @GeneratedValue and @GenericGenerator
的介绍,Hibernate在类DefaultIdentifierGeneratorFactory预先内置了很多生成ID的类:

public DefaultIdentifierGeneratorFactory() {
        register( "uuid2", UUIDGenerator.class );
        register( "guid", GUIDGenerator.class );            // can be done with UUIDGenerator + strategy
        register( "uuid", UUIDHexGenerator.class );         // "deprecated" for new use
        register( "uuid.hex", UUIDHexGenerator.class );     // uuid.hex is deprecated
        register( "assigned", Assigned.class );
        register( "identity", IdentityGenerator.class );
        register( "select", SelectGenerator.class );
        register( "sequence", SequenceStyleGenerator.class );
        register( "seqhilo", SequenceHiLoGenerator.class );
        register( "increment", IncrementGenerator.class );
        register( "foreign", ForeignGenerator.class );
        register( "sequence-identity", SequenceIdentityGenerator.class );
        register( "enhanced-sequence", SequenceStyleGenerator.class );
        register( "enhanced-table", TableGenerator.class );
    }

比如使用uuid2来生成ID,这时候@GenericGenerator的strategy值不是一个类名了,而是hibernate预置的值generator name了。

以下是示例:

@Entity
@Table(name = "COURSE")
public class Course {
    @Id
    @GeneratedValue(generator = "testGenerator")
    @GenericGenerator(name = "testGenerator", strategy = "uuid2")
    private String id;

注:如果使用strategy=“increment”这类hibernate自带的自增器,它是单机版的,并不是分布式的。

上一篇 下一篇

猜你喜欢

热点阅读