NutzCN Logo
精华 nutz dao 并非所有变量都已绑定
发布于 124天前 作者 qq_e25320ed 406 次浏览 复制 上一个帖子 下一个帖子
标签:

Sql sql = Sqls.create("select * from person where COD_CF_ID =@codCfId");
sql.params().set("codCfId", 3183);
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao.getEntity(Person.class));
dao.execute(sql);
List list = sql.getList(Person.class);

报错: org.nutz.dao.DaoException: java.sql.SQLException: ORA-01008: 并非所有变量都已绑定
debug时候发现sql对象预览为"select * from person where COD_CF_ID ="
占位符并没有成功设入参数。

PS: 这段代码之前跑了很多次都没问题,今天突然有问题。而且nutz dao,之前也出现过类似问题,参数无法设进去。
nutz版本 1.b.51

觉得很可能是nutz dao的bug

61 回复

预览被截断是老bug,但不影响运行.

先换最新版试试吧

项目已经稳定,没法临时换新版本。
我的问题和这个应该是类似的。https://nutz.cn/yvr/t/vc8rehdn4uhq4q5se34ds0s1jh。
我只是奇怪,为什么同样的程序之前没问题,现在又有问题了。

贴日志看看

你本地可以稳定重现吗?

日志是贴不了太长还是有限制? 老是报网络错误
目前是可以稳定重现的。

比较坑爹的是这个问题,时不时出现,而且是很重要的问题,绕不过去,很多同事都遇到过。

从调用这句SQL的日志开始贴就行了

at org.nutz.dao.impl.sql.run.NutDaoExecutor.exec(NutDaoExecutor.java:43)
at org.nutz.dao.impl.DaoSupport$DaoExec.invoke(DaoSupport.java:249)
at org.nutz.dao.impl.sql.run.NutDaoRunner.run(NutDaoRunner.java:31)
at org.nutz.dao.impl.DaoSupport._exec(DaoSupport.java:204)
at org.nutz.dao.impl.DaoSupport.execute(DaoSupport.java:182)
at com.ifs.infra.database.BaseDao.searchBySql(BaseDao.java:732)

执行SQL开始... 不单单是报错的这个

2017-06-19 14:21:50 INFO (main) Can not find @Column(rat_bid_lost2) in table/view ()
2017-06-19 14:21:50 DEBUG (main) SELECT * FROM CMS_CF_MAST WHERE COD_CF_ID=?
| 1 |
|------|
| 3183 |
For example:> "SELECT * FROM CMS_CF_MAST WHERE COD_CF_ID=3183"
2017-06-19 14:21:51 DEBUG (main) SELECT * FROM CMS_CF_LOAN WHERE COD_LOAN_ID=?
| 1 |
|------|
| 3183 |
For example:> "SELECT * FROM CMS_CF_LOAN WHERE COD_LOAN_ID=3183"
2017-06-19 14:21:52 DEBUG (main) SELECT * FROM CMS_CF_LOAN WHERE COD_LOAN_ID=?
| 1 |
|------|
| 3183 |
For example:> "SELECT * FROM CMS_CF_LOAN WHERE COD_LOAN_ID=3183"
2017-06-19 14:23:19 DEBUG (main) select * from CMS_CF_SC_XREF where COD_CF_ID =
2017-06-19 14:23:19 DEBUG (main) SQLException

这里有一段奇怪的日志,设了3183,但是执行好像又丢了

首先, 预处理SQL是对的
然后, For example的SQL也是对的, 那么参数矩阵应该没问题.

为啥你贴的日志执行了3次SQL, 是摘取的??? 并非连续的??

执行 sql.getAdaptors 和 sql.getParamMatrix, 打印他们的返回值看看.

PS: 回复按钮旁边还有个"插入代码或日志片段"的按钮!

执行三次,可能是我log4j问题,往同一文件打多次。
插入代码也会爆网络错误,页面卡住,假死。不知道是不是我们这边公司网络做了限制。
现在执行下你说的两个方法

打印sql.getAdaptors的数量及里面元素类名, sql.getParamMatrix的返回值直接Json.toJson打印就好了

话说, 为啥你的java代码里面的SQL是小写,但日志里面的是大写的呢??

Json.toJson(sql.getAdaptors()):
[[, {,
, }, ]]

Json.toJson(sql.getParamMatrix()):
[]

你说的是对的,刚才那个日志可能我拿错了,这是前面的查询。也是类似的查,只不过调的是dao.fetch(c, id)

通过dao.fetch(c, id)和dao.query(Class, Cnd, null),都是没问题的,只有这种动态传参才有问题

Sql sql = Sqls.create("select * from person where COD_CF_ID =@codCfId");
sql.params().set("codCfId", 3183);
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao.getEntity(Person.class));

log.info("size="+sql.getAdaptors().length);
log.info(Json.toJson(sql.getParamMatrix()), JsonFormat.full());

dao.execute(sql);
List<Person> list = sql.getList(Person.class);

写错了一行, 这样才对

log.info(Json.toJson(sql.getParamMatrix(), JsonFormat.full()));

我发现新大陆了。
Sql sql = Sqls.create("select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID =@codCfId");
sql.params().set("codCfId", codLoanId);
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao.getEntity(Person.class));
dao.execute(sql);
List list = sql.getList(Person.class);

这样子,参数成功传进去了。

Json.toJson(sql.getAdaptors()):
[{
}]
Json.toJson(sql.getParamMatrix()):
[[3183]]

按我写的log语句打印看看

修改之后的:
2017-06-19 15:52:16 INFO (main) size=1
2017-06-19 15:52:19 INFO (main) [[3183]]
2017-06-19 15:52:21 DEBUG (main) select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID =3183

移除掉 1=1, 重新打印

后面的那句debug日志是怎么来的

我把1=1去掉,现在又好了。
2017-06-19 15:53:46 INFO (main) size=1
2017-06-19 15:53:47 INFO (main) [[3183]]
2017-06-19 15:53:52 DEBUG (main) select * from CMS_CF_SC_XREF where COD_CF_ID =3183

真无法描述我现在的凌乱心情

现在的代码又改了回去,一字不变。真是神奇:
Sql sql = Sqls.create("select * from CMS_CF_SC_XREF where COD_CF_ID =@codCfId");
sql.params().set("codCfId", codLoanId);
logger.info("size="+sql.getAdaptors().length);
logger.info(Json.toJson(sql.getParamMatrix(), JsonFormat.full()));
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao.getEntity(Person.class));
dao.execute(sql);
List list = sql.getList(Person.class);

回复按钮旁边有贴代码的按钮啊啊啊啊啊啊啊啊啊

注释掉log语句看看变化

我去掉日志也好了。

等等,现在又重现了,我加了一个空格。

Sql sql = Sqls.create("select * from CMS_CF_SC_XREF where COD_CF_ID = @codCfId ");
sql.params().set("codCfId", codLoanId);
logger.info("size="+sql.getAdaptors().length);
logger.info(Json.toJson(sql.getParamMatrix(), JsonFormat.full()));
sql.setCallback(Sqls.callback.entities());
sql.setEntity(dao.getEntity(Person.class));
dao.execute(sql);
List list = sql.getList(Person.class);

sql 预览的时候之前是有占位符的,这次这个占位符,没了

select * from CMS_CF_SC_XREF where COD_CF_ID = 

日志打印出来是:
2017-06-19 16:07:35 INFO (main) size=1
2017-06-19 16:07:36 INFO (main) []

 2017-06-19 16:07:35 INFO  (main) size=1
 2017-06-19 16:07:36 INFO  (main) []
 2017-06-19 16:08:17 DEBUG (main) select * from CMS_CF_SC_XREF where COD_CF_ID = 
 2017-06-19 16:08:17 DEBUG (main) SQLException
 java.sql.SQLException: ORA-01008: 并非所有变量都已绑定

我在前面加1=1 依然报错

Sql sql = Sqls.create("select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID = @codCfId ");
 2017-06-19 16:11:30 INFO  (main) size=1
 2017-06-19 16:11:35 INFO  (main) []
 2017-06-19 16:11:38 DEBUG (main) select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID = 
 2017-06-19 16:11:38 DEBUG (main) SQLException
 java.sql.SQLException: ORA-01008: 并非所有变量都已绑定

参数矩阵是空数组,应该就是这里的问题

2017-06-19 16:07:36 INFO  (main) []

在成功执行的时候,sql预览是能看见占位符的
select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID = @codCfId
在执行失败的时候,没有占位符。参数自然设不进去。
select * from CMS_CF_SC_XREF where 1=1 and COD_CF_ID =
我猜是占位符解析的问题

debug一下getParamMatrix方法, 看看里面的rows.size()是不是0

https://github.com/nutzam/nutz/blob/1.b.51/src/org/nutz/dao/impl/sql/NutSql.java

感觉是getParamMatrix里面调用了一次rows.remove的样子

rows.size() 确实是0,这段代码等于白跑,啥也没干。

public Object[][] getParamMatrix() {
        // 仅仅去掉队尾没有参数设定的VarSet,尽可能的不遍历
        if (rows.size() > 0) {
            VarSet vs = rows.get(rows.size() - 1);
            while (null != vs) {
                if (vs.keys().size() == 0) {
                    rows.remove(vs);
                    vs = null;
                    if (rows.size() > 0)
                        vs = rows.get(rows.size() - 1);
                } else {
                    break;
                }
            }
        }
        Object[][] re = new Object[rows.size()][adaptors.length];
        int i = 0;
        for (VarSet row : rows) {
            Object[] cols = re[i++];
            for (String name : literal.getParamIndexes().names()) {
                Object value = row.get(name);
                int[] is = literal.getParamIndexes().getOrderIndex(name);
                for (int x : is)
                    cols[x] = value;
            }
        }
        return re;
    }

是第一个if就是0吗?? rows.remove(vs); 打个断点看看

第一个if,就是0。

最新跑了一次,同样的代码,又好了。
rows是一个对象,size()=1
[org.nutz.dao.impl.sql.SimpleVarSet@5d6055f2]

看看NutSql走哪个构造方法创建的, 难道addBatch没调用成功??

按源码上看, 那是无参构造方法调用了addBatch,另外2个public方法都调用了this(),

我认为是Sqls.create()的问题

得查查为啥rows是空list了

NutSql这个类很古怪。在调NutSql(String sql)方法的时候,这个rows是有对象的。但是生成对象后调getParamMatrix(),发生是一个空的list。
rows是一个私有的, private List rows;
什么时候变成空的呢?
整个NutSql只有clearBatch会清空,但是没发现有调。

getParamMatrix里面有个rows.remove

我在getParamMatrix第一行打了断点。进第一行就是空list

恩, 某个代码在 addBatch 与 getParamMatrix() 之间把rows清空了的节奏

我在debug NutzSql的时候,只要在调试表达式里面调toString()马上就变为空串。而toString()刚好调用了getParamMatrix().

@Override
    public String toString() {
        /*
         * // 语句模板 String[] ss = _createSqlElements();
         * 
         * // 填充参数 VarIndex vIndex = literal.getParamIndexes(); VarSet vs =
         * rows.get(0); for (String name : vIndex.names()) { int[] is =
         * vIndex.indexesOf(name); String s =
         * Sqls.formatFieldValue(vs.get(name)).toString(); for (int i : is)
         * ss[i] = s; }
         * 
         * return Lang.concat(ss).toString();
         */
        return super.toStatement(this.getParamMatrix(), this.toPreparedStatement());
    }

这个方法确实有可能会删掉,之前也试过,如果有两个参数的话,其中一个是不会删的。
如果只有一个参数,这里确实可能会删掉。

public Object[][] getParamMatrix() {
        // 仅仅去掉队尾没有参数设定的VarSet,尽可能的不遍历
        if (rows.size() > 0) {
            VarSet vs = rows.get(rows.size() - 1);
            while (null != vs) {
                if (vs.keys().size() == 0) {
                    rows.remove(vs);
                    vs = null;
                    if (rows.size() > 0)
                        vs = rows.get(rows.size() - 1);
                } else {
                    break;
                }
            }
        }
        Object[][] re = new Object[rows.size()][adaptors.length];
        int i = 0;
        for (VarSet row : rows) {
            Object[] cols = re[i++];
            for (String name : literal.getParamIndexes().names()) {
                Object value = row.get(name);
                int[] is = literal.getParamIndexes().getOrderIndex(name);
                for (int x : is)
                    cols[x] = value;
            }
        }
        return re;
    }

我想很可能找到问题了。我把断点打在List list = sql.getList(Person.class)。之后。这样避免鼠标移动的时候去调NutzSql去调toString()方法,同样的代码,执行成功。

toString方法触发的?

那是不是dao.execute之前执行一次toString就必定重现?

在Sql sql = Sqls.create()之后,在sql.params().set("codCfId", codLoanId);之前 调sql.toString() bug必现。

这段代码的remove()会被执行。其实想想在debug的时候鼠标移动过去就会调toString()方法,只不过这时候应该不会进我们的断点。

  if (rows.size() > 0) {
            VarSet vs = rows.get(rows.size() - 1);
            while (null != vs) {
                if (vs.keys().size() == 0) {
                    rows.remove(vs);
                    vs = null;
                    if (rows.size() > 0)
                        vs = rows.get(rows.size() - 1);
                } else {
                    break;
                }
            }
        }

嗯嗯,像是真相了

那回到最初的代码,非debug,没有toString相关的东西,是不是就不会出问题了?

对的,经测试没发现问题

新版没有remove操作了,规避了这个问题

好的,谢谢兽兽,耐心帮我看这个问题

@qq_e25320ed 互相学习嘛

添加回复
请先登陆
回到顶部