我创建了两个简单的grails V3域类,其中location是在这样的父Venue中嵌入属性类型
import java.time.LocalDate
class Venue {
String name
LocalDate dateCreated
LocalDate lastVisited
LocalDate lastUpdated
GeoAddress location
static hasOne = [location:GeoAddress]
static embedded =['location']
static constraints = {
lastVisited nullable:true
location nullable:true
}
static mapping = {
location cascade: "all-delete-orphan", lazy:false //eager fetch strategy
}
}
class GeoAddress {
String addressLine1
String addressLine2
String addressLine3
String town
String county
String country = "UK"
String postcode
static belongsTo = Venue
static constraints = {
addressLine1 nullable:true
addressLine2 nullable:true
addressLine3 nullable:true
town nullable:true
county nullable:true
country nullable:true
postcode nullable:true
}
}
然而,当我写一个集成测试 - 我发现为位置创建的级联不起作用(我必须在传递到场地之前保存位置不再是traient。也是当我在场地上使用flush运行删除:true启用,并查询地址我仍然得到返回的嵌入地址 - 我想与flush:true我看到我的GeoAddress级联删除,但我的测试失败,因为我没有得到null使用GeoAddress.get(loc。 id)正如我所期待的那样
@Integration
@Rollback
class VenueIntegrationSpec extends Specification {
void "test venue with an address" () {
when: "create a venue and an address using transitive save on embedded "
GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH")
address.save() //have to save first - else Venue save fails
Venue v = new Venue (name: "bistro", location: address)
def result = v.save()
then: "retrieve venue and check its location loaded eagerly "
Venue lookupVenue = Venue.get(v.id)
GeoAddress loc = lookupVenue.location
loc.postcode == "IP4 2TH"
loc.town == "Ipswich"
when: " we delete the venue, it deletes the embedded location (Address)"
v.delete (flush:true)
GeoAddress lookupLoc = GeoAddress.get (loc.id)
then: "address should disppear"
lookupLoc == null
}
我以为我已经正确设置了这个,但显然我还没有。可以解释为什么我的Venue.save()和delete()的级联动作不会级联到我的嵌入式位置(GeoAddress)条目。
提前致谢
答案 0 :(得分:0)
如果我理解正确
cascade: "all-delete-orphan"
仅在您拥有hasMany=[something:Something]
在你的情况下,hasOne
或GeoAddress location
可能是一个更好的设置,如果我要创建这样的关系。我知道两者之间存在细微差别。
无论如何你正在测试所有的理论。我认为您需要捕获错误以便开始解决为什么它没有预期的行为级联。所以
if (!v.delete(flush:true) {
println "--- ${v.errors}"
}
或包裹它
尝试捕获块
。我有一个与hasMany关系类似的问题,这是由于与其他表共享的记录由于底层hasMany表关系本身的设置。诀窍是从对象本身中删除条目:
lookupVenue .removeFromLocation(loc)
正如我所说,这是一个有很多关系
答案 1 :(得分:0)
我写了太新的测试,两者都有效 - 但最初的测试没有。我做了一些奇怪的事 - 只是没有发现它。这两个新的测试做同样的流程 - 只是变量是不同的 - 两者都有效。所以问题在于第一次测试。
修订测试
@Integration
@Rollback
class VenueIntegrationSpec extends Specification {
def setup() {
}
def cleanup() {
}
//original test - this fails, have to explicitly delete loc to make it work
void "test venue with an address" () {
when: "create a venue and an address using transitive save on embedded "
GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH")
address.save()
Venue v = new Venue (name: "bistro", location: address)
def result = v.save(flush:true)
then: "retrieve venue and check its location loaded eagerly "
Venue lookupVenue = Venue.get(v.id)
GeoAddress loc = lookupVenue.location
loc.postcode == "IP4 2TH"
loc.town == "Ipswich"
when: " we delete the venue, it deletes the embedded location (Address)"
//loc.delete(flush:true)
v.delete (flush:true)
if (v.hasErrors())
println "errors: $v.errors"
GeoAddress lookupLoc = GeoAddress.get (loc.id)
then: "address should disppear"
Venue.get (v.id) == null
lookupLoc == null
}
//new test - external entity - works
void "test with tempLocation" () {
when: ""
TempLocation temp = new TempLocation(name:"will")
Venue v = new Venue (name: "bistro", temp: temp)
assert v.save(flush:true)
Venue lookupVenue = Venue.get(v.id)
TempLocation t = lookupVenue.temp
assert t.name == "will"
//try delete
v.delete (flush:true)
then : " retrieve temp"
TempLocation.findAll().size() == 0
}
//new test - reuse embedded entity - works
void "test with GeoLocation" () {
when: ""
GeoAddress a = new GeoAddress(town:"ipswich")
Venue v = new Venue (name: "bistro", location: a)
assert v.save(flush:true)
Venue lookupVenue = Venue.get(v.id)
GeoAddress ta = lookupVenue.location
assert ta.town == "ipswich"
//try delete
v.delete (flush:true)
then : " retrieve temp"
GeoAddress.findAll().size() == 0
}
}
正在测试的修订主题 - Venue.groovy with emebbed GeoAddress
class Venue {
String name
LocalDate dateCreated
LocalDate lastVisited
LocalDate lastUpdated
GeoAddress location
Collection posts
//test behaviour
TempLocation temp
static hasOne = [location:GeoAddress, temp:TempLocation]
static hasMany = [posts:Post]
static embedded =['location']
static constraints = {
lastVisited nullable:true
location nullable:true, unique:true
posts nullable:true
temp nullable:true //remove later
}
static mapping = {
location cascade: "all-delete-orphan", lazy:false, unique:true //eager fetch strategy
posts sorted: "desc"
temp cascade: "all-delete-orphan", lazy:false, unique:true //remove later
}
}
class GeoAddress {
String addressLine1
String addressLine2
String addressLine3
String town
String county
String country = "UK"
String postcode
static belongsTo = Venue
static constraints = {
addressLine1 nullable:true
addressLine2 nullable:true
addressLine3 nullable:true
town nullable:true
county nullable:true
country nullable:true
postcode nullable:true
}
}
hack的新外部版本的地址/位置。使用相同的beongsTo /约束逻辑
删除版本的geoAddressclass TempLocation {
String name
//setup birdiectional one to one, cascade owned on venue
static belongsTo = [venue:Venue]
static constraints = {
name nullable:true
}
}
将尝试重新阅读火车 - 不知道为什么第一次测试失败,但接下来两次工作正常....下床睡觉 - 太累了
答案 2 :(得分:0)
我认为这是一个配置错误。嵌入式意味着实体嵌入在域类中。通常这是一个普通的POJO并且留在domainclass文件夹之外(并且在src / groovy文件夹中)。嵌入实体的所有字段都包含在嵌入实体的表中。 hasone设置两个域类实体之间的关系。因此要么使用嵌入式,要么使用hasOne,但不要同时使用两者。
此外,级联保存深层嵌套实体存在问题,这在3.2.5中得到解决。
答案 3 :(得分:0)
好的 - 我仔细阅读并查找差异 - 如果我在传递到场地构造函数之前保存嵌入的GeoAddress,则会发生这种情况(修改后的简单测试)
当我添加额外的a.save()时,在创建GeoAddress之后,测试将失败。如果我评论保存并重新运行 - 它工作正常。不确定这是一个功能还是一个bug。由于GeoAddress具有stati belongsTo = Venue声明,因此Venue应该进行传递保存。
//new test - reuse embedded entity - works
void "test with GeoLocation" () {
when: ""
GeoAddress a = new GeoAddress(town:"ipswich")
a.save()
Venue v = new Venue (name: "bistro", location: a)
assert v.save(flush:true)
Venue lookupVenue = Venue.get(v.id)
GeoAddress ta = lookupVenue.location
assert ta.town == "ipswich"
//try delete
v.delete (flush:true)
then : " retrieve temp"
GeoAddress.findAll().size() == 0
}
如果有人可以为我评论bug和功能 - 那么如果有必要,我可以在grails项目上引发一个错误来修复它。否则我只需要仔细测试并确保我在我的代码中做正确的事情