JPA:orphanRemoval = true有时会在删除时抛出NullPointerException

时间:2014-08-19 21:05:06

标签: java jpa nullpointerexception eclipselink

我正在使用JPA 2.1,EclipseLink 2.5.0,SQLite3数据库和Swing制作应用程序。

我有两个实体EntityClientEntityPhone,其中第一个实体与第二个实体有OneToMany关系。两个实体都有自己的主键,它们的关系由JoinTable表示。要访问和保留所有数据,我使用通用DAO

我试图在级联上删除,所以如果删除客户端,他的手机也会被删除。我已经通过向orphanRemoval=true注释和cascade={CascadeType.ALL}注释添加OneToManyCascadeOnDelete来实现此目的。

问题在于,由于orphanRemoval=true,如果我插入一个新客户端,然后我尝试删除它,我会得到一个NullPointerException。但是,如果我关闭应用程序并再次启动它,它将让我删除客户端没有任何问题。

这些是实体:

@Entity
@Table(name="clientes")
public class EntityClient {

    @Id
    @Column(name="id_cliente")
    private Integer idClient;

    @Column(name="nombre")
    private String name;

    @Column(name="apellidos")
    private String surname;

    @OneToMany(mappedBy="client",
               targetEntity=EntityPhone.class,
               fetch=FetchType.EAGER,
               orphanRemoval=true,
               cascade={CascadeType.ALL})
    @CascadeOnDelete
    private ArrayList<EntityPhone> phones;

    //The rest of the fields and the getters and setters...
}
@Entity
@Table(name="telefonos")
public class EntityPhone {

    @Id
    @Column(name="id_telefono")
    private Integer idPhone;

    @Column(name="telefono")
    private String phone;

    @Column(name="descripcion")
    private String description;

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinTable(name="clientes_telefonos",
               joinColumns=@JoinColumn(name="id_telefono",
                                       referencedColumnName="id_telefono"),
               inverseJoinColumns=@JoinColumn(name="id_cliente",
                                              referencedColumnName="id_cliente"))
    private EntityClient client;

    //Getters and setters...
}

这是通用DAO,它显然有一个删除方法:

public abstract class DAO<Entity, ID extends Serializable> {

    private Class<Entity> entityClass;

    private final static EntityManagerFactory emf = Persistence.createEntityManagerFactory("Database_Name");
    private final static EntityManager em = emf.createEntityManager();


    public void delete(ID id) throws PersistenceException {

            em.getTransaction().begin();

            Entity row = em.find(entityClass, id);

            em.remove(row);

            em.getTransaction().commit();
    }

    //...
}

这是我的例外:

javax.persistence.RollbackException: java.lang.NullPointerException
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
    at model.dao.DAO.delete(DAO.java:127)
    at controller.delete.ControllerDeleteClient.actionPerformed(ControllerDeleteClient.java:39)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NullPointerException
    at org.eclipse.persistence.internal.queries.CollectionContainerPolicy.iteratorFor(CollectionContainerPolicy.java:150)
    at org.eclipse.persistence.mappings.CollectionMapping.recordPrivateOwnedRemovals(CollectionMapping.java:1724)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.recordPrivateOwnedRemovals(ObjectBuilder.java:3445)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:687)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1514)
    at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:277)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1167)
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:132)
    ... 38 more

1 个答案:

答案 0 :(得分:2)

你使用静态来保存你的EntityManager这是一个坏主意 - 它不是线程安全的。您需要为每个上下文获取一个新的:

public void delete(ID id) throws PersistenceException {
    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    Entity row = em.find(entityClass, id);
    em.remove(row);
    em.getTransaction().commit();
}

这也可能有助于您当前的问题,这似乎是由于陈旧的缓存。您正在使用null电话集合管理要删除的EntityClient实例的一些方法。您可以提交一个错误,让EclipseLink处理null集合,但它实际上永远不应该为null。由于此EntityManager是长期存在的(类上是静态的),因此您必须跟踪此实体的管理和创建方式,并且该集合与引用它的EntityPhone.client关系保持同步。重新启动应用程序会清除缓存,因此查找操作将构建EntityClient的新实例,并正确填充集合,从而避免使用NPE。您可以通过调用em.refresh(entityClass)来获得相同的效果,但是跟踪集合如何为null可能会为您提供更好的性能。