クライアント ドメインのいくつかの住所フィールドから座標を計算する必要があるため、ドメイン クラスをこの要件から切り離すために、Google Maps API を使用して正しい値を取得するカスタム リスナーを追加しました。
すべてが機能しますが、デバッグ中です。リスナー内のドメイン プロパティを更新すると、別の更新イベントが起動され、その結果、更新ごとにリスナーが Google API を 2 回呼び出すことに気付きました。
誰かがこの問題を経験していますか?? 私は何を間違っていますか??
ドメイン:
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 固有のリスナーであるため、最終的に 2 つのリスナーができます。
- ClientMailListener (Grails カスタムリスナー、postinsert、postupdate、postdelete、オブジェクトに変更なし)
- ClientGeoListener (「save-update」としてマッピングされた Hibernate リスナー)
このコンボのステータスは次のとおりです。
- 更新:OK、両方のリスナーが一度だけ呼び出されます
- 挿入:KO!!、もっと深く見てみましょう。
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 リスナーを呼び出し、オブジェクトを更新します
ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34
SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685
SessionImpl.saveOrUpdate(String, Object) line: 677
SessionImpl.saveOrUpdate(Object) line: 673
3.続いて、postUpdate grails リスナーが再度呼び出されます:(
最後の更新
最後に、シンプルにするためにリスナーを 1 つだけにしてみてください。全体のポイントは、挿入後に hibernate の「save-update」リスナーが実行されることです。詳細は次のとおりです。
Hibernate リスナー (GEO) を離れるカスタム リスナーを無効にすると、次のようになります。
1.クライアント (空白の座標) に挿入し、saveupdate リスナーを呼び出します。
ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34
SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685
SessionImpl.saveOrUpdate(String, Object) line: 677
2.座標が更新され、更新があります
この大規模な更新について申し訳ありません、アイデア??