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

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

1
Jochen Buchholz 4 Окт 2018 в 18:48

1 ответ

Лучший ответ

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

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

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

Никогда не используйте 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;
    }
1
Jochen Buchholz 5 Окт 2018 в 11:10