Hibernate JPA cannot fetch new record after immediately saving it(within ms) by non-primary key(email,type) but can fetch by primary key(id)

huangapple go评论86阅读模式
英文:

Hibernate JPA cannot fetch new record after immediately saving it(within ms) by non-primary key(email,type) but can fetch by primary key(id)

问题

代码部分不需要翻译,以下是翻译好的内容:

My system has a patient entity that contains email(string), type(integer) fields, both non primary, not unique and not null fields along with other fields and of course a primary key id.
我的系统有一个patient实体,包含电子邮件(字符串)和类型(整数)字段,它们都不是主键,也不是唯一的,而且不允许为空,还有其他字段,当然还有一个主键id。

After I save a new patient entity, when I search the entity in the database by jpa query findById it works perfectly fine and it fetches that new entity that was just saved few ms ago.
在我保存一个新的patient实体后,通过jpa查询findById在数据库中搜索该实体,它运行得非常正常,可以获取刚刚保存的新实体,即刚刚几毫秒前保存的实体。

But when I search the newly saved entity by email and type by jpa query findByEmailAndTypeAndEmailIsNotNull it returns nothing but if I run the very same findByEmailAndTypeAndEmailIsNotNull query after 1 second of saving that new entity then it returns that newly saved entity.
但是,当我通过jpa查询findByEmailAndTypeAndEmailIsNotNull按电子邮件和类型搜索新保存的实体时,它什么也不返回,但如果我在保存新实体后1秒钟后运行完全相同的findByEmailAndTypeAndEmailIsNotNull查询,然后它返回刚刚保存的新实体。

Can some one diagnose the problem, is it even related to JPA? or with Database itself?
有人能诊断问题吗?这与JPA有关吗?还是与数据库本身有关?

Edit:
编辑:

@Transactional
public synchronized Patient addPatient(PatientProfileDto patientProfileDto, Integer facilityId)
throws ResourceAlreadyExistsException, EntityNotFoundException, ClientException {
// 一些代码

执行患者创建验证

// 患者创建

patientRepository.saveAndFlush(patient);

// 将患者与其他实体关联的一些代码

}

private void performPatientCreationValidations(...params)
throws ResourceAlreadyExistsException, ClientException {
if (patientRepository.findByEmailAndTypeAndEmailIsNotNull(patientDto.getEmail(), PatientType.OWNER.getId()).isPresent()) {
// 抛出错误
}
}

如果我连续5次调用API并且之间的延迟很小,那么将创建2个重复的患者,但在最后3个API调用中,它将按预期抛出错误。它还应该在接收到第2个API调用时抛出错误。还请注意,添加患者是同步函数,因此一个API调用完成保存患者后,另一个API调用会在函数上获取锁并继续执行。

英文:

My system has a patient entity that contains email(string), type(integer) fields, both non primary, not unique and not null fields along with other fields and of course a primary key id. <br><br>
After I save a new patient entity, when I search the entity in the database by jpa query findById it works perfectly fine and it fetches that new entity that was just saved few ms ago.<br>
But when I search the newly saved entity by email and type by jpa query findByEmailAndTypeAndEmailIsNotNull it returns nothing but if I run the very same findByEmailAndTypeAndEmailIsNotNull query after 1 second of saving that new entity then it returns that newly saved entity.<br><br>
Can some one diagnose the problem, is it even related to JPA? or with Database itself?

Edit:

@Transactional
public synchronized Patient addPatient(PatientProfileDto patientProfileDto, Integer facilityId)
        throws ResourceAlreadyExistsException, EntityNotFoundException, ClientException {
    // some code

    performPatientCreationValidations(ownerDto, ownerDemographicDto.getNationality().getId(), Boolean.FALSE, Boolean.FALSE);

    // patient creation

    patientRepository.saveAndFlush(patient);

    // some code to link patient to other entities
}

private void performPatientCreationValidations(...params)
        throws ResourceAlreadyExistsException, ClientException {
    if (patientRepository.findByEmailAndTypeAndEmailIsNotNull(patientDto.getEmail(), PatientType.OWNER.getId()).isPresent()) {
        // throw error
    }
}

If I hit the API 5 times in a row with neglible delay then 2 duplicate patient would get created, however on the last 3 apis hits it will throw error as it should. It should have also thrown the error upon 2nd api hit it receives. Also note the adding of patient is synchronized function so when one api hit is completed saving the patient then another api hit acquires lock on function n go on.

答案1

得分: 3

JpaRepository.findById() 在使用时效果很好,因为它从Hibernate的第一级缓存中获取实体,而不是从数据库中获取,因为通常将插入到数据库的操作推迟到必要时,例如,直到会话被刷新。只有通过实体的id才能从第一级缓存中获取实体。

因此,你要么手动使用JpaRepository.flush()刷新会话,要么使用JpaRepository.saveAndFlush()而不是JpaRepository.save(),或者在一个事务中执行你的操作。在这种情况下,你的请求共享一个会话,当它收到对相同实体的新查询时,Hibernate会立即刷新其缓存。

英文:

> After I save a new patient entity, when I search the entity in the database by jpa query findById it works perfectly fine

JpaRepository.findById() works fine as it takes the entity from Hibernate's 1st level cache, not from the database, because inserting into the database is usually deferred until necessary, e.g. until your session is flushed. An entity can be fetched from the 1st level cache only by its id.

So you have to either flush the Session manually with JpaRepository.flush(), or use JpaRepository.saveAndFlush() instead of JpaRepository.save() or execute your operations within one transaction. In this case your requests share one session and Hibernate is going to flush its cache as soon as it gets a new query for the same entity.

huangapple
  • 本文由 发表于 2023年2月8日 15:36:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/75382595.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定