spring-data-jpa JPA更新未修改的列/属性

roqulrg3  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(202)

我们有一个类User,它有一个字段isMigrated,默认为0。现在在服务层,我们尝试更新用户的status(另一个属性)。
1.我们通过用户的ID(主键)获取用户。
1.我们设定新的状态。
1.我们在相应的存储库中为该特定用户调用save方法。
现在的问题是,对应于isMigrated的列(当前值为1)也被更新为0。我们显然没有更新该属性,因此我们得出的结论是,在更新过程中进入DB的是默认值,但这怎么可能呢?
我们尝试检查一些关于JPA: update only specific fields的其他信息,但在这种情况下没有帮助。我们必须使用本机查询来修复此问题。

实体类

@Entity
@Table(name = "USER_TABLE")
public class User implements Serializable {

    //  other attributes

    @Column(name = "USER_STATUS")
    private int status;

    //  [1 = migrated, 0 = new (default)]
    @Column(name = "IS_MIGRATED")
    private int isMigrated = 0;

    //  getters and setters
}

存储库

@Repository
public interface UserRepository
        extends PagingAndSortingRepository<User, String>, JpaSpecificationExecutor<User> {
}

有关如何提取用户的更新

用户通过ID从请求中获取,我们将通过只获取所需字段来连接两个其他表,从而运行一个本地查询。因此,在获取过程中,我们不使用isMigrated列,而且该属性(调用层次结构)仅在创建用户时设置,这是一个不同的API。
获取用户后,它的引用被设置为另一个bean RequestInfo的一个属性,其作用域为Request(@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)),该属性将在整个API中使用。现在,在服务层中,我们使用RequestInfo获取用户,然后设置新的状态并调用save方法。

@Query(value = "select um.USER_ID,FIRST_NAME,LAST_NAME,USER_STATUS,um.MOB,EMAIL_ID,ENTITY_TYPE,PROFILE_ID,PARENT_ID,SOFT_DELETE, NATIONALITY_ID,DOB,GENDER,FATHER_NAME,QUALIFICATION,LANGUAGE_ID,DESIGNATION,CONTRACT_START_DATE, "
        + "CONTRACT_END_DATE, REGISTERED_DATE,ACTIVATED_DATE,CREATED_BY,CONTACT_EMAIL_ID,CONTACT_MSISDN,ERP_CODE, "
        + "LAST_FINANCIAL_TXN_DATE,MM_APP_VERSION,MM_APP_LAST_UPDATE_DATE,OFFICIAL_EMAIL_ID,SRC_OF_FUND,POW, JOB_NAME,JOB_STARTED,MONTHLY_INCOME, "
        + "MARITAL_STATUS,SALUTE,HIER_ID,LABEL_ID,PROFILE_PICTURE_ID,DOC_NAME,lm.ID , lm.ALERT_DATE , lm.BLOCKED_DATE , lm.IS_FORCE_PIN , lm.IS_FORCE_PASSWORD , lm.LAST_LOGIN_TIME , lm.LAST_NOTIFIED_DATE ,lm.PASSWORD , lm.PIN ,  "
        + "lm.WRONG_PIN_ATTEMPT , lm.PIN_EXPIRY_DATE , lm.WRONG_PWD_ATTEMPT , lm.PWD_EXPIRY_DATE , lm.USER_NAME, am.ID aid, am.ADDRESS_1, am.ADDRESS_2, am.ADDRESS_TYPE, am.CITY , am.PIN_CODE , am.POST_BOX , am.STATE , am.STREET_NAME,ui.ID_TYPE, ui.ID_NO,um.NOTIFICATION_SERVICE_TYPE "
        + " FROM USER_TABLE um inner join USER_LOGIN_TABLE lm ON um.USER_ID=lm.USER_ID and um.USER_ID=?1 "
        + " left outer join USER_ADDRESS_TABLE am ON um.USER_ID=am.USER_ID left outer join USER_IDENTI_CATION_DETAILS ui ON um.USER_ID = ui.USER_ID", nativeQuery = true)
List<Object[]> getUserByUserId(String userId);

有关获取用户时未提取的列的更新-由Ratul Sharker询问

有几列我们没有读取,如deviceIddeviceIp等。这些是String类型,因此,即使我们不向这些字段提供默认值,null也将被指定为默认值。因此,我的疑问是,为什么默认值被插入到isMigrated中,而为什么相同的值没有插入到其他两个字段中,因为这三个字段在上面的查询中没有被获取,对吗?

vaj7vani

vaj7vani1#

JPA的工作方式如下

  • 从数据库中提取对象。
  • 获取完整的对象,或者将延迟加载的jpa提供程序的代理对象附加到获取的对象。
  • 做你想做的任何修改。
  • 您调用save方法,它运行一些内部脏检查。
  • 基于脏检查,它生成UPDATE sql语句。

在您的示例中,在阅读实体期间没有获取isMigrated字段,并且您的实体默认值将其设置为0
有哪些选项
1.如果您不想更新jpa层中的isMigrated字段,请考虑将列注解定义为@Column( name= "IS_MIGRATED", updatable = false)
1.提取isMigrated字段,结转直到保存实体。
1.(有史以来最糟糕的主意)不要使用save方法,而是在存储库接口内编写冗长的JPQL,如下所示

@Modifying
@Query("UPDATE User user SET user.name = :name /*other set statements*/ WHERE user.id = :userId")
public void updateUser(@Param("name") String name, /*other properties*/, @Param("userId") int userId);

对问题中更新的答复

您提到的字段,deviceIddeviceIP这些类型的字段是要从HTTPServlerRequest更新的。我需要看到您的完整代码。我猜在保存用户之前,这些属性是从HTTPServletRequest参数设置的。有很多方法可以设置这些字段

  • 使用@PrePersist@PreUpdate内的实体。
  • 从服务层或控制器层内部的HTTPServletRequest设置这些值。

这就是为什么这些值被设置为正确的值,这与isMigrated字段不同。

相关问题