初次提交

This commit is contained in:
杨黄林
2022-02-01 11:45:53 +08:00
parent f062134ba3
commit fa44f4427d
16 changed files with 1554 additions and 2 deletions

View File

@@ -0,0 +1,115 @@
package com.cdhncy.seq.config;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
/**
* 生成器配置
*
* @author yanghuanglin
* @since 2022/1/28
*/
public class GeneratorConfig {
/**
* 数据源
*/
private DataSource dataSource;
/**
* 数据库操作模板
*/
private JdbcTemplate jdbcTemplate;
/**
* 事务处理模板
*/
private TransactionTemplate transactionTemplate;
/**
* 事务管理器
*/
private DataSourceTransactionManager transactionManager;
/**
* 自动创建表
*/
private Boolean autoCreate = true;
/**
* 序号每次增加的步长
*/
private Integer step = 1;
/**
* 表和字段配置
*/
private TableConfig tableConfig = new TableConfig();
public GeneratorConfig() {
}
public GeneratorConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public TransactionTemplate getTransactionTemplate() {
return transactionTemplate;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public DataSourceTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(DataSourceTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Boolean getAutoCreate() {
return autoCreate;
}
public void setAutoCreate(Boolean autoCreate) {
this.autoCreate = autoCreate;
}
public Integer getStep() {
return step;
}
public void setStep(Integer step) {
if (step == 0)
step = 1;
this.step = step;
}
public TableConfig getTableConfig() {
return tableConfig;
}
public void setTableConfig(TableConfig tableConfig) {
this.tableConfig = tableConfig;
}
}

View File

@@ -0,0 +1,74 @@
package com.cdhncy.seq.config;
/**
* 生成器对应的数据库表和字段配置
*
* @author yanghuanglin
* @since 2022/1/30
*/
public class TableConfig {
/**
* 当前序号表名闲置序号表会在该名称后增加后缀_unused,未锁定序号表会在该名称后增加unlock
*/
private String table = "sequences";
/**
* 序号英文名称,和序号类型组成唯一组件
*/
private String keyColumn = "key";
/**
* 序号类型
*/
private String typeColumn = "type";
/**
* 序号值
*/
private String seqColumn = "seq";
/**
* 未锁定序号使用时间
*/
private String createTimeColumn = "create_time";
public String getTable() {
return table;
}
public void setTable(String table) {
this.table = table.toLowerCase();
}
public String getKeyColumn() {
return keyColumn;
}
public void setKeyColumn(String keyColumn) {
this.keyColumn = keyColumn.toLowerCase();
}
public String getTypeColumn() {
return typeColumn;
}
public void setTypeColumn(String typeColumn) {
this.typeColumn = typeColumn.toLowerCase();
}
public String getSeqColumn() {
return seqColumn;
}
public void setSeqColumn(String seqColumn) {
this.seqColumn = seqColumn.toLowerCase();
}
public String getCreateTimeColumn() {
return createTimeColumn;
}
public void setCreateTimeColumn(String createTimeColumn) {
this.createTimeColumn = createTimeColumn.toLowerCase();
}
}

View File

@@ -0,0 +1,26 @@
package com.cdhncy.seq.dao;
import com.cdhncy.seq.po.Sequences;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public interface SequencesDao {
/**
* 查找最后被使用的序号
*/
Sequences find(Sequences sequences);
/**
* 保存新生成的序号
*/
boolean save(Sequences sequences);
/**
* 更新被使用的序号
*/
boolean update(Sequences sequences);
void createTable();
}

View File

@@ -0,0 +1,44 @@
package com.cdhncy.seq.dao;
import com.cdhncy.seq.po.SequencesUnlock;
import java.util.Date;
import java.util.List;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public interface SequencesUnlockDao {
/**
* 保存使用中的序号
*/
boolean save(SequencesUnlock sequencesUnlock);
/**
* 删除使用中的序号
*/
boolean delete(SequencesUnlock sequencesUnlock);
/**
* 列出所有使用中的序号
*/
List<SequencesUnlock> listAll();
/**
* 列出指定时间段内使用中的序号
*/
List<SequencesUnlock> listByDate(Date begin, Date end);
/**
* 删除所有使用中的序号
*/
boolean deleteAll();
/**
* 删除指定时间段内使用中的序号
*/
boolean deleteByDate(Date begin, Date end);
void createTable();
}

View File

@@ -0,0 +1,34 @@
package com.cdhncy.seq.dao;
import com.cdhncy.seq.po.SequencesUnused;
import java.util.List;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public interface SequencesUnusedDao {
/**
* 根据keytype查找seq最小的空闲序号
*/
SequencesUnused findMinSeq(SequencesUnused sequencesUnused);
/**
* 根据keytype查找seq最大的空闲序号
*/
SequencesUnused findMaxSeq(SequencesUnused sequencesUnused);
/**
* 根据keytypeseq删除空闲序号
*/
boolean delete(SequencesUnused sequencesUnused);
/**
* 批量保存空闲序号
*/
boolean saveBatch(List<SequencesUnused> sequencesUnusedList);
void createTable();
}

View File

@@ -0,0 +1,72 @@
package com.cdhncy.seq.dao.impl;
import com.cdhncy.seq.config.TableConfig;
import com.cdhncy.seq.dao.SequencesDao;
import com.cdhncy.seq.po.Sequences;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public class SequencesDaoImpl implements SequencesDao {
private final JdbcTemplate jdbcTemplate;
private final TableConfig tableConfig;
public SequencesDaoImpl(JdbcTemplate jdbcTemplate, TableConfig tableConfig) {
this.jdbcTemplate = jdbcTemplate;
this.tableConfig = tableConfig;
}
@Override
public Sequences find(Sequences sequences) {
String sql = "select * from `%s` where `%s`=? and `%s`=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn());
try {
return this.jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
Sequences result = new Sequences();
result.setKey(rs.getString(tableConfig.getKeyColumn()));
result.setType(rs.getString(tableConfig.getTypeColumn()));
result.setSeq(rs.getLong(tableConfig.getSeqColumn()));
return result;
}, sequences.getKey(), sequences.getType());
} catch (EmptyResultDataAccessException ignored) {
return null;
}
}
@Override
public boolean save(Sequences sequences) {
String sql = "insert into `%s`(`%s`,`%s`,`%s`) values(?,?,?)";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
int result = this.jdbcTemplate.update(sql, sequences.getKey(), sequences.getType(), sequences.getSeq());
return result != 0;
}
@Override
public boolean update(Sequences sequences) {
String sql = "update `%s` set `%s`=? where `%s`=? and `%s`=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getSeqColumn(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn());
int result = this.jdbcTemplate.update(sql, sequences.getSeq(), sequences.getKey(), sequences.getType());
return result != 0;
}
@Override
public void createTable() {
String sql = "CREATE TABLE IF NOT EXISTS `%s` ( " +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号英文名称'," +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号类型'," +
" `%s` BIGINT ( 2 ) NOT NULL COMMENT '已使用到的序号'," +
" PRIMARY KEY ( `%s`, `%s` ) " +
" ) COMMENT '当前序号表'";
sql = String.format(sql, tableConfig.getTable(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn(),
tableConfig.getSeqColumn(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn());
this.jdbcTemplate.execute(sql);
}
}

View File

@@ -0,0 +1,101 @@
package com.cdhncy.seq.dao.impl;
import com.cdhncy.seq.config.TableConfig;
import com.cdhncy.seq.dao.SequencesUnlockDao;
import com.cdhncy.seq.po.SequencesUnlock;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.Date;
import java.util.List;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public class SequencesUnlockDaoImpl implements SequencesUnlockDao {
private final JdbcTemplate jdbcTemplate;
private final TableConfig tableConfig;
public SequencesUnlockDaoImpl(JdbcTemplate jdbcTemplate, TableConfig tableConfig) {
this.jdbcTemplate = jdbcTemplate;
this.tableConfig = tableConfig;
}
@Override
public boolean save(SequencesUnlock sequencesUnlock) {
String sql = "insert into `%s_unlock`(`%s`,`%s`,`%s`,`%s`) values(?,?,?,?)";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn(), tableConfig.getCreateTimeColumn());
int result = this.jdbcTemplate.update(sql, sequencesUnlock.getKey(), sequencesUnlock.getType(), sequencesUnlock.getSeq(), sequencesUnlock.getCreateTime());
return result != 0;
}
@Override
public boolean delete(SequencesUnlock sequencesUnlock) {
String sql = "delete from `%s_unlock` where `%s`=? and `%s`=? and `%s`=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
int result = this.jdbcTemplate.update(sql, sequencesUnlock.getKey(), sequencesUnlock.getType(), sequencesUnlock.getSeq());
return result != 0;
}
@Override
public List<SequencesUnlock> listAll() {
String sql = "select * from `%s_unlock`";
sql = String.format(sql, tableConfig.getTable());
return this.jdbcTemplate.query(sql, rowMapper());
}
@Override
public List<SequencesUnlock> listByDate(Date begin, Date end) {
String sql = "select * from `%s_unlock` where `%s`>=? and `%s`<=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getCreateTimeColumn(), tableConfig.getCreateTimeColumn());
return this.jdbcTemplate.query(sql, rowMapper(), begin, end);
}
@Override
public boolean deleteAll() {
String sql = "delete from `%s_unlock`";
sql = String.format(sql, tableConfig.getTable());
int result = this.jdbcTemplate.update(sql);
return result != 0;
}
@Override
public boolean deleteByDate(Date begin, Date end) {
String sql = "delete from `%s_unlock` where `%s`>=? and `%s`<=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getCreateTimeColumn(), tableConfig.getCreateTimeColumn());
int result = this.jdbcTemplate.update(sql, begin, end);
return result != 0;
}
@Override
public void createTable() {
String sql = "CREATE TABLE IF NOT EXISTS `%s_unlock` ( " +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号英文名称'," +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号类型'," +
" `%s` BIGINT ( 2 ) NOT NULL COMMENT '尚未锁定的序号'," +
" `%s` DATETIME NOT NULL COMMENT '使用时间'," +
" PRIMARY KEY ( `%s`, `%s` ,`%s` ) " +
" ) COMMENT '未锁定序号表'";
sql = String.format(sql, tableConfig.getTable(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn(),
tableConfig.getSeqColumn(),
tableConfig.getCreateTimeColumn(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn(),
tableConfig.getSeqColumn());
this.jdbcTemplate.execute(sql);
}
private RowMapper<SequencesUnlock> rowMapper() {
return (rs, rowNum) -> {
SequencesUnlock sequencesUnlock = new SequencesUnlock();
sequencesUnlock.setKey(rs.getString(tableConfig.getKeyColumn()));
sequencesUnlock.setType(rs.getString(tableConfig.getTypeColumn()));
sequencesUnlock.setSeq(rs.getLong(tableConfig.getSeqColumn()));
sequencesUnlock.setCreateTime(rs.getDate(tableConfig.getCreateTimeColumn()));
return sequencesUnlock;
};
}
}

View File

@@ -0,0 +1,106 @@
package com.cdhncy.seq.dao.impl;
import com.cdhncy.seq.config.TableConfig;
import com.cdhncy.seq.dao.SequencesUnusedDao;
import com.cdhncy.seq.po.SequencesUnused;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
/**
* @author yanghuanglin
* @since 2022/1/28
*/
public class SequencesUnusedDaoImpl implements SequencesUnusedDao {
private final JdbcTemplate jdbcTemplate;
private final TableConfig tableConfig;
public SequencesUnusedDaoImpl(JdbcTemplate jdbcTemplate, TableConfig tableConfig) {
this.jdbcTemplate = jdbcTemplate;
this.tableConfig = tableConfig;
}
@Override
public SequencesUnused findMinSeq(SequencesUnused sequencesUnused) {
String sql = "select * from `%s_ununsed` where `%s`=? and `%s`=? order by `%s` asc limit 0,1";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
try {
return this.jdbcTemplate.queryForObject(sql, rowMapper(), sequencesUnused.getKey(), sequencesUnused.getType());
} catch (EmptyResultDataAccessException ignored) {
return null;
}
}
@Override
public SequencesUnused findMaxSeq(SequencesUnused sequencesUnused) {
try {
String sql = "select * from `%s_ununsed` where `%s`=? and `%s`=? order by `%s` desc limit 0,1";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
return this.jdbcTemplate.queryForObject(sql, rowMapper(), sequencesUnused.getKey(), sequencesUnused.getType());
} catch (EmptyResultDataAccessException ignored) {
return null;
}
}
@Override
public boolean delete(SequencesUnused sequencesUnused) {
String sql = "delete from `%s_ununsed` where `%s`=? and `%s`=? and `%s`=?";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
int result = this.jdbcTemplate.update(sql, sequencesUnused.getKey(), sequencesUnused.getType(), sequencesUnused.getSeq());
return result != 0;
}
@Override
public boolean saveBatch(List<SequencesUnused> sequencesUnusedList) {
String sql = "insert into `%s_ununsed`(`%s`,`%s`,`%s`) values(?,?,?)";
sql = String.format(sql, tableConfig.getTable(), tableConfig.getKeyColumn(), tableConfig.getTypeColumn(), tableConfig.getSeqColumn());
int[] result = this.jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
SequencesUnused sequencesUnused = sequencesUnusedList.get(i);
ps.setString(1, sequencesUnused.getKey());
ps.setString(2, sequencesUnused.getType());
ps.setLong(3, sequencesUnused.getSeq());
}
@Override
public int getBatchSize() {
return sequencesUnusedList.size();
}
});
return result.length != 0;
}
@Override
public void createTable() {
String sql = "CREATE TABLE IF NOT EXISTS `%s_ununsed` ( " +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号英文名称'," +
" `%s` VARCHAR ( 255 ) NOT NULL COMMENT '序号类型'," +
" `%s` BIGINT ( 2 ) NOT NULL COMMENT '闲置的的序号'," +
" PRIMARY KEY ( `%s`, `%s`, `%s` ) " +
" ) COMMENT '闲置序号表'";
sql = String.format(sql, tableConfig.getTable(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn(),
tableConfig.getSeqColumn(),
tableConfig.getKeyColumn(),
tableConfig.getTypeColumn(),
tableConfig.getSeqColumn());
this.jdbcTemplate.execute(sql);
}
private RowMapper<SequencesUnused> rowMapper() {
return (rs, rowNum) -> {
SequencesUnused result = new SequencesUnused();
result.setKey(rs.getString(tableConfig.getKeyColumn()));
result.setType(rs.getString(tableConfig.getTypeColumn()));
result.setSeq(rs.getLong(tableConfig.getSeqColumn()));
return result;
};
}
}

View File

@@ -0,0 +1,114 @@
package com.cdhncy.seq.generator;
import com.cdhncy.seq.po.Sequences;
import com.cdhncy.seq.po.SequencesUnlock;
import com.cdhncy.seq.po.SequencesUnused;
import java.util.Date;
public interface Generator {
/**
* 序号格式字符中的年
*/
String YEAR = "#year#";
/**
* 序号格式字符中的月
*/
String MONTH = "#month#";
/**
* 序号格式字符中的日
*/
String DAY = "#day#";
/**
* 序号格式字符中的格式化后的序号
*/
String SEQ = "#seq#";
/**
* 根据传入的key和type生成可用的序号对象。
* <p/>
* 如果根据key和type在{@link Sequences}中找不到记录说明该组合的序号对象还未初次生成返回的是seq为step的序号对象该对象数据会写入到{@link SequencesUnlock}中。
* <p/>
* 如果根据key和type在{@link Sequences}中找到了记录,且在{@link SequencesUnused}也找到了记录,说明该组合生成的序号有部分未使用,返回的是{@link SequencesUnused}中找到的seq最小的序号对象。同时会将{@link SequencesUnused}中找到的seq最小的记录删除然后写入到{@link SequencesUnlock}中。
* <p/>
*
* @param key 数据字典中的编码
* @param type 序号类型
* @return 可用的序号对象
*/
Sequences generate(String key, String type);
/**
* 返回根据{@link #generate(String, String)}得到的序号对象,补零后的序号字符串
* <p/>
* 如生成的为3而minLength为5则返回的是00003
*
* @param key 数据字典中的编码
* @param type 序号类型
* @param minLength 序号数字最小长度
* @return 补零后的字符串
*/
String generate(String key, String type, Integer minLength);
/**
* 将生成的序号对象格式化为指定格式
* <p/>
* pattern支持{@link #YEAR}(当前年份)、{@link #MONTH}(当前月份)、{@link #DAY}(当前日期)、{@link #SEQ}(生成的字符串序号)四个变量
* <p/>
* seq为1minLength为4pattern为#year##month##day#6#seq#则会格式化为2022013060001。此序号含义如下
* <p/>
* 序号格式:[年][月][日][固定6开头][序号1最小位数为4位不足4位则补零]
*
* @param seq 需要格式化的序号
* @param minLength 序号最小长度,不足的会补零
* @param pattern 格式
* @return 格式化后的字符串
*/
String format(Long seq, Integer minLength, String pattern);
/**
* 将生成的序号对象格式化为指定格式
* <p/>
* pattern支持{@link #YEAR}(当前年份)、{@link #MONTH}(当前月份)、{@link #DAY}(当前日期)、{@link #SEQ}(生成的字符串序号)四个变量
* <p/>
* seq为1,start为6minLength为4pattern为#year##month##day##seq#则会格式化为2022013060001。此序号含义如下
* <p/>
* 序号格式:[年][月][日][固定6开头][序号1最小位数为4位不足4位则补零]
*
* @param seq 需要格式化的序号
* @param start 序号格式化后以什么字符串开头
* @param minLength 序号最小长度,不足的会补零
* @param pattern 格式
* @return 格式化后的字符串
*/
String format(Long seq, String start, Integer minLength, String pattern);
/**
* 锁定指定序号,在序号生成后,调用该序号的逻辑完成后需要执行此方法
* <p/>
* 如办理案件时,先调用{@link #generate(String, String)}或者{@link #generate(String, String, Integer)}生成了序号,之后对案件进行了入库,如果入库完毕,则将该序号锁定,说明这个序号已被使用
* <p/>
* 注意,此处的锁定非数据库中锁定,而是{@link SequencesUnused}和{@link SequencesUnlock}中均不存在key、type、seq相同的记录视为锁定。因此此处实际是把这两个表中的记录均删除了
*
* @param sequences 需要锁定的序号
* @return 锁定结果
*/
boolean lock(Sequences sequences);
/**
* 释放所有未使用的序号
* <p/>
* {@link SequencesUnlock}中未通过{@link #lock(Sequences)}方法锁定的序号会一直存在,调用此方法会将里面的所有序号都移动到{@link SequencesUnused}中,下次生成序号时优先从{@link SequencesUnused}获取。
*/
void release();
/**
* 释放指定时间段内未使用的序号
* <p/>
* {@link SequencesUnlock}中未通过{@link #lock(Sequences)}方法锁定的序号会一直存在,调用此方法会将里面的所有序号都移动到{@link SequencesUnused}中,下次生成序号时优先从{@link SequencesUnused}获取。
*
* @param begin 开始时间
* @param end 结束时间
*/
void release(Date begin, Date end);
}

View File

@@ -0,0 +1,188 @@
package com.cdhncy.seq.generator.impl;
import com.cdhncy.seq.config.GeneratorConfig;
import com.cdhncy.seq.dao.SequencesDao;
import com.cdhncy.seq.dao.SequencesUnusedDao;
import com.cdhncy.seq.dao.SequencesUnlockDao;
import com.cdhncy.seq.dao.impl.SequencesDaoImpl;
import com.cdhncy.seq.dao.impl.SequencesUnusedDaoImpl;
import com.cdhncy.seq.dao.impl.SequencesUnlockDaoImpl;
import com.cdhncy.seq.po.Sequences;
import com.cdhncy.seq.po.SequencesUnused;
import com.cdhncy.seq.po.SequencesUnlock;
import com.cdhncy.seq.generator.Generator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.DataSource;
import java.util.*;
public class SequencesGenerator implements Generator {
private final TransactionTemplate transactionTemplate;
private final SequencesDao sequencesDao;
private final SequencesUnusedDao sequencesUnusedDao;
private final SequencesUnlockDao sequencesUnlockDao;
private final Integer step;
public SequencesGenerator(GeneratorConfig generatorConfig) {
//数据库操作模板
JdbcTemplate jdbcTemplate = generatorConfig.getJdbcTemplate();
if (jdbcTemplate == null) {
//数据源
DataSource dataSource = generatorConfig.getDataSource();
if (dataSource == null)
//若数据库操作模板为空,也没有配置数据源,则抛出异常
throw new NullPointerException("数据源不能为空");
//否则以数据源创建数据库操作模板
jdbcTemplate = new JdbcTemplate(dataSource);
}
if (generatorConfig.getTransactionTemplate() == null) {
//若没有配置事务操作模板,则从配置中取事务管理器
DataSourceTransactionManager transactionManager = generatorConfig.getTransactionManager();
if (transactionManager == null) {
//若未配置事务管理器,则通过数据源新建
DataSource dataSource = jdbcTemplate.getDataSource();
if (dataSource == null)
throw new NullPointerException("数据源不能为空");
transactionManager = new JdbcTransactionManager(dataSource);
}
//通过事务管理器创建事务操作模板
transactionTemplate = new TransactionTemplate(transactionManager);
} else {
//获取事务操作模板
transactionTemplate = generatorConfig.getTransactionTemplate();
}
this.sequencesDao = new SequencesDaoImpl(jdbcTemplate, generatorConfig.getTableConfig());
this.sequencesUnusedDao = new SequencesUnusedDaoImpl(jdbcTemplate, generatorConfig.getTableConfig());
this.sequencesUnlockDao = new SequencesUnlockDaoImpl(jdbcTemplate, generatorConfig.getTableConfig());
this.step = generatorConfig.getStep();
autoCreateTable(generatorConfig.getAutoCreate());
}
/**
* 自动创建需要的表
*/
private void autoCreateTable(Boolean autoCreate) {
if (!autoCreate)
return;
this.sequencesDao.createTable();
this.sequencesUnusedDao.createTable();
this.sequencesUnlockDao.createTable();
}
@Override
public synchronized Sequences generate(String key, String type) {
return transactionTemplate.execute(status -> {
try {
//根据传入的key和type新生成查询条件对象
Sequences condition = new Sequences(key, type);
//找到正在使用的最大序号
Sequences sequences = sequencesDao.find(condition);
if (sequences == null) {
//不存在说明还没生成将新生成的入库此时序号为默认的0
sequences = condition;
sequencesDao.save(sequences);
}
//根据传入的key和type查找空闲编号最小的一个
SequencesUnused conditionIdle = new SequencesUnused(condition);
SequencesUnused sequencesUnused = sequencesUnusedDao.findMinSeq(conditionIdle);
if (sequencesUnused == null) {
//空闲编号不存在,说明是未生成过,序号需要增加后直接使用,同时将新生成的写入到使用中表
sequences.increase(step);
SequencesUnlock sequencesUnlock = new SequencesUnlock(sequences);
sequencesUnlock.setCreateTime(new Date());
sequencesDao.update(sequences);
sequencesUnlockDao.save(sequencesUnlock);
} else {
//空闲编号存在,说明已经生成过,序号不需要增加,直接使用。同时将该空闲编号移动到使用中表
sequences = new Sequences(sequencesUnused);
SequencesUnlock sequencesUnlock = new SequencesUnlock(sequencesUnused);
sequencesUnlock.setCreateTime(new Date());
sequencesUnlockDao.save(sequencesUnlock);
sequencesUnusedDao.delete(sequencesUnused);
}
return sequences;
} catch (Exception e) {
e.printStackTrace();
status.setRollbackOnly();
return null;
}
});
}
@Override
public synchronized String generate(String key, String type, Integer minLength) {
Sequences sequences = this.generate(key, type);
if (sequences == null)
return null;
return sequences.format(minLength);
}
@Override
public String format(Long seq, Integer minLength, String pattern) {
return format(seq, null, minLength, pattern);
}
@Override
public String format(Long seq, String start, Integer minLength, String pattern) {
if (start == null)
start = "";
String seqString = start + new Sequences(seq).format(minLength);
Calendar calendar = Calendar.getInstance();
pattern = pattern.replaceAll(Generator.YEAR, String.valueOf(calendar.get(Calendar.YEAR)));
pattern = pattern.replaceAll(Generator.MONTH, String.format("%02d", calendar.get(Calendar.MONTH) + 1));
pattern = pattern.replaceAll(Generator.DAY, String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)));
pattern = pattern.replaceAll(Generator.SEQ, seqString);
return pattern;
}
@Override
public synchronized boolean lock(Sequences sequences) {
SequencesUnlock condition = new SequencesUnlock(sequences);
//将使用中表的对应数据删除,空闲表中数据在生成时会删除,因此这里不需要处理该表
return sequencesUnlockDao.delete(condition);
}
@Override
public void release() {
//列出所有使用中表存在的序号
List<SequencesUnlock> sequencesUnlockList = sequencesUnlockDao.listAll();
List<SequencesUnused> sequencesUnusedList = new ArrayList<>();
for (SequencesUnlock sequencesUnlock : sequencesUnlockList) {
sequencesUnusedList.add(new SequencesUnused(sequencesUnlock));
}
//将使用中表的序号放到空闲表中
sequencesUnusedDao.saveBatch(sequencesUnusedList);
//删除所有使用中表的数据
sequencesUnlockDao.deleteAll();
}
@Override
public void release(Date begin, Date end) {
//列出指定时间段内使用中表存在的序号
List<SequencesUnlock> sequencesUnlockList = sequencesUnlockDao.listByDate(begin, end);
List<SequencesUnused> sequencesUnusedList = new ArrayList<>();
for (SequencesUnlock sequencesUnlock : sequencesUnlockList) {
sequencesUnusedList.add(new SequencesUnused(sequencesUnlock));
}
//将指定时间段内使用中表的序号放到空闲表中
sequencesUnusedDao.saveBatch(sequencesUnusedList);
//删除指定时间段内使用中表的数据
sequencesUnlockDao.deleteByDate(begin, end);
}
}

