JPA实体版本控制实现:无Setter解决持久化异常
解决JPA乐观锁版本字段无法更新的问题(避免不必要Setter)
异常
java.lang.IllegalStateException: Cannot set property version because no setter, no wither and it's not part of the persistence constructor的核心原因是:Hibernate需要更新@Version字段实现乐观锁,但你的version是final修饰且未纳入持久化构造函数,同时没有提供Setter或符合规则的Wither方法,导致框架无法修改该字段值。
以下是三种符合你"避免不必要Setter"需求的解决方案:
方案一:将Version字段纳入持久化构造函数(保留不可变性)
这种方式完全符合不可变实体设计,Hibernate会通过构造函数初始化Version,更新时通过反射修改final字段(Hibernate对不可变实体的Version字段有特殊处理逻辑)。
修改后的实体代码:
@Table("merchant") public class Merchant { @Id private final MerchantId id; private final String name; private final MerchantKeyId merchantKeyId; private final String username; private final String password; private final String webhookUrl; @Version private final int version; @PersistenceCreator public Merchant(String id, String name, Integer merchantKeyId, String username, String password, String webhookUrl, int version) { this.id = new MerchantId(id); this.name = name; this.merchantKeyId = new MerchantKeyId(merchantKeyId); this.username = username; this.password = password; this.webhookUrl = webhookUrl; this.version = version; } // 保留Version的Getter public int getVersion() { return version; } // 其他Getter省略 }
方案二:使用Wither方法(保持不可变性)
Wither方法是不可变实体的常用更新方式,返回一个新的实体对象,Hibernate会自动识别以with开头的方法来处理Version更新。
修改后的实体代码:
@Table("merchant") public class Merchant { @Id private final MerchantId id; private final String name; private final MerchantKeyId merchantKeyId; private final String username; private final String password; private final String webhookUrl; @Version private int version; @PersistenceCreator public Merchant(String id, String name, Integer merchantKeyId, String username, String password, String webhookUrl, int version) { this.id = new MerchantId(id); this.name = name; this.merchantKeyId = new MerchantKeyId(merchantKeyId); this.username = username; this.password = password; this.webhookUrl = webhookUrl; this.version = version; } // Wither方法,Hibernate会自动识别 public Merchant withVersion(int version) { return new Merchant(this.id.getValue(), this.name, this.merchantKeyId.getValue(), this.username, this.password, this.webhookUrl, version); } // 保留Version的Getter public int getVersion() { return version; } // 其他Getter省略 }
方案三:使用字段访问策略(Hibernate 6+适用)
如果使用Hibernate 6及以上版本,可以通过@PropertyAccessor指定直接访问字段,无需Setter或构造函数参数,但Version字段不能是final(需要框架更新)。
修改后的实体代码:
@Table("merchant") @PropertyAccessor(PropertyAccessStrategy.FIELD) public class Merchant { @Id private final MerchantId id; private final String name; private final MerchantKeyId merchantKeyId; private final String username; private final String password; private final String webhookUrl; @Version private int version = 0; @PersistenceCreator public Merchant(String id, String name, Integer merchantKeyId, String username, String password, String webhookUrl) { this.id = new MerchantId(id); this.name = name; this.merchantKeyId = new MerchantKeyId(merchantKeyId); this.username = username; this.password = password; this.webhookUrl = webhookUrl; } // 保留Version的Getter public int getVersion() { return version; } // 其他Getter省略 }
内容的提问来源于stack exchange,提问作者fragilepriCe
相关产品推荐
相关产品推荐

