SpringBoot 2.1.5.RELEASE & MySQL 8.0.15 中未生成 GenerationType.IDENTITY

Posted

技术标签:

【中文标题】SpringBoot 2.1.5.RELEASE & MySQL 8.0.15 中未生成 GenerationType.IDENTITY【英文标题】:GenerationType.IDENTITY not generated in SpringBoot 2.1.5.RELEASE & MySQL 8.0.15 【发布时间】:2019-10-03 13:01:28 【问题描述】:

我有一个基本的 SpringBoot 2.1.5.RELEASE 应用程序。使用 Spring Initializer、JPA、嵌入式 Tomcat、Thymeleaf 模板引擎,并打包为可执行 JAR 文件。

我有这个域类:

     @Entity
        @Table(name="t_purchase")
        @JsonInclude(JsonInclude.Include.NON_NULL)
        public class Purchase implements Serializable 

            public Purchase() 
            

            public Purchase(Shop shop) 
                super();
                this.shop = shop;
            

            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @JsonProperty("id")
            private Long id;    

@JsonProperty("txHash")
    private String txHash;

            @ManyToOne(fetch = FetchType.EAGER)
            @JoinColumn(name = “shop_id")
            @JsonIgnore
            Shop shop;


    @Override
        public boolean equals(Object o) 
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Purchase purchase = (Purchase) o;

            if (getTxHash()==null && purchase.getTxHash()==null) 
                 return id == purchase.id;
             else 
                return Objects.equals(getTxHash(), purchase.getTxHash());
            
        



        @Override
        public int hashCode() 
            int result = (int) (id ^ (id >>> 32));
            result = 31 * result + Objects.hash(getTxHash());
            return result;
        

        …
        

然后我创建了这个 Junit 方法:

   @Test
    public void testFindByShopIdWithPurchases () 

        Shop shop = new Shop ("Shop_NAME");

        shopService.save(shop);

        Purchase purchase1 = new Purchase(shop);
        Purchase purchase2 = new Purchase(shop);

        shop.getPurchases().add(purchase1);
        shop.getPurchases().add(purchase2);

        shopService.save(shop);

        assertNotEquals (purchase1, purchase2);


       

但我有一个 AssertionError,因为这两个对象的 id 似乎都是 0:

java.lang.AssertionError: Values should be different. Actual: Purchase [id=0, txHash=null, shop=957]

这里是商店:

 @Entity
    @Table(name = “t_shop")
    public class Shop implements Serializable 


        public Shop(String name) 
            this.name = name;
        

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @JsonProperty("id")
        private Long id;

        @JsonProperty("name")
        private String name;

        @OneToMany(mappedBy = “shop", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
        @JsonIgnore
        private Set<Purchase> purchases = new HashSet<Purchase>();

            …
    

【问题讨论】:

您的测试是否使用@Transactional 注释?如果没有,您可以尝试添加吗? 确实是:( 【参考方案1】:

从 Spring 2.X.X RELEASE,spring-data-jpa 模块使用 Hibernate5 版本。如果您浏览org.springframework.data:spring-data-jpa:2.1.5.RELEASE pom.xml 文件,您会发现休眠依赖的版本为

<hibernate>5.2.17.Final</hibernate>

Hibernate5 @GeneratedValue(strategy = GenerationType.IDENTITY) 之前的 Hibernate 版本就像一个魅力。但是在发布 Hibernate5 之后,下面的fix 是必要的。

在此处复制我的answer 以供后代使用。

@Id
@GeneratedValue(strategy= GenerationType.AUTO,generator="native")
@GenericGenerator(name = "native",strategy = "native")
private Long id;

DDL

`id` BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY

原因

摘自hibernate-issue

目前,如果 hibernate.id.new_generator_mappings 设置为 false, @GeneratedValue(strategy = GenerationType.AUTO) 映射到本机。 如果此属性为真(这是 5.x 中的默认值),则 @GeneratedValue(strategy = GenerationType.AUTO) 始终映射到 序列样式生成器。

因此,在任何不支持序列的数据库上 本机(例如 mysql)我们将使用 TABLE 生成器 身份。

然而,TABLE 生成器虽然更便携,但使用单独的 每次从数据库中获取值时的事务。在 事实上,即使 IDENTITY 禁用 JDBC 批量更新和 TABLE 生成器使用池化优化器,IDENTITY 仍然可以更好地扩展。

【讨论】:

【参考方案2】:

尝试更改下面提到的属性spring.jpa.hibernate.use-new-id-generator-mappings=false   由于新的 Spring Boot 版本 2.XX 已将此属性更改为spring.jpa.hibernate.use-new-id-generator-mappings=true   使用以下链接进行详细了解Table 'DBNAME.hibernate_sequence' doesn't exist

【讨论】:

【参考方案3】:
 @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @JsonProperty("id")
        private Long id;   

当您使用 GenerationType IDENTITY 时,这意味着您负责在休眠时生成 ID。

如果您希望应用程序自动管理 id 生成,请使用 GenerationType Auto 所以在 Purchase 以及 Shop 实体

中更新您的 GenerationType
 @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @JsonProperty("id")
        private Long id;  

.................................................. .....................

如果以上修改后还是报错:

 java.sql.SQLSyntaxErrorException: Table 'shops_db.hibernate_sequence' doesn't exist 

默认情况下,Hibernate 从 hibernate_sequence 表生成密钥,我们可以通过将 hibernate.use-new-id-generator-mappings 设置为 false 来禁用它。

spring.jpa.hibernate.use-new-id-generator-mappings=false

在您的 application.properties 中设置此属性

【讨论】:

【参考方案4】:

将类型更改为 AUTO 为我解决了问题

【讨论】:

更改为 AUTO 时出现此错误:java.sql.SQLSyntaxErrorException: Table 'shops_db.hibernate_sequence' 不存在 请分享您的应用程序的 hibernate.ddl-auto 设置

以上是关于SpringBoot 2.1.5.RELEASE & MySQL 8.0.15 中未生成 GenerationType.IDENTITY的主要内容,如果未能解决你的问题,请参考以下文章

springboot问题排解

@OneToMany Spring Boot 2.1.5.RELEASE 中的 Spring Data JPA 没有获取所有数据

Springboot + Freemarker

SpringBoot集成MyBatis

Spring Boot2(002):手动创建第1个SpringBoot2简单应用——“HelloWorld” web 工程

Redis