当我从 CDI 支持 bean 调用 EJB Dao 时,返回实体中的 @OneToMany 集合被清除并为空
When I call a EJB Dao from a CDI backing bean, the @OneToMany collections in the returned entity are cleared and empty
我从支持会话范围的 cdi bean 访问 ejb 容器中的 EJB dao。 dao 使用 join fetch 执行 JQL 查询,并使用 @OneToMany 引用检索实体。 @OneToMany 集合已填充,我可以在 EJB 中使用它们,但在 CDI 支持 bean 中,集合为空并被清除。我的实体如下所示:
@Entity
@NamedQuery(name = "order.with.items",
query = "select o from Order o inner join fetch o.item i where o.id=:orderNo")
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(name = "version")
private int version;
@Column
private String name;
@OneToMany(mappedBy = "order")
private Set<Item> item = new HashSet<>();;
... getters setters
}
和引用项目的:
@Entity
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(name = "version")
private int version;
@Column
private String name;
@ManyToOne
@JoinColumn(name = "order_id", referencedColumnName = "id")
private Order order;
//... getters setters
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass()) // UPDATE: don't do it
return false; // with getClass - use instanceof
Item other = (Item) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (order == null) {
if (other.order != null)
return false;
} else if (!order.equals(other.order))
return false;
return true;
}
}
和道:
@Stateless
@LocalBean
public class OrderDao {
@PersistenceContext(unitName = "jpa-persistence-unit")
protected EntityManager entityManager;
public Order getOrderWhithItems(Long orderId) {
Order order = entityManager.createNamedQuery("order.with.items",Order.class).setParameter("orderId", orderId).getSingleResult();
// Here is the size greater than zero
System.out.println("# of items: " + order.getItem().size());
return order;
}
}
和支持 bean:
@Named
@SessionScoped
public class BackingBean {
@EJB
private OrderDao orderDao;
public BackingBean() {
Order order = orderDao.getOrderWhithItems( 4L);
Set<Item> items = order.getItem();
// This will ouputs 0
System.out.println("# of items " + items.size());
}
}
问题是,order
包含 dao 方法中的所有项目,但是当支持 bean 收到订单时,项目被撕掉并且集合为空。我还写了一个 arquillian junit 测试来测试其中的 dao,它工作得很好,订单包含测试中的项目。但不在 CDI 会话 bean 中。当我像 DTO(数据传输对象)一样在 dao 中提取集合时,我可以在支持 bean 中接收项目。
dao bean 在一个 ejb jar 中,在一个 ear 企业档案中。 CDI 支持 bean 位于同一耳朵的 war 存档中。我将我们的问题案例简化为订单项目示例。我找不到这种愚蠢行为的任何资源。我使用 wildfly 13 appserver 并使用 hibernate jpa orm。
导致该问题的原因是重写了 equals 方法。 Hibernate 我们有一个很大的缓存和代理机制,每个实体 Class 都由代理处理。问题是 equals 方法中的以下测试:
if (getClass() != obj.getClass()) // This wouldn't work in JPA!!!
return false;
在实体的情况下,这些行总是 return false,因为 obj
具有代理 class 的类型而不是实体的类型。 obj
存储在一个集合中,该集合由代理访问,同时进行缓存等。
永远不要在实体的 equals 方法中使用 getClass
,始终使用 instanceof
运算符。在我的例子中,当实体的实例从 dao 传输到支持 bean 时,这种错误的实现会导致不可预测的行为。
equals 方法应该如下所示:
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Item)) {
return false;
}
Item other = (Item) obj;
if (id != null) {
if (!id.equals(other.id)) {
return false;
}
}
return true;
}
我从支持会话范围的 cdi bean 访问 ejb 容器中的 EJB dao。 dao 使用 join fetch 执行 JQL 查询,并使用 @OneToMany 引用检索实体。 @OneToMany 集合已填充,我可以在 EJB 中使用它们,但在 CDI 支持 bean 中,集合为空并被清除。我的实体如下所示:
@Entity
@NamedQuery(name = "order.with.items",
query = "select o from Order o inner join fetch o.item i where o.id=:orderNo")
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(name = "version")
private int version;
@Column
private String name;
@OneToMany(mappedBy = "order")
private Set<Item> item = new HashSet<>();;
... getters setters
}
和引用项目的:
@Entity
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Version
@Column(name = "version")
private int version;
@Column
private String name;
@ManyToOne
@JoinColumn(name = "order_id", referencedColumnName = "id")
private Order order;
//... getters setters
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass()) // UPDATE: don't do it
return false; // with getClass - use instanceof
Item other = (Item) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (order == null) {
if (other.order != null)
return false;
} else if (!order.equals(other.order))
return false;
return true;
}
}
和道:
@Stateless
@LocalBean
public class OrderDao {
@PersistenceContext(unitName = "jpa-persistence-unit")
protected EntityManager entityManager;
public Order getOrderWhithItems(Long orderId) {
Order order = entityManager.createNamedQuery("order.with.items",Order.class).setParameter("orderId", orderId).getSingleResult();
// Here is the size greater than zero
System.out.println("# of items: " + order.getItem().size());
return order;
}
}
和支持 bean:
@Named
@SessionScoped
public class BackingBean {
@EJB
private OrderDao orderDao;
public BackingBean() {
Order order = orderDao.getOrderWhithItems( 4L);
Set<Item> items = order.getItem();
// This will ouputs 0
System.out.println("# of items " + items.size());
}
}
问题是,order
包含 dao 方法中的所有项目,但是当支持 bean 收到订单时,项目被撕掉并且集合为空。我还写了一个 arquillian junit 测试来测试其中的 dao,它工作得很好,订单包含测试中的项目。但不在 CDI 会话 bean 中。当我像 DTO(数据传输对象)一样在 dao 中提取集合时,我可以在支持 bean 中接收项目。
dao bean 在一个 ejb jar 中,在一个 ear 企业档案中。 CDI 支持 bean 位于同一耳朵的 war 存档中。我将我们的问题案例简化为订单项目示例。我找不到这种愚蠢行为的任何资源。我使用 wildfly 13 appserver 并使用 hibernate jpa orm。
导致该问题的原因是重写了 equals 方法。 Hibernate 我们有一个很大的缓存和代理机制,每个实体 Class 都由代理处理。问题是 equals 方法中的以下测试:
if (getClass() != obj.getClass()) // This wouldn't work in JPA!!!
return false;
在实体的情况下,这些行总是 return false,因为 obj
具有代理 class 的类型而不是实体的类型。 obj
存储在一个集合中,该集合由代理访问,同时进行缓存等。
永远不要在实体的 equals 方法中使用 getClass
,始终使用 instanceof
运算符。在我的例子中,当实体的实例从 dao 传输到支持 bean 时,这种错误的实现会导致不可预测的行为。
equals 方法应该如下所示:
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Item)) {
return false;
}
Item other = (Item) obj;
if (id != null) {
if (!id.equals(other.id)) {
return false;
}
}
return true;
}