Home > other >  How to get all referenced entities for a specific entity instance in JPA
How to get all referenced entities for a specific entity instance in JPA

Time:09-28

In my project, I'm trying to implement some Entities with one parent and multiple children by JPA & Hibernate like this:

/********** parent type **********/
@Entity
@Table(name = "t_parent")
public class Parent {
    @Id
    @Column(name = "f_id")
    private Long id;
}

/********** children types **********/
@Entity
@Table(name = "t_child_a")
public class ChildA {
    @Id
    @Column(name = "f_id")
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "f_parent")
    private Parent parent;
}

@Entity
@Table(name = "t_child_b")
public class ChildB {
    @Id
    @Column(name = "f_id")
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "f_parent")
    private Parent parent;
}

Because there will be more Children Types in the future, so relationships are only declared on the children side by annotation "ManyToOne", and there is no corresponding "OneToMany" on the parent side.

When delete a parent entity, any exist relationship will cause ConstraintViolationException from database.

My purpose is to find out that is there any children instance who has referenced to a specific parent instance, so i can give a clear and meaningful message to user, or any best practices for such situation?

CodePudding user response:

As JPA deals with entities not tables you'll likely have to use multiple queries to check if each child type has a reference to the parent that is penned for deletion. You can check it like this using JPQL

boolean childAExists = em.createQuery("SELECT CASE WHEN COUNT(c) > 0 THEN TRUE ELSE FALSE END FROM ChildA c WHERE c.parent = :parent", Boolean.class)
                .setParameter("parent", parent)
                .getSingleResult();

boolean childBExists = em.createQuery("SELECT CASE WHEN COUNT(c) > 0 THEN TRUE ELSE FALSE END FROM ChildB c WHERE c.parent = :parent", Boolean.class)
                .setParameter("parent", parent)
                .getSingleResult();

If you're using Spring Data JPA you can probably simplify to this

boolean exists = existsByParent(parent);

If you don't want to perform multiple queries then a native query is your best bet

If you want to do it more generically you have a few options

1. Catch the exception and return a meaningful message to the client

2. Use reflection to check which classes have the Parent class as a member and build up your queries dynamically for those entities that have a parent
    public static boolean hasParent(Class<?> clazz, Class<?> parentClazz) {
        for (Field field : clazz.getDeclaredFields()) {
              if (field.getType().equals(parentClazz)) {
                return true;
            }
        }
        return false;
    }
  1. Use inheritance mapping so you have a single table with a link to the parent table and separate tables for each of the children inheritance mapping

The below config will create tables child, childA & childB. The PK of child A and B will be the FK of child. So your query just checks if the child table has the parent_id it ignores childA and childb tables. All you have to do when adding a new childentity is extend child. Bear in mind that this is not the most performant of solutions so you may want to look at other inheritance mapping options

// Example

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Child {
    @Id
    private Long childId;

    @ManyToOne
    private Parent parent;

    // getters / setters
}

@Entity
public class ChildA extends Child {
}

@Entity
public class ChildB extends Child {
}

// Query
boolean childExists = em.createQuery("SELECT CASE WHEN COUNT(c) > 0 THEN TRUE ELSE FALSE END FROM Child c WHERE c.parent = :parent", Boolean.class)
                .setParameter("parent", parent)
                .getSingleResult();
  • Related