You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JPA中@LastModifiedDate同事务更新后返回旧值问题求助

JPA同一事务内更新实体后@LastModifiedDate返回旧值问题

在同一事务内更新User实体后再次查询,实体的普通修改字段(如username、email)能正常返回新值,但父类BaseEntity中用@LastModifiedDate标注的updatedAt字段仍显示更新前的旧值。已确认数据库中该字段已正确更新,推测是JPA缓存机制导致,但无法解决。

环境信息

  • Java版本:17
  • Spring Boot版本:3.0.3
  • 数据库:PostgreSQL

实体代码

User 实体

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import java.io.Serial;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;
import java.util.Set;

@Getter
@Setter
@ToString(callSuper = true)
@Entity
@Table(name = User.TABLE_NAME, uniqueConstraints = {
        @UniqueConstraint(
                name = User.UNIQUE_KEY_EMAIL_DELETED,
                columnNames = {User.COLUMN_EMAIL_NAME, BaseEntity.COLUMN_DELETED_NAME}),
        @UniqueConstraint(
                name = User.UNIQUE_KEY_USERNAME_DELETED,
                columnNames = {User.COLUMN_USERNAME_NAME, BaseEntity.COLUMN_DELETED_NAME}
        )
})
@SQLDelete(sql = BaseEntity.DELETE_SQL_PREFIX + User.TABLE_NAME + BaseEntity.DELETE_SQL_SUFFIX)
@Where(clause = BaseEntity.WHERE_DELETED_SQL)
@DynamicUpdate
public class User extends BaseEntity {
    public static final String TABLE_NAME = "sys_user";
    @Serial
    private static final long serialVersionUID = 2788923386294327164L;

    public static final String COLUMN_USERNAME_NAME = "username";
    public static final String COLUMN_EMAIL_NAME = "email";
    public static final String UNIQUE_KEY_EMAIL_DELETED = "uni_email_deleted";
    public static final String COLUMN_EXPIRED_TIME_NAME = "expired_time";
    public static final String COLUMN_IS_LOCKED_NAME = "locked";
    public static final String COLUMN_IS_ENABLED_NAME = "enabled";
    public static final String UNIQUE_KEY_USERNAME_DELETED = "uni_username_deleted";

    @Column(name = COLUMN_USERNAME_NAME, nullable = false)
    private String username;

    @Column(name = COLUMN_EMAIL_NAME, nullable = false)
    private String email;

    @Column(name = COLUMN_EXPIRED_TIME_NAME)
    private LocalDateTime expiredTime;

    @Column(name = COLUMN_IS_LOCKED_NAME, nullable = false)
    private Boolean locked = false;

    @Column(name = COLUMN_IS_ENABLED_NAME, nullable = false)
    private Boolean enabled = false;

    @ToString.Exclude
    @OneToOne(mappedBy = UserPassword.JOIN_COLUMN_USER_MAPPED_NAME, orphanRemoval = true,
            cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JsonIgnoreProperties(UserPassword.JOIN_COLUMN_USER_MAPPED_NAME)
    private UserPassword userPassword;

    @ToString.Exclude
    @OneToMany(mappedBy = UserToRole.JOIN_COLUMN_USER_MAPPED_NAME, orphanRemoval = true,
            cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JsonIgnoreProperties(UserToRole.JOIN_COLUMN_USER_MAPPED_NAME)
    private Set<UserToRole> userToRoles = new LinkedHashSet<>();
}

BaseEntity 父类

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.Hibernate;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;

@Getter
@Setter
@ToString
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
    public static final String COLUMN_ID_NAME = "id";
    @Serial
    private static final long serialVersionUID = 9146658035652051074L;
    public static final String COLUMN_CREATED_AT_NAME = "created_at";
    public static final String COLUMN_UPDATED_AT_NAME = "updated_at";
    public static final String COLUMN_VERSION_NAME = "version";
    public static final String COLUMN_DELETED_NAME = "deleted";
    public static final String WHERE_DELETED_SQL = "deleted=-1";
    public static final String DELETE_SQL_PREFIX = "UPDATE ";
    public static final String DELETE_SQL_SUFFIX = " SET deleted = id WHERE id=? and version=? and system_created=false";
    public static final String COLUMN_SYSTEM_CREATED_NAME = "system_created";
    public static final String COLUMN_CREATED_BY_NAME = "created_by";
    public static final String COLUMN_UPDATED_BY_NAME = "updated_by";

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = COLUMN_ID_NAME, nullable = false)
    private Long id;

    @CreatedDate
    @Column(name = COLUMN_CREATED_AT_NAME, nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = COLUMN_UPDATED_AT_NAME, nullable = false)
    private LocalDateTime updatedAt;

    @Version
    @Column(name = COLUMN_VERSION_NAME, nullable = false)
    private Integer version;

    @Column(name = COLUMN_DELETED_NAME, nullable = false)
    private Long deleted = -1L;

    @Column(name = COLUMN_SYSTEM_CREATED_NAME, nullable = false)
    private boolean systemCreated = false;
    @CreatedBy
    @Column(name = COLUMN_CREATED_BY_NAME, nullable = false, updatable = false)
    private Long createdBy;
    @LastModifiedBy
    @Column(name = COLUMN_UPDATED_BY_NAME, nullable = false)
    private Long updatedBy;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
        BaseEntity that = (BaseEntity) o;
        return id != null && Objects.equals(id, that.id);
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }

}

服务层代码

@Transactional
@Override
public UserInfoDto updateUser(UserUpdateQuery userUpdateQuery) {
    return userMapper.toDto(this.userDo(userUpdateQuery.getId(), user -> {
        if (StringUtils.hasText(userUpdateQuery.getUsername())) {
            user.setUsername(userUpdateQuery.getUsername());
        }
        if (StringUtils.hasText(userUpdateQuery.getEmail())) {
            user.setEmail(userUpdateQuery.getEmail());
        }
        return userRepository.findById(user.getId()).orElseThrow(() -> new RuntimeException());
    }));
}

protected <R> R userDo(Long id, Function<User, R> function) {
    Optional<User> userOptional = userRepository.findById(id);
    User user = userOptional.orElseThrow(() -> new DataNotExistException("exception.admin.user_not_exist"));
    return function.apply(user);
}

仓库层代码

public interface UserRepository extends JpaRepository<User, Long>,
        JpaSpecificationExecutor<User>, PagingAndSortingRepository<User, Long> {
    <T> Optional<T> findUserById(Long id, Class<T> type);

    <T> Optional<T> findUserByUsername(String username, Class<T> type);

    <T> Optional<T> findUserByEmail(String email, Class<T> type);
}

已尝试方案与需求

已尝试调用userRepository.save()和flush()方法,均无法解决问题。需求是在同一事务内更新实体后,能够获取到正确的@LastModifiedDate字段值。

内容的提问来源于stack exchange,提问作者Lyle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 08:13:12