Spring Data Repository Save Not Returning Instance With Updated Audit Fields

Viewed 4372

Why does repository.save(myEntity) not return an updated entity with the updated audit fields?

The resulting instance from MyEntityRepository.save(myEntity) and subsequently, from MyEntityService.save(myEntity) does not have the updated updatedOn date. I have verified this is correctly set in the database, so I know that auditing is working. The returned instance's updatedOn date is correct for an insert, but not for an update. I prefer to not have to immediately do a findById after every save, especially if the intent is that save() returns the udpated, attached instance.

Assuming the setting of updatedOn is occurring through a @PreUpdate hook and this hook is triggered during the entityManager.merge() call via repository.save(), I don't follow why the value would not be set on the returned instance.

Example code:

@Entity
@DynamicUpdate
@DynamicInsert
@Table(name = "my_entity", schema = "public")
@SequenceGenerator(name = "pk_sequence", sequenceName = "my_entity_seq", allocationSize = 1)
@AttributeOverrides({@AttributeOverride(name = "id", column = @Column(name = "id", columnDefinition = "int"))})
@EntityListeners(AuditingEntityListener.class)
public class MyEntity {

    protected Integer id;

    @LastModifiedDate
    private Date updatedOn;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pk_sequence")
    @Column(name = "id", nullable = false, columnDefinition = "bigint")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Version
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_on")
    public Date getUpdatedOn() {
        return updatedOn;
    }

    public void setUpdatedOn(Date updatedOn) {
        this.updatedOn = updatedOn;
    }
}


public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> { }

@Service
@Transactional(readOnly = true)
public class MyEntityService {

    @Autowired
    private MyEntityRepository repository;

    @Transactional
    public MyEntity save(MyEntity myEntity) {
        return repository.save(myEntity);
    }
}
2 Answers

I faced with the same issue.

In my case the important items that helped me to solve this problem were:
1) use repository.saveAndFlush(...) method

2) use findAllById() or findByYourOwnQuery() (annotated with @Query).

Overall, my test case looked like this:

UserAccount userAccount = UserAccount.builder().username(username).build();
userAccountRepository.saveAndFlush(userAccount);

final LocalDateTime previousUpdateDate = userAccount.getUpdateDate();
....

List<BigInteger> ids = Arrays.asList(userAccountId);    
UserAccount updatedUserAccount = userAccountRepository.findAllById(ids).get(0);  // contains updated Audit data fields

...
assertThat(actual.getUpdateDate(), is(greaterThan(previousUpdateDate)));  // true

The important thing that you shouldn't use repository.findOne(...) because it caches the reference to the object - read more.

Related