私には2つのドメインクラスがあります-Account
とMember
、そしてそれらの関係をモデル化するための関連付けとmappedBy
属性の正しい組み合わせを見つけるのに問題があります。平易な英語では、Account
は1人の主要メンバーのみを必要とします。また、0-多くの扶養家族がいます。私の最初の試みは次のようになりました:
Account.groovy
class Account {
String displayId
Member primaryMember
SortedSet<Member> dependents = new TreeSet<Member>()
static belongsTo = [Member]
static hasMany = [dependents: Member]
}
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)
}
}
これにより、アカウントとメンバーをインスタンス化して永続化しようとすると問題が発生します。例えば、
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"
}
}
2番目のテストのデータベーステーブルは次のように見えるため、例外がスローされます
アカウント
id | display_id
1 | ABCDEFG
メンバー
id | first_name | last_name | member_type | account_id
1 | John | Smith | Primary | 1
2 | Jane | Smith | Dependent | 1
明らかに、アカウントでJohnをプライマリメンバーとして、Janeを依存メンバーとして取得したいのですが、GORMがaccount.primaryMemberを読み込もうとすると、アカウントID(1 )。mappedBy
2つの関連付けを区別するための識別子が必要ですが、試したバージョンが機能しませんでした。
アカウント
static mappedBy = [primaryMember: 'primaryMember', dependents: 'dependents']
- or -
static mappedBy = [dependents: 'account']
アソシエーションとの両方のGORMドキュメント、および同じモデルへの複数のアソシエーションに関するサイト上のさまざまな質問を読みましたがmappedBy
、残念ながら、それらはすべて複数 のアソシエーションをモデル化しているようです。同じモデルに対して1対多と1対1の両方の関係を参照しているものは、次のように示しています。hasMany
class Person {
String name
static belongsTo = [team: Team]
}
class Team {
String name
static belongsTo = [coach: Person]
static hasMany = [players: Person]
}
ただし、この単純な実装を使用しようとすると、次のようになります。
--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
行smiths.save(flush: true, failOnError: true)
です。Team/Account
クラスを次のように変更したとき:
class Team {
String name
Person coach
static hasMany = [players: Person]
}
同じ例外が発生したので、なんらかのcascade
必要があると思います。にを追加してみcascade
ましたAccount
:
アカウント
static mapping = {
primaryMember cascade: 'all'
dependents cascade: 'all-delete-orphan'
}
次に、イベントトリガーを使用してみました。
アカウント
def beforeInsert() {
if (primaryMember?.isDirty()) {
primaryMember.save()
}
}
def beforeUpdate() {
if (primaryMember?.isDirty()) {
primaryMember.save()
}
}
残念ながら、これらのアプローチはどちらもtransient instance
例外を解決しませんでした。これに関する支援は大歓迎です。