DB2 创建并使用SEQUENCE
1. 创建SEQUENCE
CREATE SEQUENCE "SCHEMA"."SEQ_USER_ID" AS BIGINT START WITH 1 INCREMENT BY 1
NO MINVALUE NO MAXVALUE NO CYCLE CACHE 20 NO ORDER
2. 使用SEQUENCE
User表结构如下:
CREATE TABLE USER (
USER_ID BIGINT NOT NULL,
USER_NAME CHARACTER(30),
PRIMARY KEY (USER_ID)
)
获取SEQUENCE的值:
-- method1: NEXTVAL FOR schema.sequence_name
INSERT INTO SCHEMA.USER(USER_ID, USER_NAME) VALUES(NEXTVAL FOR SCHEMA.SEQ_USER_ID, 'username1')
-- method2: query sql, 直接执行这个SQL可以获取到SEQUENCE值
VALUES NEXTVAL FOR schema.sequence_name
3. Spring对DB2 SEQUENCE的支持
在Spring配置文件中添加如下配置:
<!--
1. 引用datasource
2. 设置SEQUENCE名称
3. 如果需要字符串形式的SEQUENCE值,则可以在左侧填充'0'
-->
<bean id="userIdSequence" class="org.springframework.jdbc.support.incrementer.DB2SequenceMaxValueIncrementer"
p:dataSource-ref="dataSource" p:incrementerName="SEQ_USER_ID" p:paddingLength="10" />
<!-- 注意:incrementerName可能需要写成SCHEMA_NAME.SEQ_NAME的形式 -->
Java代码中调用:
@Autowired
private DB2SequenceMaxValueIncrementer userIdSequence;
public void xxx() {
System.out.println(userIdSequence.nextIntValue()); // int 类型 1
System.out.println(userIdSequence.nextLongValue());// long类型 2
System.out.println(userIdSequence.nextStringValue());// string类型 "0000000003"
}
4. 扩展 DB2SequenceMaxValueIncrementer源码解析
Spring JDBC对SEQUENCE的支持:
DB2SequenceMaxValueIncrementer的继承及实现关系如下:DB2SequenceMaxValueIncrementer extends AbstractSequenceMaxValueIncrementer extends AbstractDataFieldMaxValueIncrementer implement DataFieldMaxValueIncrementer, InitializingBean
4.1 DataFieldMaxValueIncrementer接口
该接口用于获取某个字段的自增最大值(比如使用标准SQL实现,使用RDBMS中的SEQUENCE实现或者使用存储过程来实现),其中定义了如下3个方法:nextIntValue(), nextLongValue() 和nextStringValue()。
package org.springframework.jdbc.support.incrementer;
import org.springframework.dao.DataAccessException;
/**
* Interface that defines contract of incrementing any data store field's
* maximum value. Works much like a sequence number generator.
*
* <p>Typical implementations may use standard SQL, native RDBMS sequences
* or Stored Procedures to do the job.
*
*/
public interface DataFieldMaxValueIncrementer {
/**
* Increment the data store field's max value as int.
* @return int next data store value such as <b>max + 1</b>
* @throws org.springframework.dao.DataAccessException in case of errors
*/
int nextIntValue() throws DataAccessException;
/**
* Increment the data store field's max value as long.
* @return int next data store value such as <b>max + 1</b>
* @throws org.springframework.dao.DataAccessException in case of errors
*/
long nextLongValue() throws DataAccessException;
/**
* Increment the data store field's max value as String.
* @return next data store value such as <b>max + 1</b>
* @throws org.springframework.dao.DataAccessException in case of errors
*/
String nextStringValue() throws DataAccessException;
}
4.2 AbstractDataFieldMaxValueIncrementer抽象类
该抽象类作为DataFieldMaxValueIncrementer接口的基本实现,把具体实现委派到抽象的getNextKey()方法上。源码如下:
package org.springframework.jdbc.support.incrementer;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
/**
* Base implementation of {@link DataFieldMaxValueIncrementer} that delegates
* to a single {@link #getNextKey} template method that returns a {@code long}.
* Uses longs for String values, padding with zeroes if required.
*/
public abstract class AbstractDataFieldMaxValueIncrementer implements DataFieldMaxValueIncrementer, InitializingBean {
private DataSource dataSource;
/** The name of the sequence/table containing the sequence */
private String incrementerName;
/** The length to which a string result should be pre-pended with zeroes */
protected int paddingLength = 0;
// 构造方法
public AbstractDataFieldMaxValueIncrementer() {
}
public AbstractDataFieldMaxValueIncrementer(DataSource dataSource, String incrementerName) {
Assert.notNull(dataSource, "DataSource must not be null");
Assert.notNull(incrementerName, "Incrementer name must not be null");
this.dataSource = dataSource;
this.incrementerName = incrementerName;
}
// 获取下一个值必须要要有dataSource和incrementerName
@Override
public void afterPropertiesSet() {
if (this.dataSource == null) {
throw new IllegalArgumentException("Property 'dataSource' is required");
}
if (this.incrementerName == null) {
throw new IllegalArgumentException("Property 'incrementerName' is required");
}
}
// 获取int值(long值强转为int值)
@Override
public int nextIntValue() throws DataAccessException {
return (int) getNextKey();
}
// 获取long值
@Override
public long nextLongValue() throws DataAccessException {
return getNextKey();
}
// 获取String值,为String值的左侧填充'0'直到等于paddingLength
@Override
public String nextStringValue() throws DataAccessException {
String s = Long.toString(getNextKey());
int len = s.length();
if (len < this.paddingLength) {
StringBuilder sb = new StringBuilder(this.paddingLength);
for (int i = 0; i < this.paddingLength - len; i++) {
sb.append('0');
}
sb.append(s);
s = sb.toString();
}
return s;
}
/**
* Determine the next key to use, as a long.
* @return the key to use as a long. It will eventually be converted later
* in another format by the public concrete methods of this class.
*/
// 最终需要实现的抽象方法,此方法返回一个long类型的值
protected abstract long getNextKey();
}
4.3 AbstractSequenceMaxValueIncrementer抽象类
这个抽象类专门作为使用SEQUENCE作为自增值的情况。
package org.springframework.jdbc.support.incrementer;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
/**
* Abstract base class for {@link DataFieldMaxValueIncrementer} implementations that use
* a database sequence. Subclasses need to provide the database-specific SQL to use.
*/
public abstract class AbstractSequenceMaxValueIncrementer extends AbstractDataFieldMaxValueIncrementer {
// 构造方法
public AbstractSequenceMaxValueIncrementer() {
}
public AbstractSequenceMaxValueIncrementer(DataSource dataSource, String incrementerName) {
super(dataSource, incrementerName);
}
/**
* 执行SQL查询来查询SEQUENCE的下一个值
* Executes the SQL as specified by {@link #getSequenceQuery()}.
*/
@Override
protected long getNextKey() throws DataAccessException {
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
ResultSet rs = null;
try {
stmt = con.createStatement();
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
rs = stmt.executeQuery(getSequenceQuery());
if (rs.next()) {
return rs.getLong(1);
}
else {
throw new DataAccessResourceFailureException("Sequence query did not return a result");
}
}
catch (SQLException ex) {
throw new DataAccessResourceFailureException("Could not obtain sequence value", ex);
}
finally {
JdbcUtils.closeResultSet(rs);
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
/**
* 返回一个用于查询序列值得与特定数据库相关的查询SQL。
* 提供的SQL的查询结果应该只有一行,并且返回值能够被解包为long类型。
* Return the database-specific query to use for retrieving a sequence value.
* <p>The provided SQL is supposed to result in a single row with a single
* column that allows for extracting a {@code long} value.
*/
protected abstract String getSequenceQuery();
}
4.4 DB2SequenceMaxValueIncrementer-DB2序列值的实现类
DB2SequenceMaxValueIncrementer这个类实现了AbstractSequenceMaxValueIncrementer中的getSequenceQuery()方法,从而获取到特定序列序列值。
package org.springframework.jdbc.support.incrementer;
import javax.sql.DataSource;
/**
* {@link DataFieldMaxValueIncrementer} that retrieves the next value of a given sequence
* on DB2 UDB (for Unix and Windows). Thanks to Mark MacMahon for the suggestion!
*
*/
public class DB2SequenceMaxValueIncrementer extends AbstractSequenceMaxValueIncrementer {
// 构造方法
public DB2SequenceMaxValueIncrementer() {
}
public DB2SequenceMaxValueIncrementer(DataSource dataSource, String incrementerName) {
super(dataSource, incrementerName);
}
// DB2查询序列的下一个值的SQL
@Override
protected String getSequenceQuery() {
return "values nextval for " + getIncrementerName();
}
}
所以这里的重点是一条查询序列的SQL:values nextval for 序列名称。
像Oracle的序列,也只需要提供如下方法就可实现:
@Override
protected String getSequenceQuery() {
return "select " + getIncrementerName() + ".nextval from dual";
}
参考
1. [Spring中sequence的使用小结](https://blog.csdn.net/wang0928007/article/details/7175905)