Generator 模型生成器双主键问题

使用该类

com.jfinal.plugin.activerecord.generator.Generator

自动生成数据库映射的时候,双主键的表结构遇到了一些问题。


com.jfinal.plugin.activerecord.generator.MetaBuilder =>buildPrimaryKey()

方法拼接的主键与数据主键顺序不符。


如数据库双主键为:id1,id2

但生成的映射却为:id2,id1

blob.png

blob.png



blob.png



原来是MysqlJDBC 中 DatabaseMetaData 中getPrimaryKeys() 存取数据是用的TreeMap,而TreeMap如不指定排序器,默认将按照key值进行升序排序,所以导致主键顺序错误!MDZZ!


改源码!用LinkedHashMap!


评论区

JFinal

2017-02-15 20:06

这种情况 jfinal 早有考虑,如果你加入了 jfinal 俱乐部,可以在 jfinal-club 项目中看到如下的代码:
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
_MappingKit.mapping(arp);
// 强制指定复合主键的次序,避免不同的开发环境生成在 _MappingKit 中的复合主键次序不相同
arp.setPrimaryKey("document", "mainMenu, subMenu");
me.add(arp);

以上的 arp.setPrimaryKey(...) 这么代码,就是在 _MappingKit.mapping(...) 调用之后,再次强制指定复合主键的次序,这样就万无一失了

JFinal

2017-02-15 20:07

jfinal 建议对所有复合主键的表强制再指定一次复合主键的次序,确保一致性。复合主键的默认次序取决于反射得到的次序,jfinal 在开发这个功能的时候估计到未来可能出现次序不同的情况,你这次真的碰到了

埋头苦干

2017-02-15 20:09

@JFinal 问题是sql查询的出来的结果是正确的,但是resultset取出来顺序就不一样了。如果用下标取?

JFinal

2017-02-15 20:14

@埋头苦干 用 model.find 或者 Db.find 去查询

埋头苦干

2017-02-15 20:42

@JFinal 不不不,您理解错了,我说的是MetaBuilder 中的 buildPrimaryKey() 方法

JFinal

2017-02-15 21:45

@埋头苦干 buildPrimaryKey() 方法得到的 resultSet 中的主键次序很可能与建表语句中指定的字段次序有关,你打开表结构观察一下

埋头苦干

2017-02-15 21:51

@JFinal 我更新了一下问题,把图片上传上去了。第一个为自动生成的Mapping,第二个为数据库主键顺序。
偏偏就是反了。
哦,对了,我这是库之前是sqlite,没问题。
然后换到mysql,就有问题了

埋头苦干

2017-02-15 21:54

@JFinal 按照您第一条回复那样,确实可以解决问题,但是每次新建一个双主键的model 都需要修改config,失去了原来自动生成的意义了。

JFinal

2017-02-15 21:56

@埋头苦干 注意一个事实,双主键的反射生成,拿到的主键次序不是取决于 jfinal,而是取决于 JDBC,目前的设计是相对好的选择,如果你有更好的设计方案可以提给我

JFinal

2017-02-15 21:57

还有一个方案,是 jfinal 做决定,默认按照字典次序给排下序,但用户或许并不希望这种结果,还需要权衡

埋头苦干

2017-02-15 22:43

是JDBC的问题,mysql jdbc中DatabaseMetaData.getPrimaryKeys默认按照主键名字首字母排序的,并不是按照主键顺序排序。@JFinal

埋头苦干

2017-02-15 22:56

@JFinal MysqlJDBC 中 DatabaseMetaData 中getPrimaryKeys() 并非是按照首字母排序,而是取决于TreeMap、

小林123

2017-02-16 08:46

@JFinal 大叔啊啊啊啊啊啊啊啊啊啊

JFinal

2017-02-16 11:27

@埋头苦干 通过创建 MyMetaBuilder extends MytaBuilder 来扩展出自己需要的多主键次序规则来使用

埋头苦干

2017-02-16 11:32

@JFinal 没用,Mysql JDBC已经被限制了。您再看下我的问题就知道了。

JFinal

2017-02-16 11:33

@埋头苦干 我是指在 MyMetaBuilder extends MetaBuilder 这个扩展类中,在得到复合主键的时候自己再使用一个特定的规则来处理,例如自己给排个序

埋头苦干

2017-02-16 14:54

@JFinal 针对新的model是可以的。但是已经有十几个类的复合主键的顺序错乱了。只能这样改一改了。以后可不能随便切换数据库了。
问题解决了,谢谢了。

JFinal

2017-02-16 14:55

@埋头苦干 最终是采用什么方案解决的,是强制再指定一次,还是通过扩展 MetaBuilder 解决的?

埋头苦干

2017-02-16 16:47

@JFinal
为了保持一致性,我们以数据库主键顺序为准。

而MysqlJDBC 中 DatabaseMetaData 中getPrimaryKeys() 存取数据是用的TreeMap,而TreeMap如不指定排序器,默认将按照key值进行升序排序,不按照主键顺序排序,所以导致主键顺序错误!

于是,改mysql驱动源码中的DatabaseMetaData 类,然后用LinkedHashMap!

JFinal

2017-02-16 17:38

@埋头苦干 这个方案并不彻底,例如:将建表语句中指定的双主键次序换一下然后建这张表,就有问题了

埋头苦干

2017-02-16 19:24

@JFinal 总会有些取舍的。
1.
假如我在
_MappingKit.mapping(arp);
后面再
arp.setPrimaryKey("table", "id1, id2");
那就要把N个变动的表都写设置一次。
2.
创建自己的MytaBuilder ,自己实现排序。此方法还是依赖JDBC。既然JDCB提供的getPrimaryKeys有问题,那不如就直接改它。
3.
至于这个建表语句被改,这就属于极端了。导出的表结构谁会没事改动顺序?而且我们开发人员写sql的时候也会看表结构,按表别结构中主键的顺序去写findById的参数,Generator 只需要按照顺序生成即可。

JFinal

2017-02-16 19:29

@埋头苦干 自己扩展 MetaBuilder,使用固定的排序规则,不会再依赖 JDBC,与 JDBC 没有关系

例如,在 MetaBuilder 扩展类中,使用字典排序对复合主键进行排序,这个排序的结果是永远固定的

埋头苦干

2017-02-16 20:20

@JFinal 噢~~,也对。可我相信大多数人第一印象就是,我又该怎么去排序?而且有了历史代码,有了历史数据库类型,历史表结构。
我习惯在用findById的时候直接去看数据库里面的主键 顺序,而不是在返回到config mapping里面看。(不知道其他人)