问题
I am using Spring - Hibernate to control models in my application. The models are linked to each other (one-to-many, many-to-many, almost kinds of relationships), and now I have a problem when deleting one entity which is being used by other entities. The problem is I want to show the detail message that informs exactly what other objects (type, name) are using the entity that I am going to delete. It's not the common message about Constraint violation that Hibernate throws me.
For example: Car --> Person, House --> Person; then when I delete one Person who has a car and house, the message will show "There are Car (named Ford Mustang) and House (named MyHouse) linked to this Person".
1. So is there any method from Hibernate support this requirement? I guess there's no implementation for this specific requirement.
2. If not any utility available for this problem, I am thinking about below solutions:
- in each entity class (i.e Person), I will define all checking method that detect linking from the this entity to other entities, for example:
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>
}
}
-and then, in service before deleting Person entity, I will use reflection mechanism to collect results from checking methods (whose name is started with "getLinkedXXX"), and build the detail error messages.
So is this solution good? About the performance, and the convention of MVC (because I have to query data inside model class)?
Thank you for your help.
回答1:
One (not so simple) approach is to scan your entity class for @OneToMany or @ManyToMany annotated fields and perform checking so neat error message can be provided to user. Following sample code assumes you only annotate the field, not the getters method, eg:
public class Person {
@OneToMany(..)
private List<House> houses;
//...
}
First get the list of all fields using reflection:
Fields[] fields = Person.class.getDeclaredFields();
Then iterate and check for @OneToMany or @ManyToMany annotations
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 ...
}
}
The value of a field of a particular person object can be obtained using:
Person p = // fetch a person ..
Field f = // assume f is the "List<House> houses" field
List<House> houses = (List<House>) f.get(p);
回答2:
I had a similar problem, I had to check if an entity could be safely deleted to avoid foreign key constraint violations. This is how I solved:
First, I created an annotation to mark the entity that needs to be checked before deletion:
@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 { };
}
Then I created another annotation to be applied to any method that will be used to check if the entity can be deleted:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckForDelete {
}
In the entity class I used these annotations like this:
@SafeDelete(message = "Couldn't delete this entity due to...")
public class MyEntity {
@CheckForDelete
public Boolean checkForDelete() {
// Insert your business logic here
return true;
}
}
And finally, the validator for the SafeDelete annotation:
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);
}
}
Then you can follow the answer of this question to apply the validation to deletion only.
来源:https://stackoverflow.com/questions/17392207/hibernate-check-deletion-constraints