JPA实体未在迁移查询中使用默认值创建

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

JPA entity not being created with default value with migration query

问题

我正在向现有的user表中添加一个名为signature的新列,使用FlywayDB进行数据库迁移,新列将具有默认值,适用于新用户和现有用户。然而,我注意到当我尝试创建和保存新用户时,出于某种原因,该字段为空。

我不知道这是否是某种缓存问题(例如,在执行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; // 这将不会创建默认的 signature
  }
}

仓库

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

....
}

我在db.migration文件夹中进行了初始的PostgreSQL数据库设置(用于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';

编辑_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; // 这将不会创建默认的 signature
  }

然而,这似乎也不起作用,并且会给出以下错误:java.lang.IllegalArgumentException: Entity not managed

编辑_2:

感谢用户@Simon Martinelli的建议,

我不得不执行以下操作才能使其工作:

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

I am adding a new column, signature, to my existing user table -- using FlyawayDB for DB migration -- where the new column will have a default value, for both new and existing users. However, I noticed that when I try creating and saving a new user, that field is null for some reason.

I don't know if is this is some caching issue (e.g. it's using some version of the user object before my V2__adding_signature.sql migration query is executed) or if something else.

Here is what I have:

Entity

@Entity
@Table(name = &quot;users&quot;)
@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

@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

@Repository
public interface UserRepository extends JpaRepository&lt;UserEntity, String&gt; {

....
}

Initial PostGres DB set up in my db.migration folder (for 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 &#39;testValueForNow&#39;;

EDIT_1:

I have also tried to use the EntityManager to fresh my entity, like so

  @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
      }

however this doesn't seem to work either, and gives me the following error: java.lang.IllegalArgumentException: Entity not managed

EDIT_2:
Thanks to user @Simon Martinelli

I had to do the following to get it to work:

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

答案1

得分: 2

默认值仅适用于数据库。

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

英文:

The default value only applies in the database.

After insert you have to refresh the entity to get the inserted default value from the database.

答案2

得分: 0

你应该在signature字段上使用@Generated 注释,像这样:

@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; // 这样不会创建默认的signature
}

当你确保数据库中不存在带有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

英文:

You should use the @Generated annotation for the signature field in this way:

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

So, hibernate will be able to fetch the property after the entity has been persisted or updated.

Another one piece of your problem is the following code:

@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
}

It is correct when you know for sure that a user with userId does not exist. But when the user with userId is actually present in the database the column signature does not fetch. So, you should correct this method in this way:

@Transactional
public UserEntity createUser(String userId)
{
   Optional&lt;User&gt; user = userRepo.findById(userId);
   if (user.isPresent()) return user.get();

   UserEntity userEntity = new UserEntity(userId);
   return userRepo.save(userEntity);
}

I guess that saveAndFlush can be replaced by save because the flushing will be done anyway in the end of createUser method as it marked as @Transactional.

huangapple
  • 本文由 发表于 2020年8月21日 20:54:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63523314.html
匿名

发表评论

匿名网友

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

确定