【问题标题】:Hibernate check deletion constraintsHibernate 检查删除约束
【发布时间】:2013-06-27 20:24:24
【问题描述】:

我正在使用 Spring - Hibernate 来控制我的应用程序中的模型。这些模型是相互链接的(一对多、多对多、几乎是各种关系),现在我在删除一个被其他实体使用的实体时遇到了问题。问题是我想显示详细消息,确切地告知其他对象(类型、名称)正在使用我要删除的实体。这不是 Hibernate 向我抛出的关于违反约束的常见信息。
例如:Car --> Person, House --> Person;然后当我删除一个拥有汽车和房屋的人时,消息将显示“有汽车(名为福特野马)和房屋(名为 MyHouse)与此人相关联”。
1. 那么Hibernate有什么方法支持这个要求吗?我想这个特定要求没有实现。
2. 如果没有任何实用程序可用于此问题,我正在考虑以下解决方案:
- 在每个实体类(即Person)中,我将定义所有检测从该实体到其他实体的链接的检查方法,例如:

class Person {
     // Properties
     // Checking methods, return type and name of linked objects
     public Map<String, String> getLinkedCars() {
         // Query to DB to get linked cars
         // return a Map contain Class name along with object name <Car, Ford Mustang>
     }

     public Map<String, String> getLinkedHouses() {
         // Query to DB to get linked houses
         // return a Map contain Class name along with object name <House, MyHouse>
     }
}

-然后,在删除Person实体之前的服务中,我将使用反射机制收集检查方法(名称以“getLinkedXXX”开头)的结果,并构建详细的错误消息。
那么这个解决方案好吗?关于 MVC 的性能和约定(因为我必须在模型类中查询数据)?
感谢您的帮助。

【问题讨论】:

  • 使用 cascade=CascadeType.REMOVE
  • @MasudCSECUET 不会直接删除它而不提供任何错误消息信息吗?
  • 使关联是双向的,您将在 Person 实体中将汽车和房屋的集合直接链接到汽车。也就是说,我认为这并不是在每种情况下都可以做的合理的事情。如果您删除了一个打了 3000 次电话并有 500 个朋友的人,您不想列出所有这些实体。
  • 不要在模型对象中进行数据库调用。在您的操作中使用 dao/helper。
  • @JBNizet:我只是在错误消息中显示一些顶级实体,而不是全部内容。那么有什么方法可以检测到该实体的外部链接中的所有外部约束?

标签: java spring hibernate spring-mvc constraints


【解决方案1】:

一种(不那么简单)的方法是扫描您的实体类以查找@OneToMany@ManyToMany 注释字段并执行检查,以便可以向用户提供简洁的错误消息。以下示例代码假设您只注释字段,而不是 getters 方法,例如:

public class Person {
  @OneToMany(..)
  private List<House> houses;
  //...
}

首先使用反射获取所有字段的列表:

Fields[] fields = Person.class.getDeclaredFields();

然后迭代并检查@OneToMany@ManyToMany 注释

for(Field f : fields) {
   if( f.getAnnotation(OneToMany.class) != null || 
       f.getAnnotation(ManyToMany.class) != null) {
     // Here you know f has to be checked before the person is deleted ...
   }
}

可以使用以下方法获取特定人员对象的字段值:

Person p = // fetch a person ..
Field f = // assume f is the "List<House> houses" field
List<House> houses = (List<House>) f.get(p);

【讨论】:

    【解决方案2】:

    我遇到了类似的问题,我必须检查是否可以安全地删除实体以避免违反外键约束。我就是这样解决的:

    首先,我创建了一个注解来标记删除前需要检查的实体:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(validatedBy = SafeDeleteValidator.class)
    public @interface SafeDelete {
      String message() default "{lima.jefferson.SafeDelete.message}";
      Class<?>[] groups() default { };
      Class<? extends Payload>[] payload() default { };
    }
    

    然后我创建了另一个注释以应用于将用于检查实体是否可以删除的任何方法:

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CheckForDelete {
    }
    

    在实体类中,我这样使用这些注释:

    @SafeDelete(message = "Couldn't delete this entity due to...")
    public class MyEntity {
    
        @CheckForDelete
        public Boolean checkForDelete() {
            // Insert your business logic here
            return true;
        }
    
    }
    

    最后,SafeDelete 注释的验证器:

    public class SafeDeleteValidator implements ConstraintValidator<SafeDelete, Object> {
    
      @Override
      public void initialize(SafeDelete constraintAnnotation) {
    
      }
    
      @Override
      public boolean isValid(Object object, ConstraintValidatorContext context) {
    
        Method[] methods = object.getClass().getMethods();
    
        return Arrays.stream(methods)
              .filter(m -> m.getAnnotation(CheckForDelete.class) != null)
              // Deal with the exception here 
              .map(m -> (Boolean) m.invoke(object))
              .reduce(true, (a, b) -> a && b);
      }
    
    }
    

    然后您可以按照question 的答案将验证仅应用于删除。

    【讨论】:

      猜你喜欢
      • 2020-09-20
      • 1970-01-01
      • 1970-01-01
      • 2020-09-22
      • 1970-01-01
      • 2018-04-13
      • 2016-12-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多