0

最近我开始重构我的 Grails 应用程序,一切看起来都很好,直到出现“对象引用未保存的瞬态实例”错误。在调试时,我发现我的一个域对象的 id 对于每个实例始终为空。即使是从数据库加载的实例。我发现复合 id(我在那个类中使用)已知会导致问题,但几天前它工作得很好,从那时起,我对这个类做了很多更改,但它们都不应该导致这种问题...至少我找不到可能导致它的变化。

class QuestionPriority implements Serializable{

static int maxPriority = 6
static int minPriority = 1
int priority = maxPriority
Date lastTestDate
Date nextTestDate = new Date()

static belongsTo = [question:Question, subscription:Subscription]

static constraints = {
    nextTestDate(nullable: true)
    lastTestDate(nullable: true)
}

static mapping = {
    id composite: ['subscription', 'question']
    version false
}

static QuestionPriority create(Subscription sub, Question question, boolean flush = false) {
    assert sub != null
    assert question != null
    assert sub.id != null
    assert question.id != null

    QuestionPriority qp = new QuestionPriority(subscription: sub, question: question)
    sub.addToPriorities(qp)
    question.addToPriorities(qp)
    assert (qp.save(flush: flush, insert: true, failOnError: true))
    return qp
}

boolean equals(other) {
    if (!(other instanceof QuestionPriority)) {
        return false
    }

    other.subscription?.id == subscription?.id &&
            other.getQuestionToAsk?.id == question?.id
}

int hashCode() {
    def builder = new HashCodeBuilder()
    if (subscription) builder.append(subscription.id)
    if (question) builder.append(question.id)
    builder.toHashCode()
}

void markAsCorrect() {
    priorityDown()
    calculateNextTestDate()
}

void markAsWrong() {
    priorityUp()
    calculateNextTestDate()
}

String getQuestionToAsk() {
    return question.question
}

String getExpectedAnswer() {
    return question.answer
}

Date calculateNextTestDate() {
    DailyTestMode mode = subscription.testMode
    if (!lastTestDate) {
        nextTestDate = new Date(0, 0, 0)
    } else {
        nextTestDate = mode.calculateNextAskDate(this)
    }
    return nextTestDate
}

static void delete(User user, Question q) {
    QuestionPriority qp = get(user.id, q)
    if (qp) {
        qp.delete()
    }
}

static QuestionPriority get(Subscription sub, Question question) {
    assert sub != null
    assert question != null
    assert sub.id != null
    assert question.id != null

    QuestionPriority qp = find 'from QuestionPriority where subscription.id=:subId and question.id=:questionId',
            [subId: sub.id, questionId: question.id]
    if (!qp) {
        qp = create(sub, question)
    }
    return qp
}

private void priorityUp() {
    priority = Math.min(priority + 1, maxPriority)
    lastTestDate = new Date()
    calculateNextTestDate()
}

private void priorityDown() {
    priority = Math.max(priority - 1, minPriority)
    lastTestDate = new Date()
    calculateNextTestDate()
}
}

我真的很感激任何帮助

- - -编辑 - - - -

问题域对象: class Question {

String question
String answer
boolean deleted

static transients = [ 'deleted']
static belongsTo = [studyList: StudyList]
static hasMany = [priorities: QuestionPriority]

static constraints = {
    question(blank:false)
    answer(blank:false)
}

static mapping = {
    priorities(cascade: 'all-delete-orphan')
}

void setAnswer(String a){
    answer = a.trim()
}

String toString(){
    return "${question} = ${answer}"

}
}

订阅域对象

class Subscription implements Serializable {

boolean active = true
Integer rating = null
Date subscriptionStartDate = new Date()
String dailyTestModeLiteral

static belongsTo = [user:User, studyList:StudyList]
static hasMany = [priorities:QuestionPriority]

static constraints = {
    priorities(nullable: true)
    rating(nullable: true)
    subscriptionStartDate(nullable: true)
}

static mapping = {
    priorities(cascade: 'all-delete-orphan')
}

void setDailyTestMode(DailyTestMode mode){
    dailyTestModeLiteral = mode.getModeLiteral()
    for(QuestionPriority priority:priorities){
        priority.calculateNextTestDate()
    }
}

DailyTestMode getTestMode(){
    return DailyTestMode.getMode(dailyTestModeLiteral)
}

public static Subscription create(User user, StudyList list, String testModeLiteral = NormalDailyTestStrategy.literal, boolean flush = false){
    Subscription subscription = get(user.id, list.id)
    if(!subscription){
        withTransaction {
            subscription = new Subscription(user: user, studyList: list, dailyTestModeLiteral: testModeLiteral)
            user.addToSubscriptions(subscription)
            list.addToSubscriptions(subscription)
            subscription.save(failOnError: true, flush: true)
        }
        subscription.studyList.questions.each {
            QuestionPriority.create(subscription, it)
        }
    }
    return subscription
}


int getScore(){
    float prioritySum = 0
    float priorityMax = 0
    float priorityMin = 0
    priorities.each {
        prioritySum += it.priority
        priorityMin += 1
        priorityMax += QuestionPriority.maxPriority
    }
    return Math.round(100 * (prioritySum-priorityMax)/(priorityMin - priorityMax))
}

public static void delete(User user, StudyList list){
    Subscription subscription = get(user.id, list.id)
    if(subscription){
        subscription.delete()
    }
}

static Subscription get(long userId, long studyListId) {
    find 'from Subscription where user.id=:userId and studyList.id=:studyListId',
            [userId: userId, studyListId: studyListId]

}

boolean equals(other) {
    if (!(other instanceof Subscription)) {
        return false
    }

    other.user?.id == user?.id &&
            other.studyList?.id == studyList?.id
}

int hashCode() {
    def builder = new HashCodeBuilder()
    if (user) builder.append(user.id)
    if (studyList) builder.append(studyList.id)
    builder.toHashCode()
}

boolean checkIfMatches(QuestionPriority questionPriority, String questionAsked, String answerGiven) {
    return(questionPriority.question.question==questionAsked &&
        questionPriority.question.answer == answerGiven)
}

QuestionPriority checkIfMatchesAny(String questionAsked, String answerGiven) {
    Question q = Question.withCriteria {
        and {
            eq('question', questionAsked)
            eq('answer', answerGiven)
            eq('studyList', studyList)
        }
    }
    if(q){
        List<QuestionPriority> qp = QuestionPriority.withCriteria {
            and {
                eq('question', q)
                eq('subscription', this)
            }
        }
        if(qp.size()>0){
            return qp[0]
        }else{
            return null
        }
    }
}
}

经过几个小时的调试,我仍然只知道save(flush: true, failOnError: true)在 QuestionPriority 对象上执行会返回未保存的对象(没有验证错误,没有异常......没有有用的信息)。

任何想法在哪里寻找或如何寻找这个问题的原因都会有所帮助,因为我 100% 坚持这个......

4

1 回答 1

0

经过数小时的搜索,将框架更改为最新版本并修复了版本更改导致的新问题,我解决了这个问题。看起来将复合 ID 更改为普通 ID + 复合唯一键解决了这个问题。我找不到其他解决方案,即使我的代码在前几个月使用复合 ID 运行良好......(与复合 ID 相关的任何代码部分在停止工作时都已更改)而且我几乎可以肯定我尝试过早先删除复合ID,然后它没有帮助......

于 2014-01-09T00:49:02.493 回答