/**
* Copyright 2014 Duan Bingnan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pinus4j.generator.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.pinus4j.cluster.enums.EnumSyncAction;
import org.pinus4j.entity.meta.DBTable;
import org.pinus4j.entity.meta.DBTableColumn;
import org.pinus4j.entity.meta.DBTableIndex;
import org.pinus4j.entity.meta.DBTablePK;
import org.pinus4j.exceptions.DDLException;
import org.pinus4j.generator.IDBGenerator;
import org.pinus4j.utils.JdbcUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
/**
* MYSQL数据库生成器的实现. 用于生成MYSQL相关的数据表.
*
* @author duanbn
*/
public class DBMySqlGeneratorImpl implements IDBGenerator {
/**
* 日志.
*/
public static final Logger LOG = LoggerFactory
.getLogger(DBMySqlGeneratorImpl.class);
public static final String SQL_SHOWTABLE = "show tables";
private static final Map<String, Map<String, DBTable>> existsTable = new HashMap<String, Map<String, DBTable>>();
private EnumSyncAction syncAction;
@Override
public void syncTable(Connection conn, DBTable table) throws DDLException {
Map<String, DBTable> tables = _getTable(conn);
DBTable existTable = tables.get(table.getNameWithIndex());
// 已经存在表
if (existTable != null) {
if (this.syncAction == EnumSyncAction.UPDATE) {
existTable.setCluster(table.getCluster());
existTable.setName(table.getName());
existTable.setTableIndex(table.getTableIndex());
existTable.setShardingBy(table.getShardingBy());
existTable.setShardingNum(table.getShardingNum());
try {
Statement s = null;
for (String sql : table.getAlterSQL(existTable, false)) {
try {
s = conn.createStatement();
LOG.info("begin execute [" + sql + "]");
s.execute(sql);
} finally {
if (s != null) {
s.close();
}
}
}
} catch (SQLException e) {
throw new DDLException("update table = " + table.getName() + " failure", e);
}
}
return;
}
// 不存在表.
try {
Statement s = null;
for (String sql : table.getCreateSQL()) {
try {
LOG.info(sql);
s = conn.createStatement();
s.execute(sql);
} finally {
if (s != null) {
s.close();
}
}
}
} catch (Exception e) {
String ignore = "Table '" + table.getNameWithIndex() + "' already exists";
if (!e.getMessage().equals(ignore))
throw new DDLException("create table =" + table.getName() + " failure", e);
}
}
@Override
public void syncTable(Connection conn, DBTable table, int num) throws DDLException {
if (num <= 0) {
LOG.warn("生成表的数量为0, 忽略生成数据表, 请检查零库的shard_cluster表的配置");
return;
}
for (int i = 0; i < num; i++) {
table.setTableIndex(i);
syncTable(conn, table);
}
}
/**
* 获取数据库中已有的表结构.
*
* @param conn
* @return
* @throws DDLException
*/
private Map<String, DBTable> _getTable(Connection conn) throws DDLException {
String url = null;
try {
url = conn.getMetaData().getURL();
} catch (SQLException e1) {
throw new DDLException(e1);
}
if (existsTable.containsKey(url)) {
return existsTable.get(url);
}
// 保存已经在库中存在的表名
Map<String, DBTable> tables = new HashMap<String, DBTable>();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(SQL_SHOWTABLE);
rs = ps.executeQuery();
DBTable table = null;
while (rs.next()) {
String tableName = rs.getString(1);
table = new DBTable(tableName);
_initTableColumn(table, conn);
_initTableIndex(table, conn);
tables.put(tableName, table);
}
existsTable.put(url, tables);
} catch (SQLException e) {
throw new DDLException(e);
} finally {
JdbcUtil.close(ps, rs);
}
return tables;
}
/**
* 初始化数据库中已有的表的索引.
*
* @param table
* @param dbconn
* @throws SQLException
*/
private void _initTableIndex(DBTable table, Connection dbconn) throws SQLException {
ResultSet indexRs = null;
Statement s = dbconn.createStatement();
try {
indexRs = s.executeQuery("SHOW INDEX FROM `" + table.getName() + "`");
Map<String, String> map = new HashMap<String, String>();
while (indexRs.next()) {
String keyName = indexRs.getString("Key_name");
// 主键不当作索引处理
if (keyName.equals("PRIMARY")) {
continue;
}
String colName = indexRs.getString("Column_name");
boolean isUniqe = indexRs.getInt("Non_unique") == 0 ? true : false;
String fieldInfo = colName + ":" + isUniqe;
if (map.get(keyName) != null) {
map.put(keyName, map.get(keyName) + "^" + fieldInfo);
} else {
map.put(keyName, fieldInfo);
}
}
DBTableIndex index = null;
List<String> indexFields = null;
for (Map.Entry<String, String> entry : map.entrySet()) {
index = new DBTableIndex();
indexFields = Lists.newArrayList();
String[] fieldInfos = entry.getValue().split("\\^");
for (String fieldInfo : fieldInfos) {
String[] ss = fieldInfo.split(":");
indexFields.add(ss[0]);
index.setUnique(Boolean.valueOf(ss[1]));
}
index.setFields(indexFields);
table.addIndex(index);
}
} finally {
if (indexRs != null) {
indexRs.close();
}
if (s != null) {
s.close();
}
}
}
/**
* 初始化数据库中已有的表的列.
*
* @param table
* @param dbconn
* @throws SQLException
*/
private void _initTableColumn(DBTable table, Connection dbconn) throws SQLException {
ResultSet colRs = null;
Statement s = dbconn.createStatement();
try {
colRs = s.executeQuery("show full fields from `" + table.getName() + "`");
while (colRs.next()) {
String field = colRs.getString(1);
String typeVal = colRs.getString(2);
String type = typeVal;
int length = 0;
// type
if (typeVal.indexOf("(") > 0) {
type = typeVal.substring(0, typeVal.indexOf("("));
// length
length = Integer.parseInt(typeVal.subSequence(typeVal.indexOf("(") + 1, typeVal.indexOf(")"))
.toString());
}
boolean isAutoIncrement = colRs.getString(7).equals("auto_increment");
String comment = colRs.getString(9);
if (colRs.getString(5).equals("PRI")) {
DBTablePK primaryKey = new DBTablePK();
primaryKey.setField(field);
primaryKey.setType(type);
primaryKey.setLength(length);
primaryKey.setComment(comment);
primaryKey.setAutoIncrement(isAutoIncrement);
table.addPrimaryKey(primaryKey);
table.addColumn(primaryKey.toTableColumn());
} else {
DBTableColumn column = new DBTableColumn();
column.setField(field);
column.setType(type);
column.setLength(length);
// not null
if (colRs.getString(4).equals("NO"))
column.setCanNull(false);
else
column.setCanNull(true);
// default value
if (colRs.getObject(6) != null) {
column.setDefaultValue(colRs.getObject(6));
column.setHasDefault(true);
} else {
column.setHasDefault(false);
}
column.setComment(comment);
column.setAutoIncrement(isAutoIncrement);
table.addColumn(column);
}
}
} catch (SQLException e) {
LOG.warn("get column of " + table.getName() + " fail", e);
} finally {
if (colRs != null) {
colRs.close();
}
if (s != null) {
s.close();
}
}
}
public EnumSyncAction getSyncAction() {
return syncAction;
}
public void setSyncAction(EnumSyncAction syncAction) {
this.syncAction = syncAction;
}
}