#spring #spring-boot #hibernate #jpa
Вопрос:
У меня есть две модели, Account
и Transaction
. Account
Выглядит это следующим образом:
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private BigDecimal balance;
@OneToMany(fetch = FetchType.LAZY)
private List<Transaction> transactions;
public Account(final BigDecimal balance) {
this.balance = balance;
this.transactions = new ArrayList<>();
}
public void addTransaction(Transaction transaction) {
this.transactions.add(transaction);
}
}
Transaction
Сущность заключается в следующем:
public class Transaction {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
@OneToOne(fetch=FetchType.LAZY)
private Account sender;
@OneToOne(fetch=FetchType.LAZY)
private Account recipient;
private BigDecimal amount;
public Transaction(Account sender, Account recipient, BigDecimal amount) {
this.sender = sender;
this.recipient = recipient;
this.amount = amount;
}
}
Как вы можете видеть, a Transaction
находится между a Sender
и Recipient
, оба они представлены Account
сущностью. В этом случае a Transaction
будет иметь сопоставление Один к одному для Отправителя и то же самое для Получателя. Однако, с другой стороны, Account
организация должна иметь возможность представлять все транзакции со счета, независимо от того, является ли она отправителем или получателем. Использование приведенного выше сопоставления позволяет мне вставлять транзакции, однако я не могу вернуть их в сопоставление. По сути, я не уверен, какие аннотации лучше всего использовать в этом случае.
Я должен добавить, что, используя приведенный выше код, я получаю следующую ошибку hibernate:
Caused by: org.hibernate.AnnotationException: A Foreign key refering com.xxx..Account from com.xxx.Transaction has the wrong number of column. should be 1
Комментарии:
1. Я бы полностью опустил
Account.transactions
сторону отношений и извлек транзакции учетной записи с помощью чего-то вродеSELECT t FROM Transaction t WHERE t.sender=:account OR t.recipient=:account
(JPQL).
Ответ №1:
Я предполагаю, что an Account
может быть отправителем/получателем в нескольких транзакциях:
- Отправитель и получатель на самом деле являются
@ManyToOne
:public class Transaction { @Id @GeneratedValue(strategy= GenerationType.AUTO) private Integer id; @ManyToOne(fetch=FetchType.LAZY, optional = false) private Account sender; @ManyToOne(fetch=FetchType.LAZY, optional = false) private Account recipient; private BigDecimal amount; public Transaction(Account sender, Account recipient, BigDecimal amount { this.sender = sender; this.recipient = recipient; this.amount = amount; } }
Это потому, что один
Account
может быть отправителем для многих транзакций, но у одногоTransaction
может быть только отправитель. То же самое касается приемника.
Я также предполагаю, что у всех транзакций должны быть отправитель и получатель. @OneToMany
в двух разных колумах.Вы создаете соотношение «один ко многим
Account
«, но в каком столбце вы ожидаете отобразить ассоциациюTransaction
? Поскольку существует два возможных столбца, содержащих учетную запись, Hibernate ORM не знает, какой из них использовать, и выдает исключение.
Удаление связи с Account
должно решить вашу проблему:
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private BigDecimal balance;
public Account(final BigDecimal balance) {
this.balance = balance;
}
}
Когда вам понадобятся все транзакции для определенной учетной записи, вы можете выполнить следующий запрос JPQL. Например, с помощью EntityManager:
entityManager
.createQuery("FROM Transaction t WHERE t.sender=:account OR t.recipient=:account")
.setParameter("account", account )
.getResultList();
Комментарии:
1. Это имеет смысл, однако, я попробовал другой подход, который заключается в
@OneToOne
сопоставлении как отправителя, так и получателя,Transaction
и вместо этого создал два списка вAccount
:@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "sender") List<Transaction> sender_transactions
и то же самое для транзакций получателей. В этом случае я затем создал Временное поле, которое объединяет эти два списка при запросе всех транзакций,Account
и это, похоже, работает нормально. Тем не менее, мне интересно, является ли хорошей практикой делать это таким образом или, как предлагается, только одним отображением…2. Вам нужны два отдельных запроса, а затем вам нужно объединить результаты. Кажется, что это большая дополнительная работа, которой вы могли бы избежать с помощью одного запроса к БД. Но это зависит от вас. Вам все равно нужно изменить сопоставление на
@ManyToOne
(вместо@OneToOne
), если учетная запись может быть отправителем/получателем для более чем одной транзакции.