【问题标题】:JPA entity not being created with default value with migration query未使用迁移查询的默认值创建 JPA 实体
【发布时间】:2020-08-21 12:54:14
【问题描述】:

我在现有的user 表中添加了一个新列signature——使用FlyawayDB 进行数据库迁移——其中新列对于新用户和现有用户都有一个默认值。但是,我注意到当我尝试创建和保存新用户时,该字段由于某种原因为空。

我不知道这是否是一些缓存问题(例如,它在执行我的 V2__adding_signature.sql 迁移查询之前使用了用户对象的某个版本)还是其他问题。

这是我所拥有的:

实体

@Entity
@Table(name = "users")
@Data
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@DynamicUpdate
public class UserEntity {
  @Id private String userId;

  @Column(insertable = false, updatable = false)
  private String signature;

  public UserEntity(String userId) {
    this.userId = userId;
  }
}

服务

@Service
@AllArgsConstructor
public class UserService {

  private final UserRepository userRepo;

  @Transactional
  public UserEntity createUser(String userId) {
    UserEntity userEntity = new UserEntity(userId);
    userRepo.saveAndFlush(userMappingEntity);
    return userEntity; // this will not have a the default signature created
  }
}

存储库

@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {

....
}

在我的 db.migration 文件夹中设置的初始 PostGres DB(用于 Flywaydb)

V1__initial_creation.sql

CREATE TABLE IF NOT EXISTS users (
  user_id TEXT PRIMARY KEY
);

V2__adding_signature.sql

ALTER TABLE users
    ADD COLUMN signature TEXT NOT NULL DEFAULT 'testValueForNow';

EDIT_1:

我也尝试过使用 EntityManager 来刷新我的实体,就像这样

  @PersistenceContext private EntityManager entityManager;

      @Transactional
      public UserEntity createUser(String userId) {
        UserEntity userEntity = new UserEntity(userId);
        userRepo.saveAndFlush(userEntity);
        entityManager.refresh(userEntity);

        return userEntity; // this will not have a the default signature created
      }

但这似乎也不起作用,并给我以下错误:java.lang.IllegalArgumentException: Entity not managed

EDIT_2: 感谢用户@Simon Martinelli

我必须执行以下操作才能使其正常工作:

   UserEntity userEntity = new UserEntity(userId);
   userEntity = userRepo.saveAndFlush(userEntity);
   entityManager.refresh(entityManager.merge(userEntity));
   return userEntity;

【问题讨论】:

    标签: java spring postgresql hibernate jpa


    【解决方案1】:

    默认值仅适用于数据库。

    插入后,您必须刷新实体才能从数据库中获取插入的默认值。

    【讨论】:

    • 刷新实体的正确方法是什么?我尝试添加一个 EntityManager,然后通过以下方式刷新我的实体:@PersistenceContext private EntityManager entityManager; entityManager.refresh(userEntity);但由于某种原因,这似乎给了我一个 HTTP 409..
    • 实际上它返回一个 java.lang.IllegalArgumentException: Entity not managed
    • 你叫persist还是merge?如果合并,您必须传递合并返回的实体
    • 我试过了: entityManager.refresh(entityManager.merge(userEntity));在我保存并刷新实体之后
    • entityManager.refresh(userRepo.saveAndFlush(entity))
    【解决方案2】:

    您应该以这种方式将@Generated annotation 用于signature 字段:

    @Generated(value = GenerationTime.ALWAYS)
    @Column(insertable = false, updatable = false)
    private String signature;
    

    因此,在实体被持久化或更新后,hibernate 将能够获取属性。

    您的另一个问题是以下代码:

    @Transactional
    public UserEntity createUser(String userId) {
       UserEntity userEntity = new UserEntity(userId);
       userRepo.saveAndFlush(userMappingEntity);
       return userEntity; // this will not have a the default signature created
    }
    

    当您确定具有userId 的用户不存在时,这是正确的。 但是当userId 的用户实际存在于数据库中时,signature 列不会获取。 因此,您应该以这种方式更正此方法:

    @Transactional
    public UserEntity createUser(String userId)
    {
       Optional<User> user = userRepo.findById(userId);
       if (user.isPresent()) return user.get();
    
       UserEntity userEntity = new UserEntity(userId);
       return userRepo.save(userEntity);
    }
    

    我猜saveAndFlush 可以替换为save,因为无论如何都会在createUser 方法的末尾完成刷新,因为它标记为@Transactional

    【讨论】:

    • 我刚刚尝试了休眠注释,但不幸的是,它仍然没有生成签名。除此之外,我还尝试通过 entityManager.refresh(entityManager.merge(userEntity)); 刷新缓存。但由于某种原因仍然没有运气..
    • 其实我的建议应该可行。请尝试使用它并建议更正createUser 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2015-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2012-09-09
    • 1970-01-01
    相关资源
    最近更新 更多