View File

@@ -0,0 +1,88 @@
package com.cdhncy.seq.po;
/**
* 当前序号
*
* @author yanghuanglin
* @since 2022/1/28
*/
public class Sequences {
/**
* 需要生成序号的key和type组成唯一主键
*/
protected String key;
/**
* 需要生成序号的类型和key组成唯一主键
*/
protected String type;
/**
* 默认序号
*/
protected Long seq = 0L;
public Sequences() {
}
public Sequences(Long seq) {
this.seq = seq;
}
public Sequences(String key, String type) {
this.key = key;
this.type = type;
}
public Sequences(SequencesUnused sequencesUnused) {
this.key = sequencesUnused.getKey();
this.type = sequencesUnused.getType();
this.seq = sequencesUnused.getSeq();
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Long getSeq() {
return seq;
}
public void setSeq(Long seq) {
this.seq = seq;
}
/**
* 将序号增加指定步长
*
* @param step 步长
*/
public void increase(Integer step) {
this.seq += step;
}
/**
* 序号补零
*
* @param minLength 最小长度,低于此长度,会填充零
* @return 补零后的序号
*/
public String format(Integer minLength) {
if (minLength != null)
return String.format("%0" + minLength + "d", this.seq);
return String.valueOf(this.seq);
}
}

View File

@@ -0,0 +1,37 @@
package com.cdhncy.seq.po;
import java.util.Date;
/**
* 未锁定序号
*
* @author yanghuanglin
* @since 2022/1/28
*/
public class SequencesUnlock extends Sequences {
private Date createTime;
public SequencesUnlock() {
}
public SequencesUnlock(Sequences sequences) {
this.key = sequences.getKey();
this.type = sequences.getType();
this.seq = sequences.getSeq();
}
public SequencesUnlock(SequencesUnused sequencesUnused) {
this.key = sequencesUnused.getKey();
this.type = sequencesUnused.getType();
this.seq = sequencesUnused.getSeq();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

View File

@@ -0,0 +1,19 @@
package com.cdhncy.seq.po;
/**
* 闲置序号
*
* @author yanghuanglin
* @since 2022/1/28
*/
public class SequencesUnused extends Sequences {
public SequencesUnused() {
}
public SequencesUnused(Sequences sequences) {
this.key = sequences.getKey();
this.type = sequences.getType();
this.seq = sequences.getSeq();
}
}