場景:須要將從ODPS數倉中計算獲得的大額可疑交易信息導入到業務系統的mysql中供業務系統審覈。最簡單的方式是用阿里雲的組件自動進行數據同步了。可是本系統是開放是爲了產品化,要保證不一樣環境的可移植性,同時同步的表也就6個表,那麼就利用現有的基於jdbc的規則引擎工程來本身實現數據的同步。java
完整的工程代碼能夠參考個人github https://github.com/intsmaze/SqlAdaptermysql
查詢數據,將結果映射到javabean對象中 ps = conn.prepareStatement(select id,name,age from intsmaze); rs = ps.executeQuery(); Intsmaze intsmaze = null; while (rs.next()) { intsmaze = new Intsmaze(); intsmaze.setId(rs.getInt(1)); intsmaze.setName(rs.getString(2)); intsmaze.setAge(rs.getInt(3)); } 添加數據,將javabean對象字段映射到對應的表列 String sql = "insert into intsmaze(id,name,age) values (?,?,?)"; ps = conn.prepareStatement(sql); ps.setInt(1, intsmaze.getId()); ps.setString(2, intsmaze.getName()); ps.setInt(3, intsmaze.getAge()); ps.executeUpdate();
使用JDBC你們都會使用上面的方式進行開發,可是若是咱們的表的字段有50個,並且查詢不能使用 select * from 必須指定列名呢?添加數據不能使用insert into intsmaze values()必須指定插入的列名呢?你乾脆殺了我吧。git
50個字段你要作2次字段列名映射,稍有不慎就會將字段列名映射到錯誤的位置,致使最後數據錯誤,最可怕的是,還要編寫sql語句,若是後面有新增或刪除列名,那麼你又要去看一眼映射關係,看看是否影響到。這就是一種費力卻沒有技術含量的事情,並且還很容易出錯。下面就是咱們要作的各類映射,你真的很考驗個人眼神。github
result.getObjectByName("FieldName")
javabean.setFieldValue()
PreparedStatement.setString(number,FieldVale)
根據javabean自動生成insert,select語句,完成字段列名映射sql
當初開發時,一看到這麼多字段映射我煩躁不安,而後花了半天用反射把代碼從新編寫了下,後面有新的表要進行同步時,用一個工具類生成javabean的java文件,而後直接就在下面模板代碼中替換javabean類就完成了數據同步,整個操做10分鐘搞定,是否是很爽。數據庫
固然你能夠引入orm框架,可是除了hibernate框架,mybatis框架雖然免去了select和insert的映射,可是仍是要編寫前綴列名,並且我就一個小工程,我再引入ORM框架,麻不麻煩啊,有這時間還不如本身寫一寫。mybatis
public class ModelServer extends ApplicationServer { private static final Logger logger = LoggerFactory.getLogger(ExportBlockCustomerServer.class); private final static String SQL = "get_customer_infor"; private MysqlService<TestGroup> mysqlService = new MysqlService<TestGroup>(); @Override public String[] getPaths() { return new String[] { "com/hand/service/exe/blocktrade/blocktrade.xml" }; } private int date = 1; @Override public void addOptions(Options options) { options.addOption("date", true, "天數"); } @Override public void setupOptionValue(CommandLine cmd) { date = Integer.parseInt(cmd.getOptionValue("date", "1")); logger.debug("date is {}", date); } public void service() throws Exception { mysqlService.setMysqlDao(this.getMysqlDao()); AmlException exception = null; for (int i = 1; i <= date; i++) { String exeSql = (String) this.getSqlMap().get(SQL); Result result = this.getSqlAdapter().select(this.getDao(),"SELECT * from test_group");//向odps數據倉庫查詢數據,並導入到mysql中 String[] names = FilesNameUtils.getFiledName(new TestGroup());//獲得這個bean類的全部字段名稱,要保證bean類的字段名稱和數據庫表的列名一致 String insertSql = SqlUtils.getInsertSql("test_group", names);//組裝成insert into test_group (id,cny,d,party_id,age) values (?,?,?,?,?)語句 List<TestGroup> list = new ArrayList<TestGroup>(100); int number = 0; while (result.hasNext()) { result.next(); TestGroup br = (TestGroup) tableToBean(result, i,names);//將odps查詢的數據反射到TestGroup類中,不用反射見重載函數 list.add(br); number++; if (number % 100 == 0) { try { mysqlService.insert(insertSql, list, names); } catch (Exception e) { exception = new AmlException(e); } finally { list.clear(); } } } try { mysqlService.insert(insertSql, list, names); logger.info("insert data number is {}", number); } catch (Exception e) { logger.info("insert data number is {}", number); exception = new AmlException(e); } finally { result.close(); } } if (exception != null) { throw exception; } } public static void main(String[] args) throws Exception { ModelServer applicationServer = new ModelServer(); applicationServer.run(args); logger.info("execute sucess......"); System.exit(0); } private Object tableToBean(Result result, int i, String[] names) throws Exception { Class clazz = TestGroup.class; TestGroup testGroup = (TestGroup) clazz.newInstance(); for (int j = 0; j < names.length; j++) { Object object = result.getObjectByName(names[j]); if (object instanceof String) { Field f = clazz.getDeclaredField(names[j]); f.setAccessible(true); f.set(testGroup, object); } else if (object instanceof Date) { Field f = clazz.getDeclaredField(names[j]); f.setAccessible(true); f.set(testGroup, new java.sql.Date(((Date)object).getTime())); } else if (object instanceof Long) { Field f = clazz.getDeclaredField(names[j]); f.setAccessible(true); f.set(testGroup, object); } } return testGroup; } private Bigamountreport tableToBean(Result result, int i) throws SQLException { Bigamountreport bigamountreport = new Bigamountreport(); bigamountreport.setSeqno((String) result.getObjectByName("aml_id")); bigamountreport.setCustomerId((String) result .getObjectByName("party_id")); ......瘋狂的set操做return bigamountreport; } }
得到傳入對象的字段名稱的字符串數據,爲了拼接sql使用app
public static String[] getFiledName(Object o) { Field[] fields = o.getClass().getDeclaredFields(); String[] fieldNames = new String[fields.length]; for (int i = 0; i < fields.length; i++) { fieldNames[i] = fields[i].getName(); } return fieldNames; }
拼接insert的sql語句框架
public static String getInsertSql(String tableName ,String[] names) { String insertSql = StringUtils.join("insert into ",tableName ," (#{field_name}) values (#{field_value})"); String fieldName=""; String fieldValue=""; for(int j = 0; j < names.length; j++) { if(j==names.length-1) { fieldName=StringUtils.join(fieldName,names[j]); fieldValue=StringUtils.join(fieldValue,"?"); } else { fieldName=StringUtils.join(fieldName,names[j],","); fieldValue=StringUtils.join(fieldValue,"?",","); } } insertSql=insertSql.replace("#{field_name}", fieldName).replace("#{field_value}", fieldValue); logger.debug("the insert sql is :{}",insertSql); return insertSql; }
最重要的是assembleBeantoPS方法,用於根據映射字段列名ide
public class MysqlService<T> { private static final Logger logger = LoggerFactory.getLogger(MysqlService.class); private MysqlDao mysqlDao; public void assembleBeantoPS(PreparedStatement ps, int number, String FileName, Object bean) throws Exception { Type fileType = FilesNameUtils.getFieldType(FileName, bean);//根據屬性名稱返回字段類型 if (fileType == String.class) { ps.setString(number + 1, (String) FilesNameUtils.getFieldValueByName(FileName, bean)); } else if ("long".equals(fileType.toString()+"")) { ps.setLong(number + 1,(Long) FilesNameUtils.getFieldValueByName(FileName, bean)); } else if (fileType == Long.class) { ps.setLong(number + 1,(Long) FilesNameUtils.getFieldValueByName(FileName, bean)); } else if (fileType == Date.class) { ps.setDate(number + 1,new java.sql.Date(((Date)FilesNameUtils.getFieldValueByName(FileName, bean)).getTime())); } } /** * @author:YangLiu * @date:2017年12月25日 下午3:52:20 * @describe: */ public void insert(String sql, List<T> list, String[] names) throws Exception { boolean iserror=false; PreparedStatement ps = null; try { ps = mysqlDao.getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); for (int i = 0; i < list.size(); i++) { T bigamount = list.get(i); try{ for (int j = 0; j < names.length; j++) { assembleBeantoPS(ps, j, names[j], bigamount); } ps.executeUpdate(); }catch (Exception e) { iserror=true; logger.error("插入數據發生錯誤, occur {} ", e); logger.error("異常數據 {} ", bigamount); } } } catch (Exception e) { mysqlDao.getInstance().free(null, ps, mysqlDao.getConnection()); logger.error("the sql: {} occur {} ", sql, e); throw new AmlException("mysql創建鏈接時發生異常"); } finally { mysqlDao.getInstance().free(null, ps); if(iserror) { throw new AmlException("向mysql中導入數據時發生異常"); } } } /** * @deprecated * @author:YangLiu * @date:2017年12月25日 下午3:52:20 * @describe:SB寫法 */ public boolean insertBatchBigamountrecord(String sql, List<Bigamountrecord> list) throws Exception { PreparedStatement ps = null; try { // dseqno,transOrgId,policyNo,antPolicyNo,periodPrem,transactionAmountCny,transactionAmountUsd ps = mysqlDao.getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); for (int i = 0; i < list.size(); i++) { Bigamountrecord bigamountrecord = list.get(i); int j = 1; ps.setString(j++, bigamountrecord.getDseqno()); ps.setString(j++, bigamountrecord.getTransOrgId()); ps.setString(j++, bigamountrecord.getPolicyNo()); ......瘋狂的set操做 ps.addBatch(); } ps.executeBatch(); } catch (SQLException e) { mysqlDao.getInstance().free(null, ps, mysqlDao.getConnection()); return false; } finally { mysqlDao.getInstance().free(null, ps); } return true; } }