Динамические искатели с отношением Grails ко многим ко многим

У меня есть 2 класса домена, которые сопоставлены отношениями многие ко многим. Я следовал инструкциям документации Grails, но у меня все еще есть некоторые проблемы при обработке данных в этих доменах. Вот мои 2 класса домена:

class User {
    String name
    int age
    String job
    static hasMany = [groups : Group]
    static belongsTo = [org : Organization]
}

class Group {
    String groupName
    String code
    static hasMany = [members : User]
}

Мои проблемы:
1. Приведенные выше отношения требуют, чтобы один класс владение принадлежал, чтобы быть "владельцем" отношения. В этом контексте Пользователь принадлежит к Группе, но я не знаю, как поместить принадлежность к классу Пользователя, потому что стандартный синтаксис, который предлагает Grails, является статическим принадлежащим = Группа (просто укажите имя класса владельца), поэтому я не могу:
- положить его в существующую принадлежность. Вот так: статическая принадлежность To = [org: Organization, Group]
- или определить другую принадлежность, например: статическая принадлежность = [группа]

  1. Ниже приведен пример справа:

    class Book {Строка заголовка static staticTo = Автор статического hasMany = [авторы: Автор]

    static mapping = {
        authors joinTable:[name:"mm_author_books", key:'mm_book_id' ]
    }
    

    } class Author {String name static hasMany = [books: Book]

    static mapping = {
        books joinTable:[name:"mm_author_books", key:'mm_author_id']
    }
    

    }

(Ссылка: таблицы ссылок " многие ко многим" в Grails (GORM) / hibernate)
Я имею в виду, что нам нужно указать имя внешнего ключа таблицы соединения для каждого класса?

  1. Если я хочу найти всех пользователей, которые являются членами указанной группы с именем "ABC", как я могу использовать DynamicFinder of Grails?

огромное спасибо

2 ответа

Решение

Очень редко отношения m2m имеют собственную сторону, поэтому я всегда считал странным указывать одну для правильной работы GORM. Из-за этого я так не делаю. Я создаю таблицу соединений в качестве домена. Тогда все становится действительно просто.

class UserGroup implements Serializable {

    User user
    Group group

    boolean equals(other) {
        if (!(other instanceof UserGroup)) {
            return false
        }

        other.user?.id == user?.id &&
            other.group?.id == group?.id
    }

    int hashCode() {
        def builder = new HashCodeBuilder()
        if (user) builder.append(user.id)
        if (group) builder.append(group.id)
        builder.toHashCode()
    }

    static UserGroup get(long userId, long groupId) {
        find 'from UserGroup where user.id=:userId and group.id=:groupId',
            [userId: userId, groupId: groupId]
    }

    static UserGroup create(User user, Group group, boolean flush = false) {
        new UserGroup(user: user, group: group).save(flush: flush, insert: true)
    }

    static boolean remove(User user, Group group, boolean flush = false) {
        UserGroup instance = UserGroup.findByUserAndGroup(user, group)
        instance ? instance.delete(flush: flush) : false
    }

    static void removeAll(User user) {
        executeUpdate 'DELETE FROM UserGroup WHERE user=:user', [user: user]
    }

    static void removeAll(Group group) {
        executeUpdate 'DELETE FROM UserGroup WHERE group=:group', [group: group]
    }

    static mapping = {
        id composite: ['group', 'user']
        version false
    }
}

Тогда вам просто нужно создать геттеры в вашем классе User и Group. У вас не будет пользователя или группы в любом классе. Нет необходимости сопоставлять их с hasMany/ ownTo, потому что все, что нужно сделать, - это создать таблицу соединений, которую вы сделали, создав домен UserGroup.

class User {
   Set<Group> getGroups() {
    UserGroup.findAllByUser(this).collect { it.group } as Set
  }
}

class Group {
  Set<User> getUsers() {
    UserGroup.findAllByGroup(this).collect { it.user } as Set
  }
}

После того, как они у вас есть, вы можете использовать методы, которые вы создали в домене UserGroup, и / или вы можете использовать для него поиск...

def userGroupInstance = UserGroup.findByUserAndGroup(userInstance, groupInstance)
def userGroups = UserGroup.findAllByUser(userInstance)
def userGroupInstance = UserGroup.get(userId, groupId)

Вы поняли идею. Эта презентация Берта Беквита проливает больше света на то, почему это хороший подход, а также на другие полезные советы по повышению производительности.

  1. belongsTo создаст другое отношение M:1 (поле) - вы скажете, существует ли "основная группа" для User в твоем случае. Если бы я хотел использовать только одну группу, я бы использовал специальный валидатор для User.groups проверка не пустая

  2. (Я не уверен здесь) - я считаю, что да, если имя ключа отличается от имени по умолчанию Hibernat / GORM "userId"/"groupId".

  3. findBy*() методы не будут работать здесь, вам нужен CriteriaBuilder, как здесь.

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