package jef.database; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Queue; import jef.database.DbMetaData.ObjectType; import jef.database.innerpool.IConnection; import jef.database.support.RDBMS; import jef.database.test.DataSource; import jef.database.test.DataSourceContext; import jef.database.test.DatabaseInit; import jef.database.test.JefJUnit4DatabaseTestRunner; import jef.tools.JefConfiguration; import jef.tools.reflect.BeanUtils; import org.apache.commons.lang.ArrayUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; /** * {@code SequenceKeyHolder}单元测试类 * <p> * 运行前,需要: * <ul> * <li>去掉 @Ignore 注解</li> * <li>根据实际数据库的信息,修改SCHEMA的值、以及方法 init() 中 db = * DbClientFactory.getDbClient(...)这一句中的参数值</li> * </ul> * </p> * * @see SequenceKeyHolder * * @Company Asiainfo-Linkage Technologies (China), Inc. * @author luolp@asiainfo-linkage.com * @Date 2012-10-25 */ @RunWith(JefJUnit4DatabaseTestRunner.class) @DataSourceContext({ @DataSource(name = "oracle", url = "${oracle.url}", user = "${oracle.user}", password = "${oracle.password}") }) public class SequenceKeyHolderTest { private DbClient db; private String SCHEMA; private static final int CACHE_SIZE = JefConfiguration.getInt(DbCfg.SEQUENCE_BATCH_SIZE, 50); private static int SEQ_START; private static int SEQ_STEP; private static String SEQ_NAME; private static String SEQ_CREATE_SQL; private static String SEQ_NEXVAL_SQL; @DatabaseInit public void init() throws SQLException { this.SCHEMA = db.getMetaData(null).getCurrentSchema(); } @Test public void testNext() { if (db == null) return; if (db.getProfile(null).getName() == RDBMS.oracle) { try { // start < step System.out.println("============== testing sequence start < step =============="); SEQ_NAME = "seq_for_step_test1"; SEQ_START = 2; SEQ_STEP = 5; recreateSequence(); getNext(); // start = step System.out.println("============== testing sequence start = step =============="); SEQ_NAME = "seq_for_step_test2"; SEQ_START = 2; SEQ_STEP = 2; recreateSequence(); getNext(); // start > step System.out.println("============== testing sequence start > step =============="); SEQ_NAME = "seq_for_step_test3"; SEQ_START = 10; SEQ_STEP = 5; recreateSequence(); getNext(); } catch (SQLException e) { e.printStackTrace(); Assert.fail(e.getMessage()); } } } @Test public void testCreatingSequenceInLimitedTries() { if (db == null) return; if (db.getProfile(null).getName() == RDBMS.oracle) { // 该sequence名称过长,将触发"ORA-00972: 标识符过长"错误,以此来验证失败时的尝试次数。 String seq = "sys.seq_for_creation_in_limited_times"; int i = 0; do { try { Sequence holder = db.selectTarget(null).getSequence(seq, 12); Assert.assertNotNull(holder); // 获取下一个sequence值时,也需要受到失败次数的约束。 try { holder.next(); } catch (Exception e) { System.out.println(e.getClass().getName() + " " + e.getMessage()); } } catch (SQLException e) { e.printStackTrace(); } } while (i++ < 10); } } private void recreateSequence() throws SQLException { if (db.selectTarget(null).getMetaData().existsInSchema(ObjectType.SEQUENCE, SCHEMA, SEQ_NAME)) { db.getMetaData(null).dropSequence(SEQ_NAME); } SEQ_CREATE_SQL = "create sequence " + SEQ_NAME + " minvalue 1 maxvalue 999999 start with " + SEQ_START + " increment by " + SEQ_STEP + " cache 200"; db.executeSql(SEQ_CREATE_SQL); db.getSequenceManager().clearHolders(); } private void getNext() throws SQLException { Sequence holder = db.selectTarget(null).getSequence(SEQ_NAME, 12); // 首次获取下一个序列值时将通过查询DB获得 long next = getNextSequenceValue(holder); Assert.assertEquals(SEQ_START, next); // 接着,获取下一个序列值CACHE_SIZE次,此时均将直接从cache中获得 for (int i = 1; i < CACHE_SIZE; i++) { next = getNextSequenceValue(holder); Assert.assertEquals(SEQ_START + i, next); } // 再次通过查询DB获取下一个序列值,该值与cache后的一致(即不会出现主键冲突问题) long nextFromDb = getNextSequenceValueFromDb(); System.out.println("next value of ".concat(SEQ_NAME).concat("(from db)=") + nextFromDb); Assert.assertEquals(next + 1, nextFromDb); } @SuppressWarnings("rawtypes") private long getNextSequenceValue(Sequence holder) throws SQLException { long next = holder.next(); System.out.println("next value of ".concat(SEQ_NAME).concat("=") + next); Queue cacheQueue = (Queue) BeanUtils.getFieldValue(holder, "cache"); System.out.println("cache: " + ArrayUtils.toString(cacheQueue.toArray())); return next; } private long getNextSequenceValueFromDb() throws SQLException { IConnection conn = db.getConnection(); System.out.println(SCHEMA); if(SCHEMA==null){ SEQ_NEXVAL_SQL = "select " +SEQ_NAME + ".nextval from dual"; }else{ SEQ_NEXVAL_SQL = "select " + SCHEMA + "." + SEQ_NAME + ".nextval from dual"; } System.out.println(SEQ_NEXVAL_SQL); PreparedStatement ps = conn.prepareStatement(SEQ_NEXVAL_SQL); ResultSet rs = ps.executeQuery(); try { rs.next(); return rs.getLong(1); } finally { rs.close(); ps.close(); db.releaseConnection(conn); } } }