Java - Export SQL Backup Commands for Delete, Insert

這篇主要是說明如何針對查詢出來的資料來產生相關delete, insert指令,會使用到apache

beanutils及java reflect等函式庫,如此一來可以達到資料備份的目的。

由於查詢、刪除、新增指令需動態的產生,因此我們會自訂一些前置步驟規則。

首先,需提供table名稱及查詢 where條件後的指令片段
  1. String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
  2. String url = "jdbc:sqlserver://localhost:1433;DatabaseName=Homework";
  3. String user = "sa";
  4. String password = "1234";
  5. Connection m_conn = null;
  6. public static void main(String[] args) {
  7. // TODO Auto-generated method stub
  8. new ExportSQLData();
  9. }
  10. public ExportSQLData(){
  11. try{
  12. Class.forName(driver);
  13. m_conn = DriverManager.getConnection(url, user, password);
  14. String exportCon = "where year = ? and month = ? and source = ?";
  15. String exportTa = "IncomeRecord"; //table name
  16. ...
  17. }catch(Exception e){
  18. e.printStackTrace();
  19. } finally{
  20. try {
  21. m_conn.close();
  22. } catch (SQLException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26. }
  27. }

再來是組出查詢的指令
  1. public ExportSQLData(){
  2. ...
  3. String execSql = composeSQL(exportTa, exportCon);
  4. ...
  5. }
  6. public String composeSQL(String table, String condition){
  7. String sqlTemplate = "select * from %s %s";
  8. return String.format(sqlTemplate, table, condition);
  9. }

緊接著,產生ResultSet,並透過beanutils包裝成RowSetDynaClass
  1. public ExportSQLData(){
  2. ...
  3. RowSetDynaClass rsdc = getRowSet(execSql, "103", "09", "A");
  4. ...
  5. }
  6. public RowSetDynaClass getRowSet(String sql, Object ...params) throws SQLException{
  7. PreparedStatement getRowsList = m_conn.prepareStatement(sql);
  8. for(int i = 0 ; i < params.length ; i++){
  9. if(params[i] instanceof java.lang.String)
  10. getRowsList.setString(i+1, (String)params[i]);
  11. else if(params[i] instanceof java.lang.Integer)
  12. getRowsList.setInt(i+1, (Integer)params[i]);
  13. else if(params[i] instanceof java.math.BigInteger)
  14. getRowsList.setBigDecimal(i+1, (BigDecimal)params[i]);
  15. }
  16. ResultSet rs = getRowsList.executeQuery();
  17. return new RowSetDynaClass(rs);
  18. }

此時當我們得到查詢資料的RowSetDynaClass後,再來就是進行重頭戲的組delete, insert指令
  1. public ExportSQLData(){
  2. ...
  3. exportSQL(rsdc, exportTa);
  4. ...
  5. }
  6. public void exportSQL(RowSetDynaClass rsdc, String table){
  7. List<DynaBean> rows = rsdc.getRows();
  8. List<String> propertyList = new ArrayList<String>();
  9. String delTemplate = "delete from %s where %s;";
  10. String insTemplate = "insert into %s (%s) values (%s);";
  11. try {
  12. Class<?> clazz = null;
  13. try {
  14. clazz = Class.forName("com.bin.beans."+table);
  15. } catch (ClassNotFoundException e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19. //1. 取得bean field map getMethod
  20. Map<String, String> col2Method = getGetterMethod(clazz);
  21. Method m = null;
  22. PrimaryBean bean = (PrimaryBean) clazz.newInstance();
  23. String pkeys[] = bean.getPkeys();
  24. StringBuilder condition = new StringBuilder();
  25. StringBuilder columns = new StringBuilder();
  26. StringBuilder values = new StringBuilder();
  27. StringBuilder result = new StringBuilder();
  28. //2. 組出delete指令的條件
  29. for(int i = 0 ; i < pkeys.length ; i++){
  30. condition.append(pkeys[i]+" = :"+pkeys[i]+" and ");
  31. }
  32. condition.delete(condition.length()-5, condition.length());
  33. //3. 組出insert指令下的欄位名稱
  34. for(DynaProperty d : rsdc.getDynaProperties()){
  35. propertyList.add(d.getName());
  36. columns.append(d.getName()+",");
  37. }
  38. columns.deleteCharAt(columns.length()-1);
  39. // System.out.println(condition.toString());
  40. // System.out.println(propertyList);
  41. // System.out.println(col2Method);
  42. //4. 資料走訪
  43. for(int j = 0 ; j < rows.size() ; j++){
  44. bean = (PrimaryBean) clazz.newInstance();
  45. PropertyUtils.copyProperties(bean, rows.get(j));
  46. String con = condition.toString();
  47. //5. 走訪定義好的primary keys array,並且將delete條件下的指令改成值
  48. for(int i = 0 ; i < pkeys.length ; i++){
  49. m = clazz.getDeclaredMethod(col2Method.get(pkeys[i]));
  50. if(m.invoke(bean) instanceof java.lang.String)
  51. con = con.replace(":"+pkeys[i], "'"+m.invoke(bean).toString()+"'");
  52. else if(m.invoke(bean) instanceof java.lang.Integer
  53. || m.invoke(bean) instanceof java.math.BigInteger)
  54. con = con.replace(":"+pkeys[i], m.invoke(bean).toString());
  55. }
  56. result.append(String.format(delTemplate, table, con).toUpperCase()+"\n");
  57. //6. 組出insert指令的values的部分
  58. for(int k = 0 ; k < propertyList.size() ; k++){
  59. m = clazz.getDeclaredMethod(col2Method.get(propertyList.get(k)));
  60. if(m.invoke(bean) instanceof java.lang.String){
  61. values.append("'"+m.invoke(bean).toString()+"',");
  62. }else if(m.invoke(bean) instanceof java.math.BigDecimal){
  63. values.append((BigDecimal)m.invoke(bean)+",");
  64. }else if(m.invoke(bean) instanceof java.lang.Integer){
  65. values.append((Integer)m.invoke(bean)+",");
  66. }
  67. }
  68. values.deleteCharAt(values.length()-1);
  69. result.append(String.format(insTemplate, table, columns.toString(),
  70. values.toString()).toUpperCase()+"\n");
  71. values.delete(0, values.length());
  72. }
  73. System.out.println(result);
  74. } catch (Exception e){
  75. e.printStackTrace();
  76. }
  77. }
  78. public Map<String, String> getGetterMethod(Class<?> c){
  79. Map<String, String> col2method = new HashMap<String, String>();
  80. Method[] methods = c.getDeclaredMethods();
  81. List<String> fields = new ArrayList<String>();
  82. List<String> fieldsLower = new ArrayList<String>();
  83. for(Field n : c.getDeclaredFields()){
  84. fields.add(n.getName());
  85. fieldsLower.add(n.getName().toLowerCase());
  86. }
  87. String colname = "";
  88. boolean flag = false;
  89. for (Method method : methods){
  90. if (Modifier.isPublic(method.getModifiers()) &&
  91. method.getParameterTypes().length == 0) {
  92. if (method.getName().matches("^get[A-Z].*") &&
  93. !method.getReturnType().equals(void.class)){
  94. flag = true;
  95. }
  96. if (method.getName().matches("^is[A-Z].*") &&
  97. method.getReturnType().equals(boolean.class)){
  98. flag = true;
  99. }
  100. if(flag){
  101. colname = method.getName().toLowerCase()
  102. .substring(3, method.getName().length());
  103. for(int i = 0 ; i < fieldsLower.size() ; i++){
  104. if(fieldsLower.get(i).equals(colname)){
  105. col2method.put(fields.get(i), method.getName());
  106. }
  107. }
  108. }
  109. flag = false;
  110. }
  111. }
  112. return col2method;
  113. }

exportSQL method程式碼有點長,在程式碼內有寫入六個註解說明,大致講解其下的片段

code主要是做什麼事!

1. 取得bean field map getMethod

透過getGetterMethod會組出JavaBean的成員變數對應它的getter method,目的可用在

如delete from table where column1 = value1 and column2 = value2;

可以透過欄位名稱查出它的getter method,再利用java reflection得到value1, value2的值

2. 組出delete指令的條件

目的先組出delete from table where column1 = :column1 and column2 = :column2;

重點在於:column1,如此一來才知道該字串與哪個欄位有關

在每一個JavaBean內由於繼承PrimaryBean,因此需定義自行的getPkeys method,來確認

哪些欄位為primary key,如此一來才知道要刪除該筆資料時的條件欄位!!

  1. public class IncomeRecord extends PrimaryBean{
  2. private String year;
  3. private String month;
  4. private String id;
  5. private String source;
  6. private BigDecimal income;
  7. public String getYear() {
  8. return year;
  9. }
  10. public void setYear(String year) {
  11. this.year = year;
  12. }
  13. .....
  14. public String[] getPkeys() {
  15. String[] fields = { "year", "month", "id", "source"};
  16. return fields;
  17. }
  18. }

  1. public abstract class PrimaryBean {
  2. public abstract String[] getPkeys();
  3. }

3. 組出insert指令下的欄位名稱

目的先組出insert into table (column1, column2, ...)

4. 資料走訪

bean = (PrimaryBean) clazz.newInstance();
PropertyUtils.copyProperties(bean, rows.get(j));

重點會將該筆record資料對應欄位拷貝至bean

PS. PrimaryBean為所有JavaBean的父類別

5. 走訪定義好的primary keys array,並且將delete條件下的指令改成值

將剛剛的delete from table where column1 = :column1 and column2 = :column2;下的

:column1 replace to 實際值

2016/04/09補充: 利用replace的方式有遇到一種情況,那就是當欄位名稱各自為:seq, :seq1

時,這樣的欄位名稱會互相影響,因此在replace時可多加一" "空白字串,而前面在append指

令時也須多一空白字串在and前面。

6. 組出insert指令的values的部分

目的組出insert into table (column1, column2, ...) values (value1, value2, ...);

最後得到的結果如下:

輸出指令

查詢資料

留言