好记性不如烂笔头100-spring3(21)-高并发下的数据库自增主键

在一般情况下,在新增领域对象后,都需要获取对应的主键值。使用应用层来维护主键,在一定程度上有利于程序性能的优化和应用移植性的提高。在采用数据库自增主键的方案里,如果JDBC驱动不能绑定新增记录对应的主键,就需要手工执行查询语句以获取对应的主键值,对于高并发的系统,这很容易返回错误的主键。

如果数据库的并发率很高,比如在插入记录后执行查询主键之前,数据库又执行了若干条插入记录的SQL语句,这时,通过前一个SQL返回的主键值就是最后一条插入语句的主键值,而非我们希望的主键值了。
因此使用查询语句获取表自增键值是不安全的,这也是为什么有些数据库(如Oracle、Firebird)故意不提供自增键,而只提供序列的原因之一,序列强制要求你在新增记录前,先获取主键值。Oracle通过SELECT .nextval FROM DUAL获取序列的下一个值,而FireBird通过SELECT GEN_ID( 1) FROM RDB$DATABASE获取序列的下一个值。
因此在高并发的条件下,如果数据库是ORACLE之类,那么就要用序列获得数据库的自增主键。
如果不是oracle数据库(非序列自增键数据库)的话,那么就要从另外一个角度来考虑。

非序列自增键数据库
不是oracle数据库(非序列自增键数据库)的话,那么使用的方法就有些不一样。
我们经常使用数据的自增字段作为表主键,它不在应用层产生,而是在新增记录时,由数据库产生。这样,应用层在保存对象前并不知道对象主键值,而必须在保存数据后才能从数据库中返回主键值。在很多情况下,我们需要获取新对象持久化后的主键值。在Hibernate等ORM框架,新对象持久化后,Hibernate会自动将主键值绑定到对象上,给程序的开发带来了很多方便。

在JDBC 3.0规范中,当新增记录时,允许将数据库自动产生的主键值绑定到Statement或PreparedStatement中。使用Statement时,可以通过以下方法绑定主键值:
int executeUpdate(String sql,int autoGeneratedKeys)
也可以通过Connection创建绑定自增值的PreparedStatement:
PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)

当autoGeneratedKeys参数设置为Statement.RETURN_GENERATED_KEYS值时即可绑定数据库产生的主键值,设置为Statement.NO_GENERATED_KEYS时,不绑定主键值。
下面的代码演示了Statement绑定并获取数据库产生的主键值的过程:

//获取对应的表自增主键值

Statement stmt = conn.createStatement(); 
String sql = "INSERT INTO t_topic(topic_title,user_id) VALUES(‘测试主题’,’123’) "; 
stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS); 
ResultSet rs = stmt.getGeneratedKeys(); 
if( rs.next() ) { intkey = rs.getInt();}  

Spring利用这一技术,提供了一个可以返回新增记录对应主键值的方法:
int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)
org.springframework.jdbc.support.KeyHolder是一个回调接口,Spring使用它保存新增记录对应的主键,该接口的接口方法描述如下:
?Number getKey() throws InvalidDataAccessApiUsageException
当仅插入一行数据,主键不是复合键且是数字类型时,通过该方法可以直接返回新的主键值。如果是复合主键,或者有多个主键返回时,该方法抛出InvalidDataAccessApiUsageException。该方法是最常用的方法,因为一般情况下,我们一次仅插入一条数据并且主键字段类型为数字类型;

?Map getKeys() throws InvalidDataAccessApiUsageException
如果是复合主键,则列名和列值构成Map中的一个Entry。如果返回的是多个主键,则该方法抛出InvalidDataAccessApiUsageException异常;

?List getKeyList():
如果返回多个主键,即PreparedStatement新增了多条记录,则每一个主键对应一个Map,多个Map构成一个List。

 Spring为KeyHolder接口指代了一个通用的实现类GeneratedKeyHolder,该类返回新增记录时的自增长主键值。 

不同数据库获取新增主键的查询语句 (网上抄的)

DB2  
IDENTITY_VAL_LOCAL()  
Informix  
SELECT dbinfo(‘sqlca.sqlerrd1‘) FROM <TABLE>  
Sybase  
SELECT @@IDENTITY  
SqlServer  
SELECT SCOPE_IDENTITY()或SELECT @@IDENTITY  
MySql  
SELECT LAST_INSERT_ID()  
HsqlDB  
CALL IDENTITY()  
Cloudscape  
IDENTITY_VAL_LOCAL()  
Derby  
IDENTITY_VAL_LOCAL()  
PostgreSQL  
SELECT nextval(‘<TABLE>_SEQ‘)  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。