我正在使用JPA 2.1,EclipseLink 2.5.0,SQLite3数据库和Swing制作应用程序。
我有两个实体EntityClient
和EntityPhone
,其中第一个实体与第二个实体有OneToMany
关系。两个实体都有自己的主键,它们的关系由JoinTable
表示。要访问和保留所有数据,我使用通用DAO
。
我试图在级联上删除,所以如果删除客户端,他的手机也会被删除。我已经通过向orphanRemoval=true
注释和cascade={CascadeType.ALL}
注释添加OneToMany
和CascadeOnDelete
来实现此目的。
问题在于,由于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
答案 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可能会为您提供更好的性能。