package com.taobao.tddl.repo.mysql;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.taobao.tddl.common.exception.TddlRuntimeException;
import com.taobao.tddl.common.utils.XmlHelper;
import com.taobao.tddl.common.utils.extension.Activate;
import com.taobao.tddl.executor.spi.IDataSourceGetter;
import com.taobao.tddl.optimizer.config.table.RepoSchemaManager;
import com.taobao.tddl.optimizer.config.table.TableMeta;
import com.taobao.tddl.optimizer.config.table.parse.TableMetaParser;
import com.taobao.tddl.repo.mysql.spi.DatasourceMySQLImplement;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* @author mengshi.sunmengshi 2013-12-5 下午6:18:14
* @since 5.0.0
*/
@Activate(name = "MYSQL_JDBC", order = 2)
public class MysqlTableMetaManager extends RepoSchemaManager {
public static String xmlHead = "<tables xmlns=\"https://github.com/tddl/tddl/schema/table\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://github.com/tddl/tddl/schema/table https://raw.github.com/tddl/tddl/master/tddl-common/src/main/resources/META-INF/table.xsd\">";
private final static Logger logger = LoggerFactory.getLogger(MysqlTableMetaManager.class);
private final IDataSourceGetter mysqlDsGetter = new DatasourceMySQLImplement();
public MysqlTableMetaManager(){
}
protected IDataSourceGetter getDatasourceGetter() {
return this.mysqlDsGetter;
}
/**
* 需要各Repo来实现
*
* @param tableName
*/
@Override
protected TableMeta getTable0(String logicalTableName, String actualTableName) {
TableMeta ts = fetchSchema(logicalTableName, actualTableName);
return ts;
}
private TableMeta fetchSchema(String logicalTableName, String actualTableName) {
if (actualTableName == null) {
throw new TddlRuntimeException("table " + logicalTableName + " cannot fetched without a actual tableName");
}
DataSource ds = getDatasourceGetter().getDataSource(this.getGroup().getName());
if (ds == null) {
logger.error("schema of " + logicalTableName + " cannot be fetched, datasource is null, group name is "
+ this.getGroup().getName());
return null;
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = ds.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from " + actualTableName + " limit 1");
ResultSetMetaData rsmd = rs.getMetaData();
DatabaseMetaData dbmd = conn.getMetaData();
return resultSetMetaToSchema(rsmd, dbmd, logicalTableName, actualTableName);
} catch (Exception e) {
if (e instanceof SQLException) {
if ("42000".equals(((SQLException) e).getSQLState())) {
try {
rs = stmt.executeQuery("select * from " + actualTableName + " where rownum<=2");
ResultSetMetaData rsmd = rs.getMetaData();
DatabaseMetaData dbmd = conn.getMetaData();
return resultSetMetaToSchema(rsmd, dbmd, logicalTableName, actualTableName);
} catch (SQLException e1) {
logger.warn(e);
}
}
}
logger.error("schema of " + logicalTableName + " cannot be fetched", e);
return null;
} finally {
try {
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
logger.warn(e);
}
}
}
public static TableMeta resultSetMetaToSchema(ResultSetMetaData rsmd, DatabaseMetaData dbmd,
String logicalTableName, String actualTableName) {
String xml = resultSetMetaToSchemaXml(rsmd, dbmd, logicalTableName, actualTableName);
if (xml == null) {
return null;
}
xml = xml.replaceFirst("<tables>", xmlHead);
List<TableMeta> ts = null;
ts = TableMetaParser.parse(xml);
if (ts != null && !ts.isEmpty()) {
return ts.get(0);
}
return null;
}
public static String resultSetMetaToSchemaXml(ResultSetMetaData rsmd, DatabaseMetaData dbmd,
String logicalTableName, String actualTableName) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
// build
builder = dbf.newDocumentBuilder();
Document doc = builder.newDocument();
Element tables = doc.createElement("tables");
doc.appendChild(tables); // 将根元素添加到文档上
Element table = doc.createElement("table");
table.setAttribute("name", logicalTableName);
tables.appendChild(table);
Element columns = doc.createElement("columns");
table.appendChild(columns);
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
Element column = doc.createElement("column");
column.setAttribute("name", rsmd.getColumnName(i));
columns.appendChild(column);
String type = TableMetaParser.jdbcTypeToDataTypeString(rsmd.getColumnType(i));
column.setAttribute("type", type);
}
try {
ResultSet pkrs = dbmd.getPrimaryKeys(null, null, actualTableName);
if (pkrs.next()) {
Element primaryKey = doc.createElement("primaryKey");
primaryKey.appendChild(doc.createTextNode(pkrs.getString("COLUMN_NAME")));
table.appendChild(primaryKey);
} else {
Element primaryKey = doc.createElement("primaryKey");
primaryKey.appendChild(doc.createTextNode(rsmd.getColumnName(1)));
table.appendChild(primaryKey);
}
} catch (Exception ex) {
// logger.warn("fetch pk error, choose the first column as pk, tablename is: "
// + logicalTableName, ex);
logger.warn("fetch pk error, choose the first column as pk, tablename is: " + logicalTableName);
Element primaryKey = doc.createElement("primaryKey");
primaryKey.appendChild(doc.createTextNode(rsmd.getColumnName(1)));
table.appendChild(primaryKey);
}
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter outwriter = new OutputStreamWriter(baos);
XmlHelper.callWriteXmlFile(doc, outwriter, "utf-8");
outwriter.close();
String content = baos.toString();
return content;
} catch (Exception e) {
logger.error("fetch schema error", e);
}
return null;
} catch (Exception ex) {
logger.error("fetch schema error", ex);
return null;
}
}
}