美文网首页
【Spring Jpa总结】@GeneratedValue注解介

【Spring Jpa总结】@GeneratedValue注解介

作者: 伊丽莎白2015 | 来源:发表于2022-05-23 20:35 被阅读0次

    在使用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:
    • TABLE:使用一个特定的数据库表格存放主键。
    • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。(Oracle)
    • IDENTITY:主键有数据库自动生成(主要是自动增长类型)。(MySQL)
    • AUTO:主键由程序控制。(默认)
    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,它包含很多属性,其中示例有用到的:

    • name:自定义的名字,主要是为了另一个注解@GeneratedValuegenerator使用。
    • sequenceName,Oracle表中定义的sequence名字。
    • allocationSize - 每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50.
    @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注解,name可以随便取,strategy是ID的实现类,下面有介绍。
    • 其次还是需要@GeneratedValue,这时候可以不用定义strategy了(默认即AUTO),需要定义一个generator,即上面的name。

    注:@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自带的自增器,它是单机版的,并不是分布式的。

    相关文章

      网友评论

          本文标题:【Spring Jpa总结】@GeneratedValue注解介

          本文链接:https://www.haomeiwen.com/subject/ikdrprtx.html