Когда я вызываю EJB Dao из базового компонента CDI, коллекции @OneToMany в возвращаемой сущности очищаются и становятся пустыми - PullRequest
0 голосов
/ 04 октября 2018

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

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

1 Ответ

0 голосов
/ 05 октября 2018

Причиной этой проблемы является перезаписанный метод 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;
    }
...