Когда я вызываю EJB Dao из вспомогательного компонента CDI, коллекции @OneToMany в возвращаемой сущности очищаются и становятся пустыми

Я обращаюсь к дао EJB в контейнере ejb из поддерживаемого bean-компонента cdi. Дао выполняет JQL-запрос с выборкой соединения и извлекает сущность со ссылкой @OneToMany. Коллекции @OneToMany заполнены, и я могу использовать их внутри EJB, но в компоненте поддержки CDI коллекции пусты и очищены. Мои сущности выглядят так:

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

и поддерживающий боб:

@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 содержат все элементы в методе дао, но когда компонент поддержки получает заказ, элементы разрываются, и набор пуст. Я также написал тест на джунит arquillian, чтобы проверить в нем дао, и он отлично работает, и порядок содержит элементы в тесте. Но не в сессионном компоненте CDI. Когда я извлекаю набор в дао, как DTO (объект передачи данных), я могу получить элементы в компоненте поддержки.

Бин dao находится в ejb jar, в архиве предприятия уха. Поддерживающий компонент CDI находится в военном архиве в том же ухе. Я упростил наш проблемный случай до примера позиции заказа. Я не мог найти никаких ресурсов этого глупого поведения. Я использую сервер приложений wildfly 13 и использую hibernate jpa orm.

1 ответ

Решение

Причиной этой проблемы является перезаписанный метод equals. Спящий у нас большой механизм кеширования и прокси, и каждый класс сущностей обрабатывается прокси. Проблема заключается в следующем тесте в методе equals:

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

В случае объекта эти строки всегда возвращают false, потому что obj имеет тип прокси-класса, а не сущности. obj хранится в наборе, доступ к нему осуществляется через прокси, во время кэширования и так далее.

Никогда не используйте getClass в методе равенства объекта всегда используйте instanceof оператор. В моем случае эта неправильная реализация вызывает непредсказуемое поведение, в то время как экземпляр сущности перемещается от дао к компоненту поддержки.

Метод 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;
    }
Другие вопросы по тегам