package org.quickbundle.base.beans.factory;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.quickbundle.itf.base.IRmIdFactory;
import org.quickbundle.project.common.service.IRmCommonService;
import org.quickbundle.project.init.RmConfig;
import org.quickbundle.project.multidb.RmMultiDbHolder;
import org.quickbundle.tools.context.RmBeanHelper;
import org.quickbundle.tools.helper.xml.RmXmlHelper;
import org.quickbundle.tools.support.log.RmLogHelper;
import org.quickbundle.tools.support.path.RmPathHelper;
import org.quickbundle.util.RmSequenceMap;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* ID生成器单例
*
* @author 白小勇
* @version 1.0.0
* @see 需要参见的其它类
* @since 1.0.0
*/
public class RmIdFactory implements IRmIdFactory{
//批查询的数量
public static int MAX_BATCH_SIZE = RmConfig.getSingleton().getDefaultBatchSize();
//表名tableName-->ID
private Map<String, AtomicLong> mId = null;
//开发模式下每次都查表获得最新ID,表名tableName --> {sql, tablePrefix}
private Map<String, String[]> mTableName_sql = null;
private File getIdXml() {
File idXml = new File(RmPathHelper.getWebInfDir() + "/config/rm/id.xml");
if(!idXml.exists()) {
idXml = new File(RmPathHelper.getWebInfDir() + "/config/jdbc/id.xml");
}
return idXml;
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public void initBeanFactory() {
try {
long startTime = System.currentTimeMillis();
//初始化单例对象
this.mId = new HashMap<String, AtomicLong>();
//存放table_name -> <table table_code="2003" table_name="RM_AFFIX" id_name="ID"/>
Map<String, Element> mTableName_Ele = new RmSequenceMap<String, Element>();
//id.xml命名空间
Map<String, String> defaultNameSpaceMap = new HashMap<String, String>();
defaultNameSpaceMap.put("q", "http://www.quickbundle.org/schema");
//读入id.xml
Document docId_xml = RmXmlHelper.parse(getIdXml().toString(), defaultNameSpaceMap);
List<Element> lTable = docId_xml.selectNodes("/q:RmIdFactory/q:table");
for(Element eleTable : lTable) {
String tableName = eleTable.valueOf("@table_name").toUpperCase();
mTableName_Ele.put(tableName, eleTable);
//this.mId.put(tableName, new AtomicLong());//放弃对mId初始化。
}
//generateIdFromDb模式下每次都查询的sql
if(RmConfig.isGenerateIdFromDb()) {
this.mTableName_sql = new HashMap<String, String[]>();
}
if(RmConfig.getSingleton().isInitIdBatch()) { //Batch模式下批量查询maxId
doInitIdBatch(mTableName_Ele);
} else {
doInitId(mTableName_Ele);
}
RmLogHelper.getLogger(this.getClass()).info("init " + mTableName_Ele.size() + " tables, cost " + (System.currentTimeMillis()-startTime) + " milliseconds!");
} catch (Exception e) {
e.printStackTrace();
RmLogHelper.getLogger(RmIdFactory.class).error("id.xml初始化失败:" + e.toString());
}
}
//根据 集群+table的前缀和maxId,获取下一个可用值或默认起始值
long getMaxIdOrDefault(String clusterTablePrefix, String maxId) {
long result = 0L;
if(maxId != null && maxId.length() > 0) {
if(maxId.length() > 19) {
result = Long.parseLong(maxId.substring(0, 19)) + 1;
} else {
result = Long.parseLong(maxId) + 1;
}
} else {
result = Long.parseLong(clusterTablePrefix + "00000000001");
}
return result;
}
//TODO 扩展为在多集群高并发下获得 从1开始auto_increment的id
//根据<table table_code="2003" table_name="RM_AFFIX" id_name="ID"/>和clusterIdPrefix,获得查询最大值的sql
String getSqlSelectMax(Element eleTable, String clusterIdPrefix) {
String tableName = eleTable.valueOf("@table_name");
String tablePrefix = clusterIdPrefix + eleTable.valueOf("@table_code");
String idName = eleTable.valueOf("@id_name");
StringBuilder sql = new StringBuilder();
sql.append("select ");
sql.append("max(");
sql.append(idName);
sql.append(") ");
sql.append("max_id from ");
sql.append(tableName);
sql.append(" where ");
sql.append(idName);
sql.append(" like '");
sql.append(tablePrefix);
sql.append("%'");
return sql.toString();
}
void doInitIdBatch(Map<String, Element> mTableName_Ele) {
int batchSize = 0;
Map<String, Element> todoTable = new RmSequenceMap<String, Element>();
for (Map.Entry<String, Element> en: mTableName_Ele.entrySet()) {
String tableName = en.getKey().toUpperCase();
Element eleTable = en.getValue();
if("1".equals(eleTable.valueOf("@multi_db")) || batchSize >= MAX_BATCH_SIZE) {
doInitIdBatchQuery(todoTable);
batchSize = 0;
todoTable.clear();
}
todoTable.put(tableName, eleTable);
batchSize ++;
if("1".equals(eleTable.valueOf("@multi_db"))){
doInitIdBatchQuery(todoTable);
batchSize = 0;
todoTable.clear();
}
}
doInitIdBatchQuery(todoTable);
}
void doInitIdBatchQuery(Map<String, Element> todoTable) {
if(todoTable.size() == 0) {
return;
}
IRmCommonService cService = RmBeanHelper.getCommonServiceInstance();
//取到当前的集群节点ID
String clusterIdPrefix = RmConfig.getClusterIdPrefix();
StringBuilder sql = new StringBuilder();
int indexThisBatch = 0;
List<String> lTmpTableName = new ArrayList<String>();
List<String> lTmpTablePrefix = new ArrayList<String>();
for (Map.Entry<String, Element> en: todoTable.entrySet()) {
String tableName = en.getKey();
Element eleTable = en.getValue();
String tablePrefix = clusterIdPrefix + eleTable.valueOf("@table_code");
lTmpTableName.add(tableName);
lTmpTablePrefix.add(tablePrefix);
String sqlSingle = getSqlSelectMax(eleTable, clusterIdPrefix);
if(RmConfig.isGenerateIdFromDb()) {
mTableName_sql.put(tableName, new String[]{sqlSingle, tablePrefix});
}
if(sql.length() > 0) {
sql.append(" union all ");
}
sql.append(sqlSingle);
indexThisBatch ++;
}
if(!RmConfig.isGenerateIdFromDb()) {
try {
List<String> lMax_id = cService.doQuery(sql.toString(), new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("max_id");
}
});
indexThisBatch = 0; //归零
for(String maxId : lMax_id) {
long lTableId = getMaxIdOrDefault(lTmpTablePrefix.get(indexThisBatch), maxId);
this.mId.put(lTmpTableName.get(indexThisBatch), new AtomicLong(lTableId));
indexThisBatch ++;
}
} catch (BadSqlGrammarException e) {
RmLogHelper.getLogger(this.getClass()).warn("从主数据源初始化" + todoTable.keySet() + "的最大id失败:" + e.toString());
if("1".equals(todoTable.values().toArray(new Element[0])[0].valueOf("@multi_db"))) {
doBadSqlGrammar(sql.toString(), todoTable.keySet().toArray(new String[0])[0], lTmpTablePrefix.get(0));
}
}catch (Exception e) {
e.printStackTrace();
RmLogHelper.getLogger(RmIdFactory.class).error("初始化" + todoTable.keySet() + "的最大id失败:" + e.toString());
}
}
}
void doInitId( Map<String, Element> mTableName_Ele) {
IRmCommonService cService = RmBeanHelper.getCommonServiceInstance();
//取到当前的集群节点ID
String clusterIdPrefix = RmConfig.getClusterIdPrefix();
for (Map.Entry<String, Element> en: mTableName_Ele.entrySet()) {
String tableName = en.getKey();
Element eleTable = en.getValue();
String clusterTablePrefix = clusterIdPrefix + eleTable.valueOf("@table_code");
String strsql = getSqlSelectMax(eleTable, clusterIdPrefix);
if(RmConfig.isGenerateIdFromDb()) {
mTableName_sql.put(tableName, new String[]{strsql, clusterTablePrefix});
}
if(!RmConfig.isGenerateIdFromDb()) {
try {
List<String> lMax_id = cService.doQuery(strsql, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("max_id");
}
});
long lTableId = getMaxIdOrDefault(clusterTablePrefix, lMax_id.size() > 0 ? lMax_id.get(0) : null);
this.mId.put(tableName, new AtomicLong(lTableId));
} catch (BadSqlGrammarException e) {
RmLogHelper.getLogger(this.getClass()).warn("从主数据源初始化" + tableName + "的最大id失败:" + e.toString());
if("1".equals(eleTable.valueOf("@multi_db"))) {
doBadSqlGrammar(strsql, tableName, clusterTablePrefix);
}
}catch (Exception e) {
e.printStackTrace();
RmLogHelper.getLogger(RmIdFactory.class).error("初始化" + tableName + "的最大id失败:" + e.toString());
}
}
}
}
void doBadSqlGrammar(String sql, String tableName, String clusterTablePrefix) {
IRmCommonService cService = RmBeanHelper.getCommonServiceInstance();
//循环分频道数据源
String xmlFile = RmPathHelper.getWebInfDir() + "/config/spring/rm.public.applicationContext.xml";
try {
Document doc = RmXmlHelper.parse(RmXmlHelper.formatToUrl(xmlFile));
for (Iterator itBean = doc.selectNodes("/beans/node()[@parent='abstractDataSource']").iterator(); itBean.hasNext();) {
Node nBean = (Node) itBean.next();
String beanId = nBean.valueOf("@id");
if(beanId != null && beanId.startsWith("dataSource_")) {
String channelId = beanId.substring("dataSource_".length());
//高度注意,切换到分频道数据库!!!
try {
RmMultiDbHolder.setChannelId(channelId);
List lMax_id = cService.doQuery(sql, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("max_id");
}
});
if(this.mId.get(tableName) == null) { //如果当前id未初始化
this.mId.put(tableName, new AtomicLong(getMaxIdOrDefault(clusterTablePrefix, null)));
}
if(lMax_id.size() > 0 && lMax_id.get(0) != null) { //分库中有数据
if(Long.parseLong(lMax_id.get(0).toString()) + 1 > this.mId.get(tableName).longValue()) { //并且是最大的id
this.mId.get(tableName).set(Long.parseLong(lMax_id.get(0).toString()) + 1);
}
}
} catch (Exception e2) {
RmLogHelper.getLogger(this.getClass()).warn("从" + beanId + "查询" + tableName + "的最大id失败:" + e2.getMessage());
} finally {
RmMultiDbHolder.clearChannelId();
}
}
};
} catch (Exception e2) {
RmLogHelper.getLogger(this.getClass()).warn("从" + xmlFile + "初始化" + tableName + "的最大id失败:" + e2.getMessage());
}
}
/**
* 获得单例
* @return
*/
public static IRmIdFactory getIdFactory() {
if(!isInitId) {
synchronized (RmIdFactory.class) {
if(!isInitId) {
idFactory = (IRmIdFactory) RmBeanFactory.getBean("org.quickbundle.itf.base.IRmIdFactory");
idFactory.initBeanFactory();
isInitId = true;
}
}
}
return idFactory;
}
public synchronized String[] requestIdInner(String tableName, int length) {
if(idFactory == null && isInitId) {
return null;
}
if(RmConfig.isGenerateIdFromDb()) {
String strsql = this.mTableName_sql.get(tableName)[0];
List<String> lMax_id = RmBeanHelper.getCommonServiceInstance().doQuery(strsql, new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("max_id");
}
});
long lTableId = getMaxIdOrDefault(this.mTableName_sql.get(tableName)[1], lMax_id.size() > 0 ? lMax_id.get(0) : null);
if(this.mId.get(tableName) == null) {
this.mId.put(tableName, new AtomicLong());
}
if(lTableId > this.mId.get(tableName).longValue()) {
this.mId.get(tableName).set(lTableId);
}
}
//返回值
String[] ids = new String[length];
for (int i = 0; i < ids.length; i++) {
long tmpId =this.mId.get(tableName).getAndIncrement();
ids[i] = String.valueOf(tmpId);
}
return ids;
}
/**
* 获取单个唯一ID
*
* @param tableName 表名
* @return 返回内存中自增长的ID,未找到返回null
*/
public static String requestId(String tableName) {
String[] ids = requestId(tableName, 1);
if(ids == null || ids.length == 0) {
return null;
}
return ids[0];
}
/**
* 获取单个唯一ID,Long格式
*
* @param tableName 表名
* @return 返回内存中自增长的ID,未找到返回null
*/
public static Long requestIdLong(String tableName) {
return new Long(requestId(tableName));
}
/**
* 批量获取唯一ID
* @param tableName 表名
* @param length 批量数
* @return 返回内存中自增长的ID,未找到返回null
*/
public static String[] requestId(String tableName, int length) {
if(length < 1) {
return new String[0];
}
return getIdFactory().requestIdInner(tableName.toUpperCase(), length);
}
/**
* 批量获取唯一ID,Long[]格式
* @param tableName 表名
* @param length 批量数
* @return 返回内存中自增长的ID,未找到返回null
*/
public static Long[] requestIdLong(String tableName, int length) {
String[] ids = requestId(tableName, length);
Long[] result = new Long[length];
for (int i = 0; i < ids.length; i++) {
result[i] = new Long(ids[i]);
}
return result;
}
//全局单例
private static IRmIdFactory idFactory = null;
//全局单例的初始化标记,用于双检锁安全判断
private static volatile boolean isInitId = false;
}