自動分表
阿新 • • 發佈:2018-12-13
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Date; public abstract class AIPkCountSplitTable<T> { public abstract T mapRsRow(ResultSet rs) throws SQLException; public abstract boolean createTable(Connection con, int autokeyStart) throws SQLException; private String tableName; private String pkColName; private int splitCount; public String getTableName() { return tableName; } public void setTableName(String tableName) { this.tableName = tableName; } public String getPkColName() { return pkColName; } public void setPkColName(String pkColName) { this.pkColName = pkColName; } public int getSplitCount() { return splitCount; } public void setSplitCount(int splitCount) { this.splitCount = splitCount; } private void setStmtArg(PreparedStatement prepStatement, Object[] params) throws SQLException { if(null == params) return; for(int i = 0; i < params.length; ++i) { Object param = params[i]; if (param instanceof Integer) { int value = ((Integer) param).intValue(); prepStatement.setInt(i + 1, value); } else if (param instanceof String) { String s = (String) param; prepStatement.setString(i + 1, s); } else if (param instanceof Double) { double d = ((Double) param).doubleValue(); prepStatement.setDouble(i + 1, d); } else if (param instanceof Float) { float f = ((Float) param).floatValue(); prepStatement.setFloat(i + 1, f); } else if (param instanceof Long) { long l = ((Long) param).longValue(); prepStatement.setLong(i + 1, l); } else if (param instanceof Boolean) { boolean b = ((Boolean) param).booleanValue(); prepStatement.setBoolean(i + 1, b); } else if (param instanceof Date) { Date d = (Date) param; prepStatement.setDate(i+1, new java.sql.Date(d.getTime())); } } } private boolean renameTable(Connection con, int tbIdx) throws SQLException{ String newTableName = tableName + tbIdx; String sql = "RENAME TABLE " + tableName + " TO " + newTableName; Statement st = con.createStatement(); st.executeUpdate(sql); return true; } private int selectMaxId(Connection con) throws SQLException{ int maxId = -1; String sql = "SELECT MAX("+ pkColName +") FROM " + tableName; PreparedStatement st = con.prepareStatement(sql); ResultSet rs = st.executeQuery(sql); if(rs.next()) maxId = rs.getInt(1); rs.close(); return maxId; } private int selectCount(Connection con, String tbname, String sqlWhere, Object[] args) throws SQLException { int cnt = 0; String sql = "SELECT COUNT(*) FROM " + tbname + " " + sqlWhere; PreparedStatement st = con.prepareStatement(sql); this.setStmtArg(st, args); ResultSet rs = st.executeQuery(sql); if(rs.next()) cnt = rs.getInt(1); rs.close(); return cnt; } public boolean deleteById(Connection con, int id) throws SQLException { try{ String sql = "DELETE FROM " + tableName + " WHERE " + pkColName + "=?"; PreparedStatement st = con.prepareStatement(sql); st.setInt(1, id); if(1 == st.executeUpdate()) return true; }catch(SQLException ex){} int idx = id / splitCount; String acTableName = this.tableName + idx; String sql2 = "DELETE FROM " + acTableName + " WHERE " + pkColName + "=?"; PreparedStatement st2 = con.prepareStatement(sql2); st2.setInt(1, id); if(1 == st2.executeUpdate()) return true; return false; } // where xxx=? and yyy=? public int delete(Connection con, String sqlWhere, Object[] args) throws SQLException { int nupdate = 0; int maxId = selectMaxId(con); int tableIdx = maxId / splitCount; ++tableIdx; for(int i = 0 ; i < tableIdx; ++i){ String subTableName = ""; if(i == tableIdx-1) subTableName = this.tableName; else subTableName = this.tableName + i; String sql = "DELETE FROM " + subTableName + " " + sqlWhere; PreparedStatement st = con.prepareStatement(sql); this.setStmtArg(st, args); nupdate += st.executeUpdate(); } return nupdate; } private boolean innerInsert(Connection con, PreparedStatement insStmt, T value, PreparedStmtParamSetter<T> paramSetter) throws SQLException { int id = -1; paramSetter.setParams(insStmt, value); if(1 == insStmt.executeUpdate()){ ResultSet keys = insStmt.getGeneratedKeys(); if(keys.next()){ Integer generatedId = keys.getInt(1); id = generatedId.intValue(); }else{ return false;} }else{ return false;} // 達到分表靈界先刪除插入成功的,從舊錶中刪除,生成新表在插入 // 保證新建的表中有一條資料,select Max(pk) 才管用 if(0 == id % this.splitCount){ String delSql = "DELETE FROM " + this.tableName + " WHERE " + this.pkColName + "=" + id; Statement delStmt = con.createStatement(); if(1 != delStmt.executeUpdate(delSql)) return false; // 從0開始命名錶,才能用 id/splitCount直接做表名索引 1999/2000 = 0, 2000/2000=1,2000在表1中 int tbIdx = id / splitCount - 1 ; renameTable(con, tbIdx); createTable(con, id); paramSetter.setParams(insStmt, value); if(1 == insStmt.executeUpdate()){ ResultSet keys = insStmt.getGeneratedKeys(); if(keys.next()){ Integer generatedId = keys.getInt(1); id = generatedId.intValue(); }else{ return false;} }else{ return false;} } paramSetter.setId(value, id); return true; } //" (col1, col2) values(?, 1,?)" public boolean insert(Connection con, String sql, T value, PreparedStmtParamSetter<T> paramSetter) throws SQLException{ String insSql = "INSERT INTO " + this.tableName + sql; PreparedStatement insStmt = con.prepareStatement(insSql,Statement.RETURN_GENERATED_KEYS); return innerInsert(con, insStmt, value, paramSetter); } public boolean insertBatch(Connection con, String sql, List<T> values, PreparedStmtParamSetter<T> paramSetter) throws SQLException { if(null == values || values.size() == 0) return true; String insSql = "INSERT INTO " + this.tableName + sql; PreparedStatement insStmt = con.prepareStatement(insSql,Statement.RETURN_GENERATED_KEYS); for(int i = 0; i < values.size(); ++i) { T value = values.get(i); if(!innerInsert(con, insStmt, value, paramSetter)) return false; } return true; } private List<T> innerSelectAll(Connection con, String sqlWhere, Object[] args, int startTbIdx) throws SQLException { // 5999/2000 = 2,以前有0,1兩張表 int maxId = selectMaxId(con); int tableIdx = maxId / splitCount; ++tableIdx; // 加成3,表名索引為2,就是當前表了,便於在一個迴圈中處理完 List<T> elems = new ArrayList<T>(); for(int i = startTbIdx ; i < tableIdx; ++i){ String subTableName = ""; if(i == tableIdx-1) subTableName = this.tableName; else subTableName = this.tableName + i; String sql = "SELECT * FROM " + subTableName + " " + sqlWhere; PreparedStatement st = con.prepareStatement(sql); this.setStmtArg(st, args); ResultSet rs = st.executeQuery(); while(rs.next()){ elems.add(mapRsRow(rs)); } rs.close(); } return elems; } // where xx=? public List<T> select(Connection con, String sqlWhere, Object[] args) throws SQLException { return innerSelectAll(con, sqlWhere, args, 0); } // 15 30 20 45 public List<T> selectPage(Connection con, String sqlWhere, Object[] args, int offset, int limit) throws SQLException { int maxId = selectMaxId(con); int tableIdx = maxId / splitCount; ++tableIdx; List<T> elems = new ArrayList<T>(); int startTbIdx = tableIdx; int offsetCnt = 0; // 找到偏移起始表索引 if(0 != offset){ for(int i = 0 ; i < tableIdx; ++i){ String subTableName = ""; if(i == tableIdx-1) subTableName = this.tableName; else subTableName = this.tableName + i; int cnt = this.selectCount(con, subTableName, sqlWhere, args); offsetCnt += cnt; // 累計條數,0-i個表中的條數超過offset,說明要從第i個表開始偏移 if(offsetCnt > offset){ // offset 減去 0到i-1的總條數,就是在當前表中的偏移 offset = offset - (offsetCnt-cnt); startTbIdx = i; break; } } } if(startTbIdx >= tableIdx) return elems; if(-1 == limit) return innerSelectAll(con, sqlWhere, args, startTbIdx); int newLimit = limit; for(int i = startTbIdx ; i < tableIdx; ++i){ String subTableName = ""; if(i == tableIdx-1) subTableName = this.tableName; else subTableName = this.tableName + i; String sql = "SELECT * FROM " + subTableName + " " + sqlWhere + "LIMIT " + offset + "," + newLimit; PreparedStatement st = con.prepareStatement(sql); this.setStmtArg(st, args); ResultSet rs = st.executeQuery(); while(rs.next()){ elems.add(mapRsRow(rs)); --newLimit; } rs.close(); offset = 0; // 只是第一張需要偏移 if(newLimit <= 0) // never less than break; } return elems; } public T selectById(Connection con, int id) throws SQLException{ T elem = null; String sql = "SELECT * FROM " + this.tableName + " WHERE " + this.pkColName + "=?"; PreparedStatement st = con.prepareStatement(sql); st.setInt(1, id); ResultSet rs = st.executeQuery(); if(rs.next()) elem = mapRsRow(rs); rs.close(); if(null != elem) return elem; int idx = id / splitCount; String acTableName = this.tableName + idx; String sql2 = "SELECT * FROM " + acTableName + " WHERE " + this.pkColName + "=?"; PreparedStatement st2 = con.prepareStatement(sql2); st2.setInt(1, id); ResultSet rs2 = st2.executeQuery(); if(rs2.next()) elem = mapRsRow(rs2); rs2.close(); return elem; } // set xx=?, yy=? public boolean updateById(Connection con, String setSql, Object[] args, int id) throws SQLException { try{ String sql1 = "UPDATE " + this.tableName + " " + setSql + " WHERE " + this.pkColName + "=?"; PreparedStatement st1 = con.prepareStatement(sql1); this.setStmtArg(st1, args); st1.setInt(args.length+1, id); if(1 == st1.executeUpdate()) return true; }catch(SQLException e){} int idx = id / splitCount; String acTableName = this.tableName + idx; String sql2 = "UPDATE " + acTableName + " "+ setSql + " WHERE " + this.pkColName + "=?"; PreparedStatement st2 = con.prepareStatement(sql2); this.setStmtArg(st2, args); st2.setInt(args.length+1, id); if(1 == st2.executeUpdate()) return true; return false; } // set xx=?, yy =? where cc=? and zz=2 public int update(Connection con, String setSql, Object[] args) throws SQLException { int nupdate = 0; int maxId = selectMaxId(con); int tableIdx = maxId / splitCount; ++tableIdx; for(int i = 0 ; i < tableIdx; ++i){ String subTableName = ""; if(i == tableIdx-1) subTableName = this.tableName; else subTableName = this.tableName + i; String sql = "UPDATE " + subTableName + " " + setSql; PreparedStatement st = con.prepareStatement(sql); this.setStmtArg(st, args); nupdate += st.executeUpdate(); } return nupdate; } }