有這么一種場景,設(shè)計好的數(shù)據(jù)模型在有數(shù)據(jù)以后要修改數(shù)據(jù)模型。本文就是針對這個問題展開的。
要增加,刪除一列,這根本不是問題,關(guān)鍵是要修改一列。這需要考慮到那些問題呢?
在java里面有類型轉(zhuǎn)換,轉(zhuǎn)換分為兩種:自然轉(zhuǎn)換和強制轉(zhuǎn)換。自然轉(zhuǎn)換就是把子類型轉(zhuǎn)成父類型,強制轉(zhuǎn)換就是把父類型轉(zhuǎn)成子類型。在數(shù)據(jù)庫里面也一樣,當(dāng)然還要包括長度的截短和加長。強制轉(zhuǎn)換和截短會造成數(shù)據(jù)的不準(zhǔn)確和異常,本文不考慮這個情況。
那么,如何改變數(shù)據(jù)表的列類型呢?通過SQL語句直接操作數(shù)據(jù)庫,當(dāng)然能解決這個問題,可是,如何有很多張表都要修改呢?那不把人累死了!!!
我就碰到了這么一個問題,加班加點的,寫成了一個JAVA小程序,來自動實現(xiàn)我的要求。
需求:把數(shù)據(jù)庫中字段類型為blob和varchar2(4000)的數(shù)據(jù)表都修改為clob;
問題解決思路:復(fù)制數(shù)據(jù)表結(jié)構(gòu)和數(shù)據(jù)到一個臨時表,先刪除要修改的列,再創(chuàng)建同名不同類型的列,從原始表中查出這一列的數(shù)據(jù),復(fù)制的臨時表中,刪除原始表,修改臨時表名為原始表名。
處理過程日志截圖:

