1. Introduction to schema attributes
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="">
<table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
nameSuffix="" primaryKey="" autoIncrement="" needAddLimit="" type="" ruleRequired="" subTables=""/>
</schema>
primaryKey records the primary key, which is used for subsequent routing analysis and enabling self-growing primary keys
nameSuffix route, add support for dynamic date table
Whether the autoIncrement record is automatically incremented by the primary key, the default is not, (enable the global sequence handler)
Whether the needAddLimit record needs to be added to the returned result set limit, the default needs to be added
type record type, whether it is global
Whether the ruleRequired record is bound with a sharding rule
subTables sub-table function
Second, source code interpretation
private void load(String dtdFile, String xmlFile) { InputStream dtd = null; InputStream xml = null; dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile); xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile); Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement(); //Load all DataHost first loadDataHosts(root); //Reload all DataNodes loadDataNodes(root); //Finally load all Schema loadSchemas(root); } <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode=""> <table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> <table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> <table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" /> </schema> private void loadSchemas(Element root) { NodeList list = root.getElementsByTagName("schema"); for (int i = 0, n = list.getLength(); i < n; i++) { Element schemaElement = (Element) list.item(i); //read each property String name = schemaElement.getAttribute("name"); String dataNode = schemaElement.getAttribute("dataNode"); String checkSQLSchemaStr = schemaElement.getAttribute("checkSQLschema"); String sqlMaxLimitStr = schemaElement.getAttribute("sqlMaxLimit"); int sqlMaxLimit = -1; //Read sql return result set limit if (sqlMaxLimitStr != null && !sqlMaxLimitStr.isEmpty()) { sqlMaxLimit = Integer.parseInt(sqlMaxLimitStr); } // check dataNode already exists or not, see if there is a datanode in the schema tag String defaultDbType = null; //Check and add dataNode if (dataNode != null && !dataNode.isEmpty()) { List dataNodeLst = new ArrayList(1); dataNodeLst.add(dataNode); checkDataNodeExists(dataNodeLst); String dataHost = dataNodes.get(dataNode).getDataHost(); defaultDbType = dataHosts.get(dataHost).getDbType(); } else { dataNode = null; } //Load all tables under schema Map<String, TableConfig> tables = loadTables (schemaElement); / / Determine whether the schema is repeated if (schemas.containsKey(name)) { throw new ConfigException("schema " + name + " duplicated!"); } // If the table is set, you do not need to set the dataNode attribute, and if the table is not set, the dataNode attribute must be set. if (dataNode == null && tables.size() == 0) { throw new ConfigException( "schema " + name + " didn't config tables,so you must set dataNode property!"); } SchemaConfig schemaConfig = new SchemaConfig(name, dataNode, tables, sqlMaxLimit, "true".equalsIgnoreCase(checkSQLSchemaStr)); //Set the DB type, which is helpful for the subsequent sql statement routing analysis if (defaultDbType != null) { schemaConfig.setDefaultDataNodeDbType(defaultDbType); if (!"mysql".equalsIgnoreCase(defaultDbType)) { schemaConfig.setNeedSupportMultiDBType(true); } } // Determine whether there is a database type that is not mysql, which is convenient for parsing to determine whether to enable multi-database paging syntax parsing for (TableConfig tableConfig : tables.values()) { if (isHasMultiDbType(tableConfig)) { schemaConfig.setNeedSupportMultiDBType(true); break; } } //Record the DB type of each dataNode Map<String, String> dataNodeDbTypeMap = new HashMap<>(); for (String dataNodeName : dataNodes.keySet()) { DataNodeConfig dataNodeConfig = dataNodes.get(dataNodeName); String dataHost = dataNodeConfig.getDataHost(); DataHostConfig dataHostConfig = dataHosts.get(dataHost); if (dataHostConfig != null) { String dbType = dataHostConfig.getDbType(); dataNodeDbTypeMap.put(dataNodeName, dbType); } } schemaConfig.setDataNodeDbTypeMap(dataNodeDbTypeMap); schemas.put(name, schemaConfig); } }
private Map<String, TableConfig> loadTables(Element node) { // Map<String, TableConfig> tables = new HashMap<String, TableConfig>(); // Support quotes [`] BEN GONG in table names Map<String, TableConfig> tables = new TableConfigMap(); NodeList nodeList = node.getElementsByTagName("table"); for (int i = 0; i < nodeList.getLength(); i++) { Element tableElement = (Element) nodeList.item(i); String tableNameElement = tableElement.getAttribute("name").toUpperCase(); //TODO: Routing, add support for dynamic date table String tableNameSuffixElement = tableElement.getAttribute(" nameSuffix ").toUpperCase(); if ( !"".equals( tableNameSuffixElement ) ) { if( tableNameElement.split(",").length > 1 ) { throw new ConfigException("nameSuffix " + tableNameSuffixElement + ", require name parameter cannot multiple breaks!"); } //The prefix is used to indicate the date format tableNameElement = doTableNameSuffix (tableNameElement, tableNameSuffixElement); } //Record the primary key for subsequent routing analysis and enable self-increasing primary keys String[] tableNames = tableNameElement.split(","); String primaryKey = tableElement.hasAttribute("primaryKey") ? tableElement.getAttribute("primaryKey").toUpperCase() : null; //Whether the primary key of the record is auto-incremented, the default is not, (enable the global sequence handler) boolean autoIncrement = false; if (tableElement.hasAttribute("autoIncrement")) { autoIncrement = Boolean.parseBoolean(tableElement.getAttribute("autoIncrement")); } //Whether the record needs to be added to the returned result set limit, the default needs to be added boolean needAddLimit = true; if (tableElement.hasAttribute("needAddLimit")) { needAddLimit = Boolean.parseBoolean (tableElement.getAttribute ("needAddLimit")); } //Record type, whether it is global String tableTypeStr = tableElement.hasAttribute("type") ? tableElement.getAttribute("type") : null; int tableType = TableConfig.TYPE_GLOBAL_DEFAULT; if ("global".equalsIgnoreCase(tableTypeStr)) { tableType = TableConfig.TYPE_GLOBAL_TABLE; } //Record dataNode, which dataNode is distributed on String dataNode = tableElement.getAttribute("dataNode"); TableRuleConfig tableRule = null; if (tableElement.hasAttribute("rule")) { String ruleName = tableElement.getAttribute("rule"); tableRule = tableRules.get(ruleName); if (tableRule == null) { throw new ConfigException("rule " + ruleName + " is not found!"); } } boolean ruleRequired = false; //Record whether there is a sharding rule bound if (tableElement.hasAttribute("ruleRequired")) { ruleRequired = Boolean.parseBoolean (tableElement.getAttribute ("ruleRequired")); } if (tableNames == null) { throw new ConfigException("table name is not found!"); } //distribute function, rearrange dataNode String distPrex = "distribute("; boolean distTableDns = dataNode.startsWith(distPrex); if (distTableDns) { dataNode = dataNode.substring(distPrex.length(), dataNode.length() - 1); } // sub-table function String subTables = tableElement.getAttribute("subTables"); for (int j = 0; j < tableNames.length; j++) { String tableName = tableNames[j]; TableRuleConfig tableRuleConfig=tableRule ; if(tableRuleConfig!=null) { //Special processing for the function that implements TableRuleAware Create a new instance according to each table RuleConfig rule= tableRuleConfig.getRule(); if(rule.getRuleAlgorithm() instanceof TableRuleAware) { tableRuleConfig = (TableRuleConfig) ObjectUtil.copyObject(tableRuleConfig); tableRules.remove(tableRuleConfig.getName()) ; String newRuleName = tableRuleConfig.getName() + "_" + tableName; tableRuleConfig. setName(newRuleName); TableRuleAware tableRuleAware= (TableRuleAware) tableRuleConfig.getRule().getRuleAlgorithm(); tableRuleAware.setRuleName(newRuleName); tableRuleAware.setTableName(tableName); tableRuleConfig.getRule().getRuleAlgorithm().init(); tableRules.put(newRuleName,tableRuleConfig); } } TableConfig table = new TableConfig(tableName, primaryKey, autoIncrement, needAddLimit, tableType, dataNode, getDbType(dataNode), (tableRuleConfig != null) ? tableRuleConfig.getRule() : null, ruleRequired, null, false, null, null,subTables); checkDataNodeExists(table.getDataNodes()); // Check whether the sharding table sharding rule configuration is legal if(table.getRule() != null) { checkRuleSuitTable(table); } if (distTableDns) { distributeDataNodes(table.getDataNodes()); } //check for deduplication if (tables.containsKey(table.getName())) { throw new ConfigException("table " + tableName + " duplicated!"); } // put in map tables.put(table.getName(), table); } //Only when tableName is configured with a single table (without commas) can there be subtables if (tableNames.length == 1) { TableConfig table = tables.get(tableNames[0]); // process child tables recursively calls processChildTables (tables, table, dataNode, tableElement); } } return tables; } /** * Process dynamic date table, support YYYYMM, YYYYMMDD two formats * * YYYYMM format: yyyymm,2015,01,60 * YYYYMMDD format: yyyymmdd,2015,01,10,50 * * @param tableNameElement * @param tableNameSuffixElement * @return */ private String doTableNameSuffix(String tableNameElement, String tableNameSuffixElement) { String newTableName = tableNameElement; String[] params = tableNameSuffixElement.split(","); String suffixFormat = params[0].toUpperCase(); if ( suffixFormat.equals("YYYYMM") ) { //read parameters int yyyy = Integer.parseInt( params[1] ); int mm = Integer.parseInt( params[2] ); int mmEndIdx = Integer.parseInt( params[3] ); // date processing SimpleDateFormat yyyyMMSDF = new SimpleDateFormat("yyyyMM"); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, yyyy ); cal.set(Calendar.MONTH, mm - 1 ); cal.set(Calendar.DATE, 0 ); //rewrite table name StringBuffer tableNameBuffer = new StringBuffer(); for(int mmIdx = 0; mmIdx <= mmEndIdx; mmIdx++) { tableNameBuffer.append( tableNameElement ); tableNameBuffer.append( yyyyMMSDF.format(cal.getTime()) ); cal.add(Calendar.MONTH, 1); if ( mmIdx != mmEndIdx) { tableNameBuffer.append(","); } } //A batch of table names separated by commas A1, B2, C3 newTableName = tableNameBuffer.toString(); } else if ( suffixFormat.equals("YYYYMMDD") ) { //read parameters int yyyy = Integer.parseInt( params[1] ); int mm = Integer.parseInt( params[2] ); int dd = Integer.parseInt( params[3] ); int ddEndIdx = Integer.parseInt( params[4] ); // date processing SimpleDateFormat yyyyMMddSDF = new SimpleDateFormat("yyyyMMdd"); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, yyyy ); cal.set(Calendar.MONTH, mm - 1 ); cal.set(Calendar.DATE, dd ); //rewrite table name StringBuffer tableNameBuffer = new StringBuffer(); for(int ddIdx = 0; ddIdx <= ddEndIdx; ddIdx++) { tableNameBuffer.append( tableNameElement ); tableNameBuffer.append( yyyyMMddSDF.format(cal.getTime()) ); cal.add(Calendar.DATE, 1); if ( ddIdx != ddEndIdx) { tableNameBuffer.append(","); } } //A batch of table names separated by commas A1, B2, C3 newTableName = tableNameBuffer.toString(); } return newTableName; }
Three, dynamic date table demonstration
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<table name="tab" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user1" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="user" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<!-- 2015--January starts from December 2014, and accumulates to 24 months-->
<table name="test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" nameSuffix="yyyymm,2015,01,24" />
<!-- Starting from January 1, 2015, the total accumulated to 100 days -->
<table name="demo" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" nameSuffix="yyyymmdd,2015,01,01,100" />
</schema>