Използвате имена на класове и полета в HQL и имена на таблици и колони в SQL. Така че не трябва да виждате долни черти (поне не много) в HQL заявки. Освен това е малко вероятно това да е фактор тук, но HQL заявките се изпълняват от findAll и тези, които се изпълняват от executeQuery може да бъде малко по-различен. Не мога да си спомня каква е разликата, но executeQuery е правилният, без значение кой клас домейн е включен.
Трудно е да се знае, без да се видят класовете на домейна, но изглежда като artifact_id трябва да бъде artifact.id и document_id трябва да бъде document.id . И тъй като имате екземпляра на документа, по-правилно е да сравнявате обектите, а не техните идентификатори. Накрая приемам, че active е булево свойство, така че се нуждае от булева стойност, а не от 1 или 0. И така, като съберем всичко това, най-доброто ми предположение е, че това е, което искате:
def artifacts = Artifact.executeQuery(
"FROM Artifact WHERE id NOT IN ( " +
"SELECT artifact.id FROM Classification " +
"WHERE active = :active) AND document =:doc",
[active: true, doc:document],
[max:limit, offset:startIndex])
Обърнете внимание, че трябва да разделите стойностите на param от контролите за страниране в две карти.