Java Hibernate/JPA:只有一个条目可以有特定的字段值

Java Hibernate/JPA:只有一个条目可以有特定的字段值,java,hibernate,jpa,jakarta-ee,jdbc,Java,Hibernate,Jpa,Jakarta Ee,Jdbc,我需要的东西,似乎不是那么具体,但无论如何,我无法拿出好的和复杂的解决方案 假设我有一个非常简单的hibernate/jpa实体: @Entity(name="entity") public class Type { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @Column(unique = true, nullable = false) private Str

我需要的东西,似乎不是那么具体,但无论如何,我无法拿出好的和复杂的解决方案

假设我有一个非常简单的hibernate/jpa实体:

@Entity(name="entity")
public class Type {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;

    @Column(unique = false, nullable = false)
    private boolean defaultType;
}
我需要的是以某种方式注释defaultType字段,以便只有(确切地说)一个持久化实体将该值作为true。当新实体以此defaultType为true进行持久化时,必须更改旧实体(defaultType=true),并将其defaultType值更改为false。此外,如果任何实体发生更改(其defaultType更改为true),则应应用相同的规则

据我所知,这可以在业务逻辑内部实现(例如在DAO层),使用DB触发器或hibernates拦截器或事件(如果有其他方法,请告诉我)。我尝试使用DAO解决方案,但这是一种糟糕的解决方案,因为它可以被绕过,对于这样简单的操作来说,它真的很笨拙。DB触发器不能与hibernate/jpa注释一起添加(如果我没有弄错的话),我不知道如何使用hibernate拦截器/事件来实现此功能


那么,这个问题的最佳解决方案是什么呢?

您需要在JPA中使用回调方法,例如,或者:

@Entity
@EntityListeners(com.acme.AlertMonitor.class) // set callback method in another class
public class Account {
   Long accountId;
   Integer balance;
   boolean preferred;

   @Id
   public Long getAccountId() { ... }
   ...
   public Integer getBalance() { ... }
    ...
   @Transient 
   public boolean isPreferred() { ... }
   ...

   public void deposit(Integer amount) { ... }
   public Integer withdraw(Integer amount) throws NSFException {... }

   @PreUpdate // callback method in some class 
   protected void validateCreate() {
     if (getBalance() < MIN_REQUIRED_BALANCE)
        throw new AccountException("Insufficient balance to open an
account");
   }

  @PostUpdate  // callback method in some class 
  protected void adjustPreferredStatus() {
      preferred =
      (getBalance() >= AccountManager.getPreferredStatusLevel());
   }
}

// callback method in another class 
public class AlertMonitor {
   @PreUpdate  // callback method in another class
   public void updateAccountAlert(Account acct) {
      Alerts.sendMarketingInfo(acct.getAccountId(), acct.getBalance());
   }
}
@实体
@EntityListeners(com.acme.AlertMonitor.class)//在另一个类中设置回调方法
公共类帐户{
长帐号;
整数平衡;
布尔型优先;
@身份证
公共长getAccountId(){…}
...
公共整数getBalance(){…}
...
@短暂的
公共布尔值({…}
...
公共无效存款(整数金额){…}
公共整数提取(整数金额)抛出NSFEException{…}
@PreUpdate//某个类中的回调方法
受保护的void validateCreate(){
如果(getBalance()=AccountManager.getPreferredStatusLevel());
}
}
//另一个类中的回调方法
公共类警报监视器{
@PreUpdate//另一个类中的回调方法
公共无效更新帐户警报(帐户帐户){
Alerts.sendMarketingInfo(账户getAccountId(),账户getBalance());
}
}
更新:关于您的问题,如果我不知道您想要什么,此代码可能会帮助您:

@Entity(name="entity")
@EntityListeners(com.yourpackage.TypeListner.class)
public class Type {

    ...
@Column(unique = false, nullable = false)
private boolean defaultType;
}

public class TypeListner {

pivate static Type objectWithTrue = null;

public void init() { // call this method when application is started 
    List<Type> results = entityManager
                  .createQuery("from Type", Type.class)
                  .getResultList();
    for(Type type: results) {
         if(type.getDefaultType()) {
             objectWithTrue = type;      
         }  
    }
}

private void changeDefaultType(Type changed) {
    if(changed.getDefaultType()) {
       if(changed != objectWithTrue && objectWithTrue != null) {        
         objectWithTrue.setDefaultType(false);
       }
       objectWithTrue = changed;
   }
}

@PostPresist  
public void newType(Type changed) {
   changeDefaultType(changed);
}

@PostUpdate
public void updateType(Type changed) {
   changeDefaultType(changed);
}

@PreRemove
public void removeType(Type changed) {
if(changed.getDefaultType() && objectWithTrue == changed) {
      objectWithTrue = null;
    }
}
@Entity(name=“Entity”)
@EntityListeners(com.yourpackage.TypeListner.class)
公共类类型{
...
@列(unique=false,nullable=false)
私有布尔型;
}
公共类类型列表器{
pivate静态类型objectWithTrue=null;
public void init(){//在应用程序启动时调用此方法
列表结果=entityManager
.createQuery(“从类型”,类型.class)
.getResultList();
对于(类型:结果){
if(type.getDefaultType()){
objectWithTrue=type;
}  
}
}
私有void changefaulttype(类型已更改){
如果(已更改。getDefaultType()){
如果(已更改!=objectWithTrue&&objectWithTrue!=null){
objectWithTrue.setDefaultType(false);
}
objectWithTrue=changed;
}
}
@后抵抗
public void newType(类型已更改){
changeDefaultType(已更改);
}
@假想
public void updateType(类型已更改){
changeDefaultType(已更改);
}
@预移除
public void removeType(类型已更改){
if(changed.getDefaultType()&&objectWithTrue==changed){
objectWithTrue=null;
}
}

您可以使用listner@PreUpdate和@PrePresist,并且每次都覆盖所有类型对象,而不存储任何变量(这对性能来说不如第一个示例好,但更可靠):

@PreUpdate
void updateType(类型已更改){
if(已更改。getDefaultType()
列表结果=entityManager
.createQuery(“从类型”,类型.class)
.getResultList();
对于(类型:结果){
if(已更改!=type&&type.getDefaultType(){
type.setDefaultType(false);
}  
}
}
}

答案已经回答,但我想补充一点:在数据库中设置约束是有很好的理由的;然后,无论数据在数据库中如何结束,数据都会受到保护。如果从JPA的角度解决这个问题,那么只有在使用JPA时,保护才到位。只是想一想,我不知道您的环境,当前的需求需求或未来需求。你能将你的例子改编成这个问题吗?从我的观点来看,使用
@PreUpdate
很难实现这一点。而
@postpdate
在这里也没有帮助,因为在那一点上已经发生了错误的更新。你也应该提到
@PrePersist
。不完全是这样的。有很多您的解决方案有问题的地方:缺少初始化(重新启动Web应用程序后,
objectWithTrue
null
)。
changeDefaultType()
将不起作用,因为只有当
objectWithTrue
!=null时才会设置它,这是永远不会设置的。即使您解决了这个问题,
objectWithTrue.setDefaultType(false);
也不会起作用,因为会话结束后,您的静态实例将被分离(如果您在
postapdate
中调用它,就会发生这种情况)。添加init()方法以获取所有类型对象并找到deftype=true的位置,并在应用程序启动时调用此方法,会出现什么问题?添加此init()最后一个示例我提到的另外两件事呢?它们仍然适用。我要说的是,用
EntityListeners
实现这一点很困难。
@PreUpdate 
void updateType(Type changed) {
    if(changed.getDefaultType()
        List<Type> results = entityManager
                  .createQuery("from Type", Type.class)
                  .getResultList();
       for(Type type: results) {
           if(changed != type && type.getDefaultType()) {
             type.setDefaultType(false);    
           }  
       }
    }
 }