持久化/合并时的JPA树值传播

时间:2014-09-27 10:52:24

标签: jpa jpa-2.1

我有

@Entity
public class Node
{
    @ManyToOne
    private Node parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Node> children;

    @ManyToMany
    private List<Owner> ownerList;

    ...
}

我希望在持久/合并时将owner传播给子节点。规则是:

Any node must have at least its parent node owners

我试过

@PreUpdate
public void propagateOwnersTopDown(Node node)
{
    for(Node child : node.getChildren())
    {
        for(Owner owner : node.getOwnerList())
        {
            if(!child.getOwnerList().contains(owner))
            {
                child.getOwnerList().add(owner);
            }
        }
    }
}

@PreUpdate
public void propagateOwnersBottomUp(Node node)
{
    if(node.getParent() == null) return;

    for(Owner owner : node.getParent().getOwnerList())
    {
        if(!node.getOwnerList().contains(owner))
        {
            node.getOwnerList().add(owner);
        }
    }
}

即使我知道它不合法,事实上,这些不起作用(@PreUpdate仅在根目录上调用。)

我知道我可以

  • 编写一个addOwner()方法,将所有者传播给子级
    • 但是如果其他一些代码调用node.getOwnerList().add(owner)传播被破坏

  • 显式调用propagateOwnersTopDown()或类似em.merge(node)之前的内容(通常在事务内部)
    • 但我必须将其添加到每个EJB /事务方法

我想知道是否有一些巧妙的方法来实现这一目标。

我没有数据库专家,但是可以实现与存储过程/触发器/任何内容的一致性吗?

如果为true,是否可以/合法地调用@PrePersist / @PreUpdate中的过程/触发器/任何内容?

我在EclipseLink 2.5.2和MySQL 5.6上

谢谢。

1 个答案:

答案 0 :(得分:0)

我实现了自己的Listener子系统:

protected <T extends AbstractEntity> void invokeListeners(T entity, Class<? extends Annotation> annotation)
{
    ServiceListeners serviceListeners = entity.getClass().getAnnotation(ServiceListeners.class);
    if(serviceListeners != null)
    {
        try
        {
            for(Class<?> listenerClass : serviceListeners.value())
            {
                Object instance = listenerClass.newInstance();

                Set<Method> methods = ReflectionUtils.getAllMethods(listenerClass, ReflectionUtils.withAnnotation(annotation));
                for(Method m : methods)
                {
                    m.invoke(instance, entity);
                }
            }
        }
        catch(Exception e)
        {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
}

public <T extends AbstractEntity> void create(T entity)
{
    invokeListeners(entity, OnCreate.class);

    em.persist(entity);
}

public <T extends AbstractEntity> T update(T entity)
{
    invokeListeners(entity, OnUpdate.class);

    T entity2 = em.merge(entity);
    em.flush();
    em.refresh(entity2);

    return entity2;
}