有没有办法在Spring Boot中使用JPA保存时获取一个“刷新后”的已保存实体?

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

Is there a way to get a 'refreshed' saved entity when saving with JPA in Spring boot?

问题

首先,我不确定是否正在缓存请求或者只是未填充字段。

我有一个名为 CmsRegions 的实体,其中包含以下内容:

public class CmsRegions extends Model implements Serializable {
       
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 100)
    @Column(name = "name")
    private String name;
    
    @Lob
    @Size(max = 65535)
    @Column(name = "description")
    private String description;
    
    @Size(max = 2)
    @Column(name = "lang")
    private String lang;
    
    @Size(max = 2)
    @Column(name = "locale")
    private String locale;
    
    @Basic(optional = false)
    @NotNull
    @Column(name = "is_active")
    private short isActive;
    
    @Column(name = "parent_id")
    private Integer parentId;
        
    @Basic(optional = false)
    @Column(name = "created", insertable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date created;
    
    @Basic(optional = false)
    @Column(name = "modified", insertable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date modified;


    ...getters and setters etc...
}

我有一个用于检索实体的基本存储库调用:

@NoRepositoryBean
public interface BaseCmsRepository<T extends Serializable> extends JpaRepository<T, Long> {
    
    @Query("SELECT a FROM #{#entityName} a WHERE a.id = :id")
    List<T> getById(
            @Param("id") Integer id
    );

当我保存实体时,它可以成功保存到以下表中:

CREATE TABLE `cms_regions` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `description` text,
  `lang` char(2) DEFAULT NULL,
  `locale` char(2) DEFAULT NULL,
  `is_active` tinyint unsigned NOT NULL DEFAULT '0',
  `parent_id` int unsigned DEFAULT NULL,
  `created` datetime DEFAULT CURRENT_TIMESTAMP,
  `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

createdmodified 字段按预期正常设置,但是我从保存操作中得到的实体具有 createdmodified 日期的 NULL 值。

我进行了一个测试 - 我的最后插入的ID是31,所以我将以下代码添加到我的控制器中:

cmsService.saveRegion(region); // 保存实体

CmsRegions testreg1 = cmsService.getRegion(1);
CmsRegions testreg2 = cmsService.getRegion(region.getId());
CmsRegions testreg3 = cmsService.getRegion(30);
CmsRegions testreg4 = cmsService.getRegion(32);  // 当前保存的实体新ID (32)

在执行此操作时,testreg1 包含了预期的日期对象作为 Date 对象。testreg2 包含了空值(此时 region.getId() = 32)。testreg3 包含了创建和修改的日期对象。

最终值 testreg4 也是 NULL。问题是,下次运行时,testreg4 中将再次包含这些值。但下一次迭代中,testreg2(33)再次为 NULL。

我不知道这是否是缓存问题,或者其他什么问题。看起来好像如果在调用完成之前尝试提取数据库值 - 即使数据现在已经在数据库中(通过断点确认) - 结果字段总是为空。下一次运行时,它们会被填充。

为什么会这样?我需要做什么才能获得这些值?

英文:

First of all, I am not certain if this is caching requests or otherwise just not populating fields.

I have an entity called CmsRegions, which contains the following:

public class CmsRegions extends Model implements Serializable {
       
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = &quot;id&quot;)
    private Integer id;
    
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 100)
    @Column(name = &quot;name&quot;)
    private String name;
    
    @Lob
    @Size(max = 65535)
    @Column(name = &quot;description&quot;)
    private String description;
    
    @Size(max = 2)
    @Column(name = &quot;lang&quot;)
    private String lang;
    
    @Size(max = 2)
    @Column(name = &quot;locale&quot;)
    private String locale;
    
    @Basic(optional = false)
    @NotNull
    @Column(name = &quot;is_active&quot;)
    private short isActive;
    
    @Column(name = &quot;parent_id&quot;)
    private Integer parentId;
        
    @Basic(optional = false)
    @Column(name = &quot;created&quot;, insertable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date created;
    
    @Basic(optional = false)
    @Column(name = &quot;modified&quot;, insertable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date modified;


    ...getters and setters etc...
}

I have the basic repository call to retrieve the entity as:

@NoRepositoryBean
public interface BaseCmsRepository&lt;T extends Serializable&gt; extends JpaRepository&lt;T, Long&gt; {
    
    @Query(&quot;SELECT a FROM #{#entityName} a WHERE a.id = :id&quot;)
    List&lt;T&gt; getById(
            @Param(&quot;id&quot;) Integer id
    );
    

When I save the entity, it saves to the database fine into the following table:

CREATE TABLE `cms_regions` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `description` text,
  `lang` char(2) DEFAULT NULL,
  `locale` char(2) DEFAULT NULL,
  `is_active` tinyint unsigned NOT NULL DEFAULT &#39;0&#39;,
  `parent_id` int unsigned DEFAULT NULL,
  `created` datetime DEFAULT CURRENT_TIMESTAMP,
  `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

The created and modified fields get set fine as expected, but the resulting entity I get from my save action, has NULL values for the created and modified dates.

I ran a test - my last insert ID was 31, so I added the following code to my controller:

    cmsService.saveRegion(region); // save entity

    CmsRegions  testreg1 = cmsService.getRegion(1);
    CmsRegions  testreg2 = cmsService.getRegion(region.getId());
    CmsRegions  testreg3 = cmsService.getRegion(30);
    CmsRegions  testreg4 = cmsService.getRegion(32);  // currently saved entity new ID (32)

When doing this - testreg1 contained the created and modified dates as Date objects as hoped. testreg2 contained null values (region.getId() = 32 at this point). testreg3 contained the Date objects for created and modified.

The final value, testreg4 was also NULL. The kicker is, next time I ran it, testreg4 had the values in it for created and modified. But the next iteration, (33) in testreg2 was once again NULL.

有没有办法在Spring Boot中使用JPA保存时获取一个“刷新后”的已保存实体?

I do not know if this is a caching issue, or what. It just seems that if I try to pull the database value before the call has finished - EVEN though the data is now in the database (confirmed with breakpoints) - the resulting fields are always null. Next run, they are populated.

Why? What do I need to do to get these values?

答案1

得分: 1

使用JpaRepository#save保存时,或者在任何类似接口上使用此方法时,都会返回来自数据库的实体。您可以直接使用它。最常用于填充生成的id:

assertNull(entity.getId());
entity = entityRepository.save(entity);
assertNotNull(entity.getId());

当另一个进程更改数据且持久性上下文未刷新时,情况会稍微复杂一些。然后需要另一个查询。您可以通过使用EntityManager#refresh并传递要从数据库“重新获取”的实体来解决这个问题。目前我不知道这个机制是否存在于spring-data-jpa中。

英文:

When saving using JpaRepository#save or this method on any similar interface, the entity from the database is returned. You can just use it. It's most commonly used to populate the generated id:

assertNull(entity.getId());
entity = entityRepository.save(entity);
assertNotNull(entity.getId());

It's a bit more complex when the other process is changing the data and persistence context is not refreshed. Another query would be needed then. You can go around that by using EntityManager#refresh and passing the entity you would like to "refetch" from the database. Currently I don't know whether that mechanism is present in spring-data-jpa.

huangapple
  • 本文由 发表于 2020年9月3日 10:45:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/63716041.html
匿名

发表评论

匿名网友

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

确定