【问题标题】: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 Dao 时,返回的实体中的 @OneToMany 集合被清除并为空
【发布时间】:2019-03-10 02:07:36
【问题描述】:

我从支持 sessionscoped cdi bean 访问 ejb 容器中的 EJB dao。 dao 使用连接提取执行 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 方法中的所有项目,但是当 backing bean 接收到订单时,项目被撕掉并且集合为空。我还编写了一个 arquillian junit 测试来测试其中的 dao,它运行良好,并且订单包含测试中的项目。但不在 CDI 会话 bean 中。当我像 DTO(数据传输对象)一样提取 dao 中的集合时,我可以接收支持 bean 中的项目。

dao bean 位于 ejb jar 中,位于 ear 企业档案中。 CDI 支持 bean 位于同一耳朵的战争档案中。我将我们的问题案例简化为订单项目示例。我找不到这种愚蠢行为的任何资源。我使用 wildfly 13 appserver 并使用 hibernate jpa orm。

【问题讨论】:

    标签: java hibernate jpa ejb wildfly


    【解决方案1】:

    导致该问题的原因是被覆盖的 equals 方法。 Hibernate 给我们一个大的缓存和代理机制,每个实体类都由代理处理。问题是equals方法中的以下测试:

        if (getClass() != obj.getClass()) // This wouldn't work in JPA!!!
             return false;
    

    对于实体,这些行总是返回 false,因为 obj 具有代理类的类型而不是实体的类型。 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;
        }
    

    【讨论】:

    • 从历史上看,我在 Class 上使用过 isAssignableFrom() 方法,在极少数情况下 instanceof 也适得其反。但那是在旧版本的 Hibernate (3.6) 中。
    • 感谢您的提示。我读过它,但我们使用hibernate 5.2。这个问题我早就知道了,但是以前有个同事改了equals函数,所以没关注。
    猜你喜欢
    • 1970-01-01
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-07
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多