简体   繁体   English

与Grails中的同一域类一对一和一对多关联

[英]One-to-one and one-to-many association to same domain class in Grails

I have two domain classes -- Account and Member , and I am having trouble finding the correct combination of associations and mappedBy attributes to model their relationship. 我有两个域类AccountMember ,并且在查找关联和mappedBy属性的正确组合以建模它们的关系时遇到了麻烦。 In plain English, an Account requires one and only one primary member. 用简单的英语来说,一个Account只需要一个主要成员。 It also has 0 - many dependents. 它还有0个-许多依赖项。 My initial attempt looked like: 我最初的尝试看起来像:

Account.groovy Account.groovy

class Account {
    String displayId

    Member primaryMember
    SortedSet<Member> dependents = new TreeSet<Member>()

    static belongsTo = [Member]
    static hasMany = [dependents: Member]
}

Member.groovy Member.groovy

class Member implements Comparable<Member> {
    MemberType memberType = MemberType.PRIMARY
    String firstName
    String lastName

    static belongsTo = [account: Account]

    @Override
    int compareTo(Member m) {
        return (this.memberType <=> m?.memberType) * 100 + (this.lastName <=> m?.lastName) * 10 + (this.firstName <=> m?.firstName)
    }
}

This causes problems when I attempt to instantiate and persist the accounts and members. 当我尝试实例化并保留帐户和成员时,这会导致问题。 For example, 例如,

AccountIntegrationTests.groovy AccountIntegrationTests.groovy

class AccountIntegrationTests extends GroovyTestCase {

    Account smiths
    Member john
    Member jane

    @Before
    void setup() {}

    @Test
    void testShouldLoadAccountWithNoDependents() {
        // Arrange
        smiths = new Account(displayId: "ABCDEFG")

        john = new Member(firstName: "John", lastName: "Smith", memberType: MemberType.PRIMARY)
        smiths.primaryMember = john
        smiths.save(flush: true, failOnError: true)

        def smithsId = smiths.id
        smiths.discard()

        // Act
        def loadedSmiths = Account.get(smithsId)

        // Assert
        assert loadedSmiths.members.size() == 1
        assert loadedSmiths.primaryMember == john
        assert loadedSmiths.dependents.size() == 0
    }

    @Test
    void testShouldLoadAccountWithOneDependent() {
        // Arrange
        smiths = new Account(displayId: "ABCDEFG")
        john = new Member(firstName: "John", lastName: "Smith", memberType: MemberType.PRIMARY)
        smiths.primaryMember = john
        smiths.addToDependents(new Member(firstName: "Jane", lastName: "Smith", memberType: MemberType.DEPENDENT))
        smiths.save(flush: true, failOnError: true)

        john = smiths.primaryMember
        jane = smiths.dependents.first()
        def smithsId = smiths.id
        smiths.discard()

        // Act
        def loadedSmiths = Account.get(smithsId)

        // Assert
        assert loadedSmiths.members.size() == 2
        assert loadedSmiths.primaryMember.firstName == "john"
        assert loadedSmiths.dependents.size() == 1
        assert loadedSmiths.dependents.first().firstName == "jane"
    }
}

will throw exceptions because the database tables for the second test look like 将抛出异常,因为第二个测试的数据库表看起来像

Account 帐户

id | display_id
1  | ABCDEFG

Member 会员

id | first_name | last_name | member_type | account_id
1  | John       | Smith     | Primary     | 1
2  | Jane       | Smith     | Dependent   | 1

Obviously I would like the account to retrieve John as the primary member, and Jane as the dependent, but when GORM tries to load account.primaryMember, it throws a Hibernate exception that there are multiple rows (in Member) matching the account ID (1). 显然,我希望该帐户以John为主要成员,而Jane为从属,但是当GORM尝试加载account.primaryMember时,它将引发一个Hibernate异常,即(成员中)有多行与该帐户ID相匹配(1 )。 I need a mappedBy discriminator to distinguish between the two associations, but the versions I have tried did not work: 我需要一个mappedBy区分mappedBy来区分两个关联,但是我尝试过的版本不起作用:

Account 帐户

static mappedBy = [primaryMember: 'primaryMember', dependents: 'dependents']
- or -
static mappedBy = [dependents: 'account']

I have read the GORM documentation for both the associations and mappedBy , as well as various questions on the site regarding multiple associations to the same model , unfortunately they all seem to be modeling multiple hasMany associations. 我已经阅读了有关关联和mappedBy的GORM文档,以及网站上有关同一模型的 多个关联的 各种 问题 ,但不幸的是,它们似乎都在为多个hasMany关联建模。 The one that does reference both one-to-many and one-to-one relationships to the same model illustrates it as: 确实将一对多关系和一对一关系都引用到同一模型的人将其说明为:

class Person {
    String name

    static belongsTo = [team: Team]
}

class Team {
    String name

    static belongsTo = [coach: Person]
    static hasMany = [players: Person]
}

However, I tried to use this simple implementation and got: 但是,我尝试使用此简单的实现并得到:

--Output from testShouldLoadAccountWithNoDependents--
| Failure:  testShouldLoadAccountWithNoDependents(AccountIntegrationTests)
|  org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: Account; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Account
    at AccountIntegrationTests.testShouldLoadAccountWithNoDependents(AccountIntegrationTests.groovy:26)
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Account
    ... 1 more

AccountIntegrationTests.groovy:26 is the line smiths.save(flush: true, failOnError: true) . AccountIntegrationTests.groovy:26是行smiths.save(flush: true, failOnError: true) When I modified the Team/Account class to: 当我将Team/Account类修改为:

class Team {
    String name
    Person coach

    static hasMany = [players: Person]
}

I encountered the same exception, so I believe some kind of cascade is required. 我遇到了相同的异常,因此我认为需要某种cascade I tried adding a cascade to the Account : 我尝试向该Account添加一个cascade

Account 帐户

static mapping = {
    primaryMember cascade: 'all'
    dependents cascade: 'all-delete-orphan'
}

then I also tried using event triggers: 然后我也尝试使用事件触发器:

Account 帐户

def beforeInsert() {
    if (primaryMember?.isDirty()) {
        primaryMember.save()
    }
}

def beforeUpdate() {
    if (primaryMember?.isDirty()) {
        primaryMember.save()
    }
}

Unfortunately, neither of these approaches resolved the transient instance exception. 不幸的是,这些方法都不能解决transient instance异常。 Any assistance on this is greatly appreciated. 在这方面的任何帮助将不胜感激。

The embarrassingly simple solution was to remove the static belongsTo = [account: Account] from Member . 令人尴尬的简单解决方案是从Member删除static belongsTo = [account: Account] The bidirectional association was causing the issues. 双向关联导致了问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM