oracle数据库主键用nextval,batchSave系列方法报“无效的列索引”

jfinal2.2


batchSave系列方法中:

QQ图片20160718195313.png

columns里应该把主键名去掉。



评论区

JFinal

2016-07-18 21:25

能否更加确地说明一下问题,反馈内容随时可以修改,目前无法判断问题什么,感谢支持

i++

2016-07-19 09:36

例如:oracle表名:t_user pk:userid 自增序列名:seq_user_id 对应model:User
字段有:userid username
List userList = new ArrayList();
userList.add(User.DAO.set("userid","seq_user_id.nextval").set("username","jfinal"));
userList.add(User.DAO.set("userid","seq_user_id.nextval").set("username","redis"));
Db.batchSave(userList ,2);
////////////////////////
public class User extends Model {
private static final long serialVersionUID = 4827606126715970198L;
public static final User DAO = new User();
}

报错:
INFO - Sql: insert into t_user(username, userid) values(?, seq_user_id.nextval)
ERROR - com.jfinal.plugin.activerecord.ActiveRecordException: java.sql.SQLException: 无效的列索引
at com.jfinal.plugin.activerecord.DbPro.batch(DbPro.java:981)
at com.jfinal.plugin.activerecord.DbPro.batchSave(DbPro.java:1068)
at com.jfinal.plugin.activerecord.Db.batchSave(Db.java:546)

Sql语句只有一个问号。但batchSave(DbPro.java:1068)中columns="userid,username"
有两个字段,最终
pst.setObject(j + 1, value);两次。
是我哪里用法不对?
@JFinal

JFinal

2016-07-19 09:44

@JFinal 把 User.DAO 这个静态对象彻底删除掉,jfinal 已然不建议model中存在这 DAO对象了。

将这个 DAO 放在业务层中最好,否则就引发了你现在的误用,User.DAO.set(...) 这种用法会带来线程安全问题,因为 static 对象是被全局共享的。这个问题在 jfinal 手册有红色字体进行强调说明。

先去掉这个 DAO 对象的 set(...) 试试,再次强调一下 DAO,只能使用其中的 find(...) 与 paginate(...) 方法,其它的 save()、update()、set()、put() 一律不能使用,如果要使用,你应该 new User().set(...) 这样来用,而不是 DAO.set(...) 这样用

JFinal

2016-07-19 09:44

@i++ 前面 @ 错人了

i++

2016-07-19 10:01

@JFinal
List userList = new ArrayList();
userList.add(new User().set("userid","seq_user_id.nextval").set("username","jfinal"));
userList.add(new User().set("userid","seq_user_id.nextval").set("username","redis"));
Db.batchSave(userList ,2);试了一样的错。User类我没有用javaBean的set get(不喜欢,就是烦用那些get,set才用JFINAL的,但现在jfinal,又开始加入了baseBean,哎)
再有如你所说。要是DAO.set共享了。那只有一个User对象了。那也不会报错的。只会对数据库产生两个username=redis的记录。但我这个举例确实不该这么用

JFinal

2016-07-19 10:31

@i++ 加入 baseBean 是一个可选项,可以不用去生成 baseModel。关于 nextval 的用法,确实以前没有人在 Db.batchSave(...) 下用过,我也无法确定错误原因,希望你单步调试一下确定问题所在,再分享给社区,感谢你的支持 ^_^

JFinal

2016-07-19 10:33

@i++ 此外,即便是生成了 baseModel,但你最终的 Model 中仍然是看不到 getter、setter 的,所以这个 baseModel 特性其实是既引入了传统 java bean 的好处,又消除了其坏处,当然,如果你仍然不喜欢用,不去生成这个 baseModel 就可以了。

i++

2016-07-19 11:02

问题我自己改好了。我来反遗是因为我不想该JFINAL的源代码。我有代码洁癖。如果可以把修改的代码放到我项目的ext包里,就为所谓,但这个情况不行。要改DbPro.java。在public int[] batchSave(List modelList, int batchSize) 里改,或在 private int[] batch(Config config, Connection conn, String sql, String columns, List list, int batchSize)里改。希望下个版本能改好。

JFinal

2016-07-19 11:12

@i++ 能否把修改后的代码分享出来,方便 jfinal 改进。此外,你其实不用直接改 DbPro,可以通过继承 DbPro,然后覆盖掉 batchSave() 方法,最后再通过 DbKit 的一些工具方法将原来的 DbPro 用你的 MyDbPro 替换掉即可。

i++

2016-07-19 11:23

条条大道通罗马,但我希望的是。有一个基础框架给出的有且只有一条相对好走能通的路。因为是基础框架,给那么多实现方式,不同的人用起来就会选择自己的方式,提供的方式多了,每个方式都要去改进,越改越多,多了再改,就会像spring一样,越来越大,到最后就背离了你的初忠了。多种传参方式,路由方式,要不要baseMode,其实只要提供一种就好了。要扩展的自己的扩,jfinal官方实在要做也弄个不同分支出来。有喜欢的人下有这些功能的。不喜欢的用原来的。

JFinal

2016-07-20 21:38

@i++ 你说得没错,给予用户太多选择不一定是好事,jfinal 在很多设计上也是在坚持这个原则,但是在 base model 这个功能上,很多用户的需求太强烈了,并且有些第三方库需要 getter、setter 支持才能正常工作,已经到了不能不做的地步

i++

2017-03-20 19:23

@jfinal
3.0版,这个问题还是没改。这个框架没有做单元测试吧。把DbPro.java中的两处:
int index = 0;
// the same as the iterator in Dialect.forModelSave() to ensure the order of the attrs
for (Entry e: attrs.entrySet())
attrNames[index++] = e.getKey();
String columns = StrKit.join(attrNames, ",");
代码改成:
StringBuffer columns = new StringBuffer();
for (Entry e : attrs.entrySet()){
String key = e.getKey();
if (attrs.get(key) instanceof String && config.dialect.isOracle() && ((String)attrs.get(key)).endsWith(".nextval")) {
} else {
columns.append(key+",");
}
}
if (columns.length()>1) {
columns = columns.deleteCharAt(columns.length()-1);
}

就可以了。

JFinal

2017-06-23 11:30

@i++ jfinal 3.1 版本改进了这个地方,升级到了 jfinal 3.1 没?

i++

2018-12-27 18:30

@JFinal
你好
public int[] batchSave(String tableName, List recordList, int batchSize)
oracle 自增主键时这个方法还是不能用。
3.6版的也是。
这个方法中把主键从columns删除的同时,应该把主键赋值给pKeysNoUse

否则在config.dialect.forDbSave(tableName, pKeysNoUse, record, sql, parasNoUse);方法中isPrimaryKey(colName, pKeys) 是false的。
会把主键拼成?

i++

2018-12-28 15:31

修正后的方法:
public int[] batchSave(String tableName, List recordList, int batchSize) {
if (recordList == null || recordList.size() == 0)
return new int[0];

Record record = recordList.get(0);
Map cols = record.getColumns();
int index = 0;
StringBuilder columns = new StringBuilder();
String pKeys = "";//增加的
// the same as the iterator in Dialect.forDbSave() to ensure the order of the columns
for (Entry e : cols.entrySet()) {
if (config.dialect.isOracle()) { // 支持 oracle 自增主键
Object value = e.getValue();
if (value instanceof String && ((String)value).endsWith(".nextval")) {
pKeys = pKeys.concat(",").concat(e.getKey());//增加的
continue ;
}
}

if (index++ > 0) {
columns.append(',');
}
columns.append(e.getKey());
}
if(pKeys.startsWith(",")){
pKeys.replaceFirst(",", "");
}
StringBuilder sql = new StringBuilder();
String[] pKeysNoUse = pKeys.split(",");//修改的
List parasNoUse = new ArrayList();
config.dialect.forDbSave(tableName, pKeysNoUse, record, sql, parasNoUse);
return batch(sql.toString(), columns.toString(), recordList, batchSize);
}

热门反馈

扫码入社