#java #jdbc #eclipselink #jpa-2.1 #java-ee-8
#java #jdbc #eclipselink #jpa-2.1 #java-ee-8
Вопрос:
Я совершенно новичок в концепции JPA и ORM, поэтому я надеюсь, что кто-нибудь сможет доходчиво объяснить, в чем может быть проблема с моим кодом.
@Entity
@Table(name = "PERSISTENCE_customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1005220876458L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
@OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<CustomerOrder> orders;
}
@Entity
@Table(name = "PERSISTENCE_ORDER")
public class CustomerOrder implements Serializable{
private static final long serialVersionUID = 199102142021L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
String status;
@NotNull
@OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<LineItem> lineItems = new ArrayList();
@NotNull
private String orderNumber;
................
................
}
@Entity
@Table(name = "PERSISTENCE_LINEITEM")
public class LineItem implements Serializable {
private static final long serialVersionUID = 1991217202100959L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
private Integer quantity;
@NotNull
private Part part;
}
Первоначально объект Customer создается через пользовательский интерфейс и успешно сохраняется. Позже у клиента есть заказ, и я обновляю клиента с помощью CustomerOrder следующим образом:
private void UpdateCustomer(Customer customer) {
FacesContext fc = FacesContext.getCurrentInstance();
List<ShoppingCartItem> shoppingCart = getShoppingCart();
CustomerOrder order = new CustomerOrder();
List<CustomerOrder> orders = customer.getOrders();
order.setLastUpdated(new Date());
order.setOrderNumber(getInvoiceNumber());
List<LineItem> lineItems = shoppingCart
.stream()
.map(e -> (new LineItem(e.getPart(), e.getQuantity())))
.collect(Collectors.toList());
order.setLineItems(lineItems);
order.setStatus("Pending Shipment");
order.setTotal(getTotal());
orders.add(order);
customer.setOrders(orders);
try {
updateOrders(customer, orders);
fc.addMessage(null,
new FacesMessage("Customer order added successfuly"));
} catch (ListServiceException e) {
FacesMessage errMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Error while adding customer order: ", e.getMessage());
fc.addMessage(null, errMsg);
}
}
private void updateOrders(Customer cust, List<CustomerOrder> orders) throws ListServiceException {
try { //em is the EntityManager injected as the field member
if (em != null) {
if (em.isOpen()) {
Customer c = getCustomer(cust.getId());
c.setOrders(orders);
em.merge(c);
} else {
logger.severe("Entity manager is closed");
}
else {
logger.severe("Entity manager is NULL");
}
} catch (Exception e) {
throw ThrowListServiceException.wrapException(e);
}
}
Как только EntityManage объединяется, я получаю следующее исключение. У меня сложилось впечатление, что мне не нужно явно сохранять объекты LineItem и CustomerOrder самостоятельно. Я думал, что JPA сохранит все объекты в графе объектов. Почему я получаю это исключение? (Я использую сервер GlassFish 5.1 с EclipseLink JPA)
Заранее спасибо.
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Error Code: 30000
Call: INSERT INTO PERSISTENCE_CUSTOMER_PERSISTENCE_ORDER (orders_ID, Customer_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="orders" sql="INSERT INTO PERSISTENCE_USER_PERSISTENCE_ORDER (orders_ID, User_ID) VALUES (?, ?)")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
...............................
.................................
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Обновить
Используя отладчик IDE (Netbeans), я прошел через код, и, как я и предсказывал, во время слияния объектов JPA не добавляет новые объекты, которые являются частью графа объектов, в контекст сохранения. Например, в методе updateOrders(), когда я пытаюсь обновить существующий объект Customer списком нового объекта CustomerOrder, JPA не выясняет, что элементы в списке еще не являются частью контекста сохранения, и их необходимо добавить. В результате мне пришлось изменить свой код, чтобы сначала добавить список в контекст сохранения, а затем объединить объект Customer с новым сохраненным списком. При этом я больше не получаю исключение.
Кстати, на данный момент все отношения сопоставления являются однонаправленными, потому что я не видел никаких причин использовать двунаправленное сопоставление. Однако получу ли я что-нибудь, сделав эти сопоставления двунаправленными?
Комментарии:
1. Когда именно вы получаете сообщение об ошибке. Когда вы запускаете проект? Можете ли вы объяснить их немного подробнее? Спасибо.
2. Привет, Юсуф. Хороший вопрос. Когда я просматриваю код с помощью отладчика, я вижу, что метод updateOrders() завершается без каких-либо ошибок или исключений. Я даже делаю еще один шаг и извлекаю Клиента сразу после его объединения в updateOrders(), и график объектов выглядит хорошо и точно. Исключение возникает где-то на этапе рендеринга JSF, когда он использует объект Customer для заполнения страницы пользовательского интерфейса, но отладчик не может показать этот процесс.
3. Спасибо за хорошее объяснение. Я полагаю, это не имеет ничего общего с @onetomany. Вы использовали значение true. Добавленное вами исключение выдает ошибку null. Я продолжаю проверять код
Ответ №1:
В вашем сопоставлении OneToMany отсутствует спецификация соединения или значение mappedBy
Комментарии:
1. Sir3nka, согласно спецификации JPA, для однонаправленного сопоставления не требуется ни mappedBy, ни JointColumn, хотя JoinColum устраняет необходимость использования дополнительной таблицы соединений. Может ли быть так, что, когда я обновляю существующий объект Customer новым CustomerOrder, который еще не является частью контекста сохранения, проблема? При сохранении нового объекта JPA сохранит весь граф объектов, однако, судя по исключению, это не так при слиянии объекта. Я чувствую, что JPA ожидает, что я сохраню CustomerOrder сначала, чтобы объединить его с Customer. Это правильно?
Ответ №2:
Я это заметил. Во-первых, вы должны зафиксировать новый порядок в базе данных.Затем вы должны связать его с пользователем.Я не уверен, решает ли это вашу проблему, но это проблема.Вы можете это проверить ?
Комментарии:
1. Юсуф, как я указывал ранее. Я подумал о проблеме и решил ее
Ответ №3:
На мой взгляд, если вы сохраните информацию о клиенте в своем объекте заказа, это может решить эту проблему.
@ManyToOne ()
private Customer customer;
И в вашем объекте Customer вы должны поместить поле mappedBy=customer для заказов.
После этого, вместо того, чтобы отдавать заказы для клиента, вы можете предоставить клиенту конкретный заказ. На мой взгляд, это позволит добиться лучшего сопоставления отношений;
order.setCustomer(customer);
Надеюсь, я правильно понял, и это решит вашу проблему. Когда вы предоставляете сведения о клиенте для заказа, вам не нужно указывать сведения о списке заказов для того же клиента. Должно быть достаточно только одного из них.
Комментарии:
1. Эрдем, спасибо за комментарий, но я уже решил проблему. Смотрите Обновление вопроса. Проблема заключалась в моем понимании того, как работает JPA при объединении объектов. Я подумал, что если новый объект (т.Е. CustomerOrder) добавляется к существующему объекту Customer, JPA достаточно умен, чтобы выяснить, что новый объект (CustomerOrder) еще не является частью контекста сохранения, и ему необходимо сначала сохранить этот новый объект, прежде чем объединять его с объектом Customer. Проблема была решена путем ручного добавления CustomerOrder и последующего объединения его с Клиентом.
2. Эрдем, также то, что вы предлагаете, является верным дизайнерским решением и, возможно, другим решением проблемной области.
3. Привет, Тони. Я рад, что вы решили проблему, добавив порядок вручную. Но создание нового списка для заказов и обновление всего списка заказов вручную показалось мне немного болезненным. Если вы сохраняете информацию о клиентах для каждого заказа, она автоматически увеличит список для вас, и вам не нужно будет постоянно иметь дело со всем списком заказов клиента, если вы спросите меня. :). Ну, я бы предпочел иметь дело с одним порядком, а не со всем списком все время.
4. Эрдем: Чем больше я думаю о вашем предложении по дизайну, тем больше оно мне нравится, и я мог бы пойти этим путем, однако на данный момент я борюсь с концепциями однонаправленного и двунаправленного отображения, и я действительно не знаю, когда использовать то или другое. Мне также нравится использовать @JointColumn для устранения таблицы сопоставления, но я не могу заставить ее работать, поэтому, как только я получу лучшее представление о JAP, я внесу изменения в дизайн. Еще раз спасибо
5. С помощью двунаправленного сопоставления вы можете получить доступ к обоим направлениям. Например, у вас есть клиент, который извлекается из репозитория. Если у customer и order правильные двунаправленные отношения, вы можете заставить клиентов делать заказы, просто используя getter (customer.GetOrders). Также, если у вас есть билет, и вы можете получить доступ к клиенту билета с помощью getter ticket.GetCustomer. Он предоставляет множество подобных полезных навыков.