持久/合并上的 JPA 树值传播

Posted

技术标签:

【中文标题】持久/合并上的 JPA 树值传播【英文标题】:JPA tree value propagation on persist/merge 【发布时间】:2014-09-27 10:52:24 【问题描述】:

我有

@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;

    ...

我想将owners 传播给坚持/合并的孩子。规则是:

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/事务方法中

我想知道是否有一些巧妙的方法可以做到这一点。

我不是数据库专家,但是否有可能实现与存储过程/触发器/任何东西的一致性?

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

我正在使用 EclipseLink 2.5.2 和 mysql 5.6

谢谢。

【问题讨论】:

EclipseLink 2.5.2 具有 JPA 2.1。 你是对的。谢谢。 【参考方案1】:

我实现了自己的 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;

【讨论】:

以上是关于持久/合并上的 JPA 树值传播的主要内容,如果未能解决你的问题,请参考以下文章

处理 JPA 合并的最佳方法?

附加实体是否必须在JPA中合并?

Spring @Transactional 合并和持久化问题

JPA:持久化/更新孩子期间的事件通知?

使用合并持久性单元管理器后未调用休眠事件侦听器

Jpa中的persist方法和merge方法都有哪些区别,帮我举一个例子解释下?