最近,JHipster 7.7.0应用程序开始为所有表生成服务器端/数据库错误。该应用程序已部署到生产环境中,用户输入数据没有问题。假设有一个名为“Product”的实体,并且用户添加了几个条目:
| ID | Name |
|------|-------------|
| 1001 | Corn flakes |
| 1002 | Baked beans |
| 1003 | Carrots |
| 1004 | Apples |
| 1005 | Watermelon |
字符串
我注意到的第一件事是ID值从1001开始计数。(这是新…在JHipster 6中,ID值从1开始。)
在我对应用程序代码做了一些改进之后,我重新部署到了生产环境中。用户尝试添加新产品,在服务器日志中生成以下错误:
2022-03-24 12:54:43.775 ERROR 11277 --- [ XNIO-1 task-1] o.h.e.jdbc.batch.internal.BatchingBatch : HHH000315: Exception executing batch [java.sql.BatchUpdateException: (conn=33) Duplicate entry '1001' for key 'PRIMARY'], SQL: insert into product (name, id) values (?, ?)
2022-03-24 12:54:43.776 WARN 11277 --- [ XNIO-1 task-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2022-03-24 12:54:43.776 ERROR 11277 --- [ XNIO-1 task-1] o.h.engine.jdbc.spi.SqlExceptionHelper : (conn=33) Duplicate entry '1001' for key 'PRIMARY'
2022-03-24 12:54:43.779 ERROR 11277 --- [ XNIO-1 task-1] o.z.problem.spring.common.AdviceTraits : Internal Server Error
org.springframework.dao.DataIntegrityViolationException: could not execute batch; SQL [insert into product (name, id) values (?, ?)]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute batch
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:276)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:566)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
at com.mycompany.app.web.rest.ProductResource$$EnhancerBySpringCGLIB$$84c14d6d.createProduct(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
...
型
显然,ID计数器在1001
处重新启动,找到了一个条目(Corn flakes
),数据库产生了错误。
当用户再次尝试保存时,会发生几乎相同的错误,但这次ID值增加到1002
,现在与Baked beans
冲突,数据库会抛出另一个“Duplicate entry”异常。
重复保存失败,直到用户通过1005
(Watermelon
),保存成功,通过1006
。(后续保存到此表中的操作将继续进行,不会出错。)
类似的错误发生在所有实体表上,并且只能通过重复地“保存”直到ID增加超过该表中已经存在的最后一个ID值来解决。
这是个问题。
调查
从JHipster版本6开始,我就一直在生成JHipster应用程序,通常没有问题。
我查看了一个旧的JHipster 6项目,并将实体与新的JHipster 7项目中的实体进行了比较,注意到在如何生成ID值的策略上发生了转变。
在JHipster 6(我认为是更早的版本)中,每个实体的ID值定义如下:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
型
结合每个实体的Liquibase变更日志文件条目(例如..._added_entity_Product.xml
):
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
型liquibase 00000000000000_initial_schema.xml
文件包含(靠近顶部):
<property name="autoIncrement" value="true"/>
型
在这种情况下,创建的数据库表将ID字段定义为AUTO_INCREMENT字段,这意味着ID值和增量由数据库管理:
CREATE TABLE `product` {
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL
} ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
型
**然而,在JHipster 7中,情况发生了变化。**对于每个实体,ID属性定义为:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(name = "sequenceGenerator")
@Column(name = "id")
private Long id;
型
与Liquibase更改日志文件条目(例如..._added_entity_Product.xml
):
<column name="id" type="bigint">
<constraints primaryKey="true" nullable="false"/>
</column>
型liquibase 00000000000000_initial_schema.xml
文件包含(靠近顶部):
<changeSet id="00000000000000" author="jhipster">
<createSequence sequenceName="sequence_generator" startValue="1050" incrementBy="50"/>
</changeSet>
型
在此配置中,序列生成器似乎在管理ID值和增量。数据库表不再将ID字段定义为AUTO_INCREMENT:
CREATE TABLE `product` {
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL
} ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
型
临时修复
为了让事情正常工作,我对JHipster 7项目进行了以下更改:
1.已修改每个域POJO实体的ID
属性注解:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
型
1.已修改每个实体的liquibase更改日志XML文件(..._added_entity_Product
等):
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
型
1.修改liquibase 00000000000000_initial_schema.xml
文件,在顶部附近添加以下行:
<property name="autoIncrement" value="true"/>
型
现在,我的数据库被生成,每个实体表ID值被定义为:
`id` bigint(20) NOT NULL AUTO_INCREMENT
型
这似乎对我有效,但我对此并不满意。
问题
理想情况下,我不想改变JHipster 7的原始配置,并更好地理解如何管理这个ID序列的生成。(我不确定修改changelog文件是否是解决这个问题的正确方法,并且对JHipster团队的数据库专业知识比对我自己的更有信心。
- 我如何控制这个序列生成器,并指示它从适合每个实体的值中选择,特别是在重新部署/更新之后?有可能吗?
- 我可以指示它自动查找数据库中的下一个可用值而不抛出错误和失败吗?
- 为什么JHipster团队做出了这样的改变?有优势吗?有人能给我指出一个技术资源,让我可以了解这个优势吗?
有没有人能给我指出正确的方向,让我学习和提高我的理解?
注意:如果不清楚,从一开始就遭受此数据库问题的项目是JHipster 7。它最初在JHipster 6中 * 不是 *,并升级到7。我提到JHipster 6的唯一原因是因为以前使用JHipster 6的项目没有遇到这个问题,所以我正在调查JHipster版本之间的变化,特别是关于ID值的处理。
环境:JHipster 7.7.0(Angular,monolithic),MariaDB 10.4,OpenJDK 16.0.2_7,操作系统Windows 10 Pro和openSUSE 15.2,Firefox 98.0.2和Chrome 99.0.4844.84。
2条答案
按热度按时间fzwojiic1#
序列对于性能更好,并且可以为每个批预分配。参见https://vladmihalcea.com/mariadb-10-3-database-sequences/
这种策略已经用于其他数据库,因为MariaDB在10.3中引入了序列,JHipster团队决定在所有数据库中应用相同的策略,因为这是一个突破性的变化,他们在主要版本中进行了更改。
iqjalb3h2#
字符串