代碼思路及重要代碼:
1:獲取系統(tǒng)所有的用戶表:
String sql = " select table_name from user_tables " ;
2:獲取表的主鍵(只考慮單主鍵)
String sql = " select column_name from user_cons_columns " +
" where constraint_name = (" +
" select constraint_name from user_constraints " +
" where table_name = upper('"+tableName+"') " +
"and constraint_type = 'P'" +
")" ;
3:獲取表中列信息:列名,類型 和長度。
String sql = " select column_name,data_type,data_length from
user_tab_columns where table_name = '"+ tableName+"'" ;
4:根據(jù)2和3,判斷要修改的列,組織(主鍵和要修改的列信息)數(shù)據(jù)。
代碼片段:
List<String[]> :裝有要修改表的表名,主鍵,列信息(列名,類型,長度);List中,第一條數(shù)據(jù)為表名,第二條數(shù)據(jù)為主鍵,其余為要修改的列信息,
String[]為變長數(shù)組;
String columnName="";
String dataType="";
String dataLength="";
List<String[]> result = new ArrayList<String[]>(3);
result.add(new String[]{tableName});
String primaryKeyName = getPrimaryKey(tableName);
if(primaryKeyName.equals("")||primaryKeyName==null) return null ;
result.add(new String[]{primaryKeyName});
try {
String sql = " select column_name,data_type,data_length from user_tab_columns where table_name = '"+ tableName+"'" ;
ResultSet resultSet = OracleDBUtil.executeQuery(sql);
while(resultSet.next()){
columnName=resultSet.getString(1);
dataType=resultSet.getString(2);
dataLength=resultSet.getString(3);
if(dataType.equals("BLOB")){
result.add(new String[]{columnName,dataType});
}else if((dataType.equals("VARCHAR2")&&Integer.parseInt(dataLength) >= Constant.varchar2_size)||(dataType.equals("VARCHAR")&&Integer.parseInt(dataLength) >= Constant.varchar2_size)){
result.add(new String[]{columnName,dataType,dataLength});
}
}
resultSet.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
return result;
5:取出4返回的信息,組織數(shù)據(jù),啟動修改流程;
for (List<String[]> table : tables) {
//tableInfo = tableInfo+ "\t表名:\t" ;
tableInfo = "\t表名:\t" ;
tableInfo = tableInfo+table.get(0)[0]+ "\t主鍵 :\t "+table.get(1)[0]+" \t字段 :\t ";
String[] columns = new String[table.size()-2];
for (int i =2;i<table.size();i++) {
if(table.get(i)==null) break ;
tableInfo = tableInfo+table.get(i)[0]+" "+table.get(i)[1];
if(table.get(i).length == 3) tableInfo = tableInfo+ "("+table.get(i)[2]+")";
tableInfo = tableInfo + "\t";
String[] columnInfo = table.get(i) ;
columns[i-2]=columnInfo[0];
}
tableInfo = tableInfo+"\n";
System.out.println(tableInfo);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = "";
System.out.println(" 是否修改,請輸入'Y'或者'N' ! ");
try {
str = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if(str.equalsIgnoreCase("y")){
blobOrVarchar2Clob(table.get(0)[0],table.get(1)[0],columns);
}
}
System.out.println("數(shù)據(jù)表修改處理邏輯結(jié)束!!!");
6:修改流程:使用了門面模式,實現(xiàn)了“復(fù)制數(shù)據(jù)表結(jié)構(gòu)和數(shù)據(jù)到一個臨時表,先刪除要修改的列,再創(chuàng)建同名不同類型的列,從原始表中查出這一列的數(shù)據(jù),復(fù)制的臨時表中,刪除原始表,修改臨時表名為原始表名。”構(gòu)想。
public static boolean blobOrVarchar2Clob(String srcTableName, String srcTableIdName, String
columnNames) {
if (srcTableName == null || srcTableName.equals("")
|| columnNames == null || columnNames.length == 0
|| srcTableIdName == null || srcTableIdName.equals(""))
return false;
String printColumnNames = "" ;
for (String columnName : columnNames) {
printColumnNames += " " + columnName ;
}
System.out.println("馬上就被修改 表名: "+srcTableName + " ,ID: " + srcTableIdName+" 字段: "+printColumnNames);
Utils.createTempTable(srcTableName);
for (String columnName : columnNames) {
if(columnName==null||columnName.equals(""))continue ;
Utils.dropTempTableColumn(srcTableName, columnName);
Utils.addTempTableClobColumn(srcTableName, columnName);
Utils.copyDataOneColumn(srcTableName, columnName, srcTableIdName);
}
Utils.dropSrcTable(srcTableName+"_");
// Utils.dropSrcTable(srcTableName);
// Utils.modifyTempTableToSrcTable(srcTableName);
return true;
}
7:從原始表復(fù)制一列數(shù)據(jù)到臨時表:將數(shù)據(jù)寫入Map結(jié)構(gòu),再寫入臨時表。
代碼片斷:
HashMap<Integer, String> map = new HashMap<Integer, String>();
try {
while(resultSet.next()){
String content = "";
int id = resultSet.getInt(srcTableIdName);
int columnIndex = resultSet.findColumn(columnName);
ResultSetMetaData resulSetMetaData = resultSet.getMetaData();
String columnTypeName =resulSetMetaData.getColumnTypeName(columnIndex);
if("VARCHAR2".equals(columnTypeName)||"VARCHAR".equals(columnTypeName)){
content = resultSet.getString(columnName);
}else if("BLOB".equals(columnTypeName)){
Blob blob = resultSet.getBlob(columnName);
if(blob==null)continue;
content = Utils.readBlobToString(blob);
}
map.put(id, content);
}
resultSet.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
Set<Entry <Integer,String> > set = map.entrySet();
Iterator<Entry<Integer, String>> iterator =set.iterator();
while(iterator.hasNext()){
Entry<Integer, String> entry = iterator.next();
sql = " update " + srcTableName + "_ " + " set "
+ columnName + " = ? where " + srcTableName + "_."
+ srcTableIdName + " = ? " ;
PreparedStatement pstatement =OracleDBUtil.getPreparedStatement(sql);
try {
pstatement.setString(1, entry.getValue());
pstatement.setInt(2, entry.getKey());
OracleDBUtil.executeUpdate(pstatement);
pstatement.close();