Динамические искатели с отношением 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]
- или определить другую принадлежность, например: статическая принадлежность = [группа]
Ниже приведен пример справа:
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)
Я имею в виду, что нам нужно указать имя внешнего ключа таблицы соединения для каждого класса?
- Если я хочу найти всех пользователей, которые являются членами указанной группы с именем "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)
Вы поняли идею. Эта презентация Берта Беквита проливает больше света на то, почему это хороший подход, а также на другие полезные советы по повышению производительности.
belongsTo
создаст другое отношение M:1 (поле) - вы скажете, существует ли "основная группа" дляUser
в твоем случае. Если бы я хотел использовать только одну группу, я бы использовал специальный валидатор дляUser.groups
проверка не пустая(Я не уверен здесь) - я считаю, что да, если имя ключа отличается от имени по умолчанию Hibernat / GORM "
userId
"/"groupId
".findBy*()
методы не будут работать здесь, вам нужен CriteriaBuilder, как здесь.