我需要从客户端域中的某些地址字段计算coords,因此为了将域类与此要求分离,我添加了一个使用Google Maps API获取正确值的自定义侦听器。
一切正常,但是调试'我已经意识到更新侦听器内的域属性会引发另一个更新事件,并且我的听众每次更新都会调用Google API 。
有人遇到过这个问题吗?我做错了什么?
域
class Client{
String address
String city
String country
String postalCode
double lat
double lng
....
}
服务
class GoogleMapsService{
static transactional=false
def grailsApplication
def geocode(String address){
address=address.replaceAll(" ", "+")
def urlApi=grailsApplication.config.googleMaps.apiUrl+"&address=${address}"
def urlJSON = new URL(urlApi)
def geoCodeResultJSON = new JsonSlurper().parseText(urlJSON.getText())
return geoCodeResultJSON
}
}
事件监听器:
class ClientListener extends AbstractPersistenceEventListener{
public boolean supportsEventType(Class<? extends ApplicationEvent> eventClass) {
switch(eventClass){
case [PreInsertEvent,
PreUpdateEvent,
PostInsertEvent,
PostUpdateEvent,
PostDeleteEvent]:
return true
default:
return false
}
}
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
if(!(event.entityObject instanceof Client)){
return
}
switch(event.eventType) {
//GOOGLE MAPS geocode
case [EventType.PreInsert,EventType.PreUpdate]:
this.updateCoords(event.entityObject)
break
//OTHER STUFF (notifications, no changing inside)
case EventType.PostUpdate:
//....
}
}
private String composeAddress(Client cli){
def resul=[cli.address,cli.city,cli.country,cli.postalCode]
return resul.findAll{it}.join(",")
}
private void updateCoords(Client cli){
def fullAddress=this.composeAddress(cli)
if(fullAddress){
def coords=googleMapsService.geocode(fullAddress)
if (coords.status=="OK"){
//**IMPORTANT STUFF THESE TWO LINES RAISE AN EVENT
cli.lat=coords.results.geometry.location.lat[0]
cli.lng=coords.results.geometry.location.lng[0]
}
}
}
}
更新
实体在侦听器中为null,因此我无法使其从preUpdate运行, 看起来有一个未解决的问题(http://jira.grails.org/browse/GRAILS-9374)
试试SaveOrUpdate ......
大更新:
我越来越近但仍然无法避免重复的电话。
由于我需要一个'saveOrUpdate'监听器,而且这个监听器是Hibernate特定的监听器,我最终会有两个监听器:
此组合的状态为:
1。它执行插入并调用postInsert grails侦听器,为什么????
ClientMailListener.onPersistenceEvent(AbstractPersistenceEvent) line: 45
ClientMailListener(AbstractPersistenceEventListener).onApplicationEvent(ApplicationEvent) line: 46
SimpleApplicationEventMulticaster.multicastEvent(ApplicationEvent) line: 97
GrailsWebApplicationContext(AbstractApplicationContext).publishEvent(ApplicationEvent) line: 324
ClosureEventTriggeringInterceptor.publishEvent(AbstractEvent, AbstractPersistenceEvent) line: 163
ClosureEventTriggeringInterceptor.onPostInsert(PostInsertEvent) line: 129
EntityIdentityInsertAction.postInsert() line: 131
EntityIdentityInsertAction.execute() line: 90
ActionQueue.execute(Executable) line: 273
ClosureEventTriggeringInterceptor.performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) line: 250
ClosureEventTriggeringInterceptor(AbstractSaveEventListener).performSave(Object, Serializable, EntityPersister, boolean, Object, EventSource, boolean) line: 203
ClosureEventTriggeringInterceptor(AbstractSaveEventListener).saveWithGeneratedId(Object, String, Object, EventSource, boolean) line: 129
ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).saveWithGeneratedOrRequestedId(SaveOrUpdateEvent) line: 210
ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).entityIsTransient(SaveOrUpdateEvent) line: 195
ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).performSaveOrUpdate(SaveOrUpdateEvent) line: 117
ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).onSaveOrUpdate(SaveOrUpdateEvent) line: 93
ClosureEventTriggeringInterceptor.onSaveOrUpdate(SaveOrUpdateEvent) line: 108
SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685
2. 然后调用save-update侦听器,并更新de object
ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34
SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685
SessionImpl.saveOrUpdate(String, Object) line: 677
SessionImpl.saveOrUpdate(Object) line: 673
3。然后,再次调用postUpdate grails侦听器 :(
上次更新
最后尝试只使用一个监听器来保持简单。看起来重点是插入后执行hibernate'save-update'监听器,详情:
如果我禁用自定义侦听器而离开Hibernate侦听器(GEO),则会发生以下情况:
1。插入客户端(空白坐标)并调用saveupdate Listener:
ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34
SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685
SessionImpl.saveOrUpdate(String, Object) line: 677
2。更新了Coords,并且有更新
对不起,这个大型更新,想法??
答案 0 :(得分:4)
Grails在后台使用Hibernate。通常,您不会更新PreUpdate
事件中的属性。您可以这样做,但如果您不更新属性和基础PersistentEntity
,您可能会遇到一些不良行为。
解释您获得两次更新的原因的工作流程:
lat
/ lng
属性。lat
/ lng
的旧值。dirty
lat
/ lng
的相同值。 避免重复更新的最简单的解决方案是使用SaveOrUpdate
事件而不是BeforeUpdate
,因为这实际上发生在流程的早期。
但是,,因为您的目标是尽量减少不必要的API调用次数,所以仍然值得使用BeforeUpdate
,因为在调用API之前手动检查您的实体是否有点肮脏可能比它的更多努力价值。所以,你要做的是,正如@Andrew建议的那样,只要确保你更新属性和,即:
event.entity.setProperty('lat', coords.results.geometry.location.lat[0])
event.entity.setProperty('lng', coords.results.geometry.location.lng[0])
cli.lat = coords.results.geometry.location.lat[0]
cli.lng = coords.results.geometry.location.lng[0]
更新:我的假设是检查域对象是否脏,可能很烦人实际上是基于我必须为NHibernate编写的代码。这当然是Grails,它应该像这样简单:
if (event.entityObject.isDirty()) { /* Call API */ }
答案 1 :(得分:1)
可以尝试使用lat
,而不是直接lng
/ EntityAccess
设置者访问权限。有关示例,请参阅Grails核心中的AutoTimestamp listener。