Когда я вызываю 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;
}