在我尝试创建(或重复使用)并将关键字标签附加到图像库应用程序中的图像的并发情况(异步文件上载)中,我在创建时遇到唯一索引或主键违规 标记域对象。
控制器调用服务方法将图像添加到库以及属于它的关键字标签。
我是以错误的方式解决这个问题还是我错过了什么? 我目前正在使用Grails 2.3.5。
代码:
class ImageAssetService {
def addTagToImage(ImageAsset imageAsset, String kw, String locale) {
def tagName = kw.toLowerCase(Locale.forLanguageTag(locale))
// OFFENDING LINE NEXT
def tag = Tag.findOrSaveByNameAndLocale(tagName, locale, [lock: true])
imageAsset.addToTags(tag)
if(!imageAsset.save()) {
throw new ImageAssetException()
}
imageAsset
}
def addTagsToImage(ImageAsset imageAsset, Set<String> keywords, String locale) {
keywords.each { kw ->
addTagToImage(imageAsset, kw, locale)
}
imageAsset
}
// CONTROLLER CALLS THIS METHOD
def addImageAsset(InputStream inputStream, String filename, long fileSize, long authorId, String timeZoneOriginal, String heading, String description, Set tags, String imageCollectionName) {
// Create the ImageAsset domain object
ImageAsset imageAsset = new ImageAsset(
filename: filename,
fileSize: fileSize,
author: Author.get(authorId),
timeZoneOriginal: TimeZone.getTimeZone(timeZoneOriginal),
heading: heading,
description: description
).save()
// Add any tags
addTagsToImage(imageAsset, tags, 'en')
/*
...
CODE REMOVED FOR BREVITY
....
*/
return imageAsset
}
}
class Tag {
String locale
String name
static hasMany = [ translations : Tag, synonyms : Tag ]
void setName(String name) { this.@name = name.toLowerCase() }
static constraints = {
locale unique: ['name']
}
static belongsTo = ImageAsset
static searchable = {
except = ['locale']
name boost: 2.0
translations component: [prefix: 'translations_', maxDepth: 2]
synonyms component: [prefix: 'synonyms_', maxDepth: 2]
}
static mapping = {
table 'tags'
}
}
class ImageAsset {
String filename
String heading
String description
String place
String city
String country
String gps
long fileSize = 0
int pixelWidth = 0
int pixelHeight = 0
Date dateTimeOriginal
TimeZone timeZoneOriginal
boolean enabled = true
Date dateCreated
static belongsTo = [
Author,
ConceptualImageCategory,
RepresentativeImageCategory,
ImageCollection
]
static hasOne = [ author : Author ]
static hasMany = [
conceptualCategories : ConceptualImageCategory,
representativeCategories : RepresentativeImageCategory,
collections : ImageCollection,
metadata : Metadata,
tags : Tag
]
static constraints = {
filename blank: false
heading nullable: true
description nullable: true
place nullable: true
city nullable: true
country nullable: true
gps nullable: true
pixelWidth nullable: true
pixelHeight nullable: true
dateTimeOriginal nullable: true
timeZoneOriginal nullable: true
}
static mapping = {
description type: 'text'
}
static searchable = {
//only = ['filename', 'heading', 'description', 'tags', 'metadata']
author component: [prefix: 'author_']
tags component: [prefix: 'tags_']
metadata component: [prefix: 'metadata_']
}
}
错误讯息:
Unique index or primary key violation: "CONSTRAINT_INDEX_27 ON PUBLIC.TAGS(NAME, LOCALE) VALUES ( /* key:11 */ 895, 0, 'en', 'work')"; SQL statement:
insert into tags (id, version, locale, name) values (null, ?, ?, ?) [23505-173]. Stacktrace follows:
Message: Unique index or primary key violation: "CONSTRAINT_INDEX_27 ON PUBLIC.TAGS(NAME, LOCALE) VALUES ( /* key:11 */ 895, 0, 'en', 'work')"; SQL statement:
insert into tags (id, version, locale, name) values (null, ?, ?, ?) [23505-173]
Line | Method
->> 331 | getJdbcSQLException in org.h2.message.DbException
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 171 | get in ''
| 148 | get . . . . . . . . . . in ''
| 101 | getDuplicateKeyException in org.h2.index.BaseIndex
| 68 | add . . . . . . . . . . in org.h2.index.TreeIndex
| 52 | add in org.h2.index.MultiVersionIndex
| 125 | addRow . . . . . . . . . in org.h2.table.RegularTable
| 127 | insertRows in org.h2.command.dml.Insert
| 86 | update . . . . . . . . . in ''
| 79 | update in org.h2.command.CommandContainer
| 235 | executeUpdate . . . . . in org.h2.command.Command
| 154 | executeUpdateInternal in org.h2.jdbc.JdbcPreparedStatement
| 140 | executeUpdate . . . . . in ''
| 102 | doCall in org.grails.datastore.gorm.GormStaticApi$_methodMissing_closure2
| 105 | addTagToImage . . . . . in se.originalab.imagedb.ImageAssetService
| 94 | doCall in se.originalab.imagedb.ImageAssetService$_addTagsToImage_closure3
| 93 | addTagsToImage . . . . . in se.originalab.imagedb.ImageAssetService
| 45 | addImageAsset in ''
| 31 | upload . . . . . . . . . in se.originalab.imagedb.UploadController
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter . . . . . . . . in grails.plugin.cache.web.filter.AbstractFilter
| 53 | doFilter in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
| 49 | doFilter . . . . . . . . in grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter
| 82 | doFilter in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
| 1110 | runWorker . . . . . . . in java.util.concurrent.ThreadPoolExecutor
| 603 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 722 | run . . . . . . . . . . in java.lang.Thread
答案 0 :(得分:1)
我自己找到了解决方案。我不得不将Tag的创建分解为它自己的事务。现在,通过控制器的同步服务方法调用对Tag.findOrSaveByNameLocale()的每次调用。然后我将它们添加到ImageAsset。
这样做的一个问题是,如果ImageAsset的创建失败,标签仍然会被保留,但在我的用例中,这不是一个大问题。