Когда следует использовать hasMany для отношений N:1 в классах домена grails?

В Grails я могу реализовать отношение N:1 следующим образом:

class Parent { hasMany = [children:Child] }
class Child  { belongsTo = [parent:Parent] }

Теперь (если addTo и removeFrom всегда используются правильно), я могу получить детей Parent через parent.children.

Но я также могу сделать это без hasMany:

class Parent { }
class Child  { belongsTo = [parent:Parent] }

Затем я должен использовать Child.findAllByParent(parent), чтобы получить всех детей.

Мой вопрос: есть ли какие-то важные причины, по которым я должен использовать hasMany, если могу также запрашивать детей родителей вторым способом?

Я полагаю, что иногда проще (и, возможно, быстрее, если его извлекают вместе с родителем?) Просто ссылаться на parent.children, но, с другой стороны, этот список может стать довольно длинным, когда есть несколько дочерних элементов. И что мне не нравится в hasMany, так это то, что вам всегда нужно позаботиться о addTo или removeFrom или очистить сеанс после добавления нового Child с Parent, чтобы grails делал это автоматически...

Является ли ответом то, что вы должны просто использовать hasMany, если детей мало, и не использовать его, если их много (по соображениям производительности), или есть что-то еще за этим?

2 ответа

Решение

Использование hasMany по сравнению с ownTo больше связано с каскадным поведением, которое вы хотите указать при обновлении / удалении. Во втором примере для каскадирования установлено значение ALL на дочерней стороне и NONE на родительской стороне. Если вы удалите ребенка, с родителем ничего не произойдет. Если вы удалите родителя, все дети будут автоматически удалены.

В первом примере для каскадирования установлено значение ALL на родительской стороне и SAVE-UPDATE на дочерней стороне. Так что теперь вы можете сделать что-то вроде:

parent.addToChildren(child1)
parent.addToChildren(child2)
parent.addToChildren(child3)
parent.save(flush:true)

И когда вы сохраните родителя, все дети будут обновлены.

Касаясь чего-то, что вы не просили, вы также можете предположить что-то вроде:

class Parent { hasMany = [children:Child] }
class Child  { Parent parent }

Если вы определите отношения между дочерним элементом и родительским способом таким образом, вам потребуется вручную управлять дочерними объектами, которые ссылаются на родительского элемента при удалении родительского элемента *. (* это исправляет предыдущее утверждение, которое оказалось неточным)

Итак, hasMany / ownTo имеет два основных соображения:

  1. Какую каскадную стратегию вы хотите выполнить при обновлении / удалении
  2. Как вы, скорее всего, собираетесь получить доступ к данным, если вы ожидаете, что вам понадобится получить набор потомков для родителя, иметь метод parent.getChildren() довольно удобно.

ОБНОВИТЬ:

Я также хочу уточнить, GORM не будет стремиться получить, когда вы используете hasMany; по умолчанию GORM использует стратегию отложенной выборки, поэтому он не получит дочерние элементы до тех пор, пока не будет предпринята попытка доступа к parent.children.

Если вы хотите, чтобы связь по умолчанию охотно выбиралась, вы можете указать соответствующее отображение:

class Parent { 
  hasMany = [children:Child]
  static mapping = {
    children lazy:false
  }
}

Наконец, вы упомянули, что вам не нравится, что вам нужно беспокоиться о addTo / removeFrom на стороне hasMany. Вам не нужно делать это, если вы сохраняете с помощью flush: true.

def parent = Parent.get(id)
def child = new Child(name:'child1', parent:parent)
if(child.save(flush:true)) {
  // both sides of the relationship should be good now
} 

РЕДАКТИРОВАТЬ: исправлен обратный порядок значений по умолчанию для дочернего / родительского каскада и исправлено неправильное представление о том, как Горм обрабатывает отношения без принадлежности.

Отличный вопрос, и в настоящее время принят хороший ответ. Есть еще один важный аспект производительности, который происходит, когда вы добавляете и сохраняете нового дочернего элемента. В первом примере Grails по умолчанию должен загрузить весь список дочерних элементов из базы данных, прежде чем вставить новый в Set, чтобы гарантировать уникальность. Во втором случае это не так, что приводит к гораздо лучшей производительности. Вы можете обойти это поведение в своем первом примере, определив дочерние элементы как "Коллекцию" в соответствии с http://grails.org/doc/latest/guide/single.html

Другие вопросы по тегам