英文:
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 = "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
@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<UserEntity, String> {
....
}
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 'testValueForNow';
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<User> 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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论