#java #entity-relationship #foreign-key-relationship #entities #bidirectional
#java #сущность-отношение #foreign-key-relationship #сущности #двунаправленный
Вопрос:
Допустим, у нас есть следующие две сущности.
Библиотека
public class Library
{
private Collection<Book> books = new ArrayList<Book>();
public boolean add(Book book)
{
return books.add(book);
}
public boolean remove(Book book)
{
return books.remove(book);
}
}
Книга
public class Book
{
private Library library;
public setLibrary(Library library)
{
this.library = library;
}
}
Это двунаправленная связь, которая, скорее всего, довольно быстро разорвется. Даже если вы добавляете книгу в библиотеку, библиотека книги не обновляется. И даже если вы обновляете библиотеку книги, библиотека не обновляется.
Итак, как бы вы правильно поддерживали эти двунаправленные отношения? Я мог бы, конечно, просто поставить add
remove
в setLibrary
и наоборот, но это, насколько я вижу, привело бы к циклу, который, вероятно, закончился бы на StackOverflowError
.
Ответ №1:
Определите «владельца» для отношения. Библиотека «владеет» книгой или книга владеет библиотекой?
Ответ на этот вопрос говорит вам, кто обновляет объект «slave».
Я чувствую, что библиотека владеет книгой, поэтому код «добавить книгу в библиотеку» должен перейти в библиотеку, которая затем обновит обратные ссылки.
Комментарии:
1. Хорошо, так вот как это обычно делалось бы тогда? Значит, в этом случае
Book
может быть немым, и вы будете вызывать толькоsetLibrary
вLibrary
классе?2. @Svish: Да, это идея, вы можете добавлять книги только в библиотеку, которая затем устанавливает библиотеку книги. Вы не можете напрямую установить библиотеку в книге.
3. ДА. Это обходной путь, потому что предложение veredesmarald работает слишком медленно на современных компьютерах. Если вопрос «эта книга уже есть в библиотеке» будет иметь сложность O (1) (= требует одинакового времени, независимо от размера библиотеки), мы могли бы позволить добавить код с обеих сторон.
4. Имеет смысл. Думаю, тогда я сделаю это таким образом 🙂
Ответ №2:
Хотя я бы рекомендовал метод определения владельца отношений Аарона, альтернативой, которая позволяет set
/ add
/ remove
методам оставаться там, где они есть, является также определение соответствующих has
методов ( hasBook
в Library
и hasLibrary
в Book
). Перед вызовом метода add
or remove
на другом конце отношения сначала запрашивайте, существует ли он уже, и если он существует, не вызывайте его.
Комментарии:
1. Это было бы идеальным решением, если бы проверка была быстрой. Современные компьютеры все еще слишком медленные для этого:-(
2. @Aaron Digulla: Я согласен, именно поэтому я сразу сказал, что предпочитаю ваш подход, но я все еще чувствовал, что стоит указать на это как на вариант.
3. Да, думаю, я сделаю то, что предлагает @Aaron, но это тоже был хороший момент, который я мог бы использовать при других обстоятельствах 🙂
Ответ №3:
Я что, тупой?
public boolean add(Book book)
{
book.setLibrary(this);
return books.add(book);
}
public boolean remove(Book book)
{
book.setLibrary(null);
return books.remove(book);
}
Комментарии:
1. Нет. Проблема в том, что вы в дополнение к этому делаете
this.books.remove(this);this.books = books; this.books.add(this);
вsetLibrary
классе.
Ответ №4:
В предлагаемом вами решении не могли бы вы проверить ссылки на равенство, чтобы избежать цикла?
public boolean add(Book book)
{
if(book.getLibrary != this) book.setLibrary(this);
return books.add(book);
}
Комментарии:
1. Это не работает: если книга уже была в другой библиотеке, ссылка на библиотеку в книге не обновлялась бы, и в итоге вы получили бы книгу, которая есть в нескольких библиотеках (с точки зрения библиотеки), но книга будет думать, что это только в одной из них.