我正在研究一个从字符串中解析文本单元(引用为“ngrams”)并将它们存储在MySQL数据库中的项目。
在MySQL中,我有以下过程,如果它不存在,则应该在特定数据集(表)中存储ngram并返回ngram的id:
CREATE PROCEDURE `add_ngram`(IN ngram VARCHAR(400), IN dataset VARCHAR(128), OUT ngramId INT)
BEGIN
-- try get id of ngram
SET @s = CONCAT('SELECT `id` INTO @ngramId FROM `mdt_', dataset, '_b1` WHERE `ngram` = ''', ngram, ''' LIMIT 1');
PREPARE stm FROM @s;
EXECUTE stm;
SET ngramId = @ngramId;
-- if id could not be retrieved
IF ngramId IS NULL THEN BEGIN
-- insert ngram into dataset
SET @s = CONCAT('INSERT INTO `mdt_', dataset, '_b1`(`ngram`) VALUES (''', ngram, ''')');
PREPARE stm FROM @s;
EXECUTE stm;
SET ngramId = LAST_INSERT_ID();
END;
END IF;
END$$
数据集表只有两列:id
,一个自动递增的int作为主键,ngram
,一个varchar(400)
作为唯一索引。< / p>
在我的scala应用程序中,我有一个接收字符串的方法,将其拆分为ngrams,然后通过将ngrams传递给上述过程返回ngrams'id的Seq:
private def processNgrams(text: String, dataSet: String) {
val ids = parser.parse(text).map(ngram => {
val query = dbConn.prepareCall("CALL add_ngram(?,?,?)")
query.setString(1, ngram)
query.setString(2, dataSet)
query.registerOutParameter(3, java.sql.Types.INTEGER)
query.executeUpdate()
dbConn.commit()
val id = query.getInt(3)
Debug(s"parsed ngram - id: $id ${" " * (3 - id.toString.length)}[ $ngram ]")
id
}
}
上面代码中的 dbConn
是java.sql.Connection
的实例,并且自动提交设置为false。
当我执行此操作时,我注意到数据库中存储了很少的ngrams。以下是上述方法的调试语句打印出来的内容:
因此,有多个ngrams彼此明显不同,似乎从过程返回相同的id。如果我查看数据库表,我可以看到例如ngram“i”的id为“1”,但似乎ngrams之后立即插入也返回id为“1”。对于我在表中查找的其他ngram也是如此。这让我相信程序调用可能会被缓存?
我已经尝试了很多东西,例如在方法之外创建准备好的调用并重新使用它并每次调用clearParameters,每次在方法内创建一个新的调用(如上所述),甚至睡觉呼叫之间的线程,但似乎没有任何区别。
我还通过在MySQL客户端中手动运行查询来测试该过程,但它似乎运行正常,尽管我的程序以比我手动更快的速度执行查询,因此这可能会产生影响。
我不完全确定这是一个JDBC问题,因为该程序会发出调用或MySQL问题。我是scala和MySQL程序的新手,所以请原谅我,如果这是非常简单的事情,那就逃过了我。
答案 0 :(得分:0)
想出来!事实证明,存储过程导致了所有麻烦。
在过程中,我通过执行动态SQL查询(因为表名被传入)来检查是否存在ngram,该值存储@ngramId
中的值,这是一个会话变量。我将它存储在@ngramId
而不是ngramId
(一个过程输出参数),因为预处理语句只能选择会话变量(或者我最初创建过程时被错误告知) 。接下来,我将@ngramId
的值设置为ngramId
,并检查ngramId
是否为null,以确定表中是否存在ngram;如果为null,则将ngram插入表中,并将ngramId
设置为最后插入的id。
问题在于,因为@ngramId
是会话变量,并且因为我对所有过程调用使用了相同的数据库连接,所以@ngramId的值在调用之间保持不变。因此,例如,如果我使用ngram“I”进行调用,并且在id为1的数据库中找到它,@ngramId
现在具有值1.接下来如果我尝试插入另一个没有的ngram存在于表中,动态select语句没有返回任何内容,因此@ngramId
的值保持为1.由于输出参数ngramId
填充了@ngramId
的值,现在它是不再是NULL,它绕过了在数据库中插入ngram的if语句,并返回了表中找到的最后一个ngram的id,导致ngram id的看似缓存。
解决方案是将以下行添加为过程中的第一个语句:
SET @ngramId = NULL;
在同一会话中调用该过程之间重置@ngramId
的值。