我有大量的數據,我希望使用GORM加載到數據庫中。Grails DuplicateKeyException/NonUniqueObjectException異步承諾內批量加載時
class DbLoadingService {
static transactional = false
// these are used to expedite the batch loading process
def sessionFactory
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP
// these are example services that will assist in the parsing of the input data
def auxLoadingServiceA
def auxLoadingServiceB
def handleInputFile(String filename) {
def inputFile = new File(filename)
// parse each line and process according to record type
inputFile.eachLine { line, lineNumber ->
this.handleLine(line, lineNumber)
}
}
@Transactional
def handleLine(String line, int lineNumber) {
// do some further parsing of the line, based on its content
// example here is based on 1st 2 chars of line
switch (line[0..1]) {
case 'AA':
auxLoadingServiceA.doSomethingWithLine(line)
break;
case 'BB':
auxLoadingServiceB.doSomethingElseWithLine(line)
break;
default:
break;
}
if (lineNumber % 100 == 0) cleanUpGorm()
}
def cleanUpGorm() {
def session = sessionFactory.getCurrentSession()
session.flush()
session.clear()
propertyInstanceMap.get().clear()
}
}
class AuxLoadingServiceA {
static transactional = false
doSomethingWithLine(String line) {
// do something here
}
}
class AuxLoadingServiceB {
static transactional = false
doSomethingElseWithLine(String line) {
// do something else here
}
}
我故意只對每一行的負載做了頂級服務transactional。實際上在頂層下有很多級別的服務,而不僅僅是所示的單個Aux A服務層。因此,我不希望產生多層事務的開銷:我認爲我應該只需要1.
加載到數據庫中的數據模型包含一對具有hasMany/belongsTo關係的域對象。與域對象的這種交互是在子層內完成的,並且不會顯示在我的代碼中以保持示例的可管理性。
,這似乎是引起該問題的域對象類似於此:
class Parent {
static hasMany = [children: Child]
static mapping = {
children lazy: false
cache true
}
}
class Child {
String someValue
// also contains some other sub-objects
static belongsTo = [parent : Parent]
static mapping = {
parent index: 'parent_idx'
cache true
}
}
需要所示的cleanupGorm()方法,否則服務研磨到大量的線路後完全停止。當我移動加載到一個異步過程,這樣一旦
// Called from with a service/controller
dbLoadingService.handleInputFile("someFile.txt")
然而,:
def promise = task {
dbLoadingService.handleInputFile("someFile.txt")
}
我得到
當我啓動數據庫負載,一切工作完全按預期一個DuplicateKeyException/NonUniqueObjectException:
error details: org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session : [com.example.SampleDomainObject#1]; nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.example.SampleDomainObject#1]
所以,我的問題是,什麼是關於將大量數據異步加載到Grails DB中的最佳實踐?爲了確保內存中的對象在會話中保持一致,是否需要執行刷新/清除會話的操作?緩存對象時是否需要完成某些操作?
首先,你不應該做這樣的重批處理。使用像Spring Batch這樣的真正的批處理框架。但是,您是否嘗試過爲每個任務使用新的休眠會話?這可能有幫助。 – 2015-02-10 19:37:38
新的hibernate會話,意味着這裏有一個新的hibernate會話嗎? inputFile.eachLine {line,lineNumber - > this.handleLine(line,lineNumber) } – John 2015-02-10 19:42:30
是的,使用這個:http://grails.github.io/grails-doc/latest/ref/Domain%20Classes/withNewSession。 html – 2015-02-10 19:45:06