国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

香港云服务器
服務器之家 - 編程語言 - Java教程 - 通過反射注解批量插入數據到DB的實現方法

通過反射注解批量插入數據到DB的實現方法

2019-06-20 15:47Scub Java教程

今天小編就為大家分享一篇關于通過反射注解批量插入數據到DB的實現方法,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧

批量導入思路

最近遇到一個需要批量導入數據問題。后來考慮運用反射做成一個工具類,思路是首先定義注解接口,在bean類上加注解,運行時通過反射獲取傳入Bean的注解,自動生成需要插入DB的SQL,根據設置的參數值批量提交。不需要寫具體的SQL,也沒有DAO的實現,這樣一來批量導入的實現就和具體的數據庫表徹底解耦。實際批量執行的SQL如下:

1insert into company_candidate(company_id,user_id,card_id,facebook_id,type,create_time,weight,score) VALUES (?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE type=?,weight=?,score=?

第一步,定義注解接口

注解接口Table中定義了數據庫名和表名。RetentionPolicy.RUNTIME表示該注解保存到運行時,因為我們需要在運行時,去讀取注解參數來生成具體的SQL。

01@Documented
02@Retention(RetentionPolicy.RUNTIME)
03@Target(ElementType.TYPE)
04public @interface Table {
05  /**
06   * 表名
07   * @return
08   */
09  String tableName() default "";
10  /**
11   * 數據庫名稱
12   * @return
13   */
14  String dbName();
15}

注解接口TableField中定義了數據庫表名的各個具體字段名稱,以及該字段是否忽略(忽略的話就會以數據庫表定義默認值填充,DB非null字段的注解不允許出現把ignore注解設置為true)。update注解是在主鍵在DB重復時,需要更新的字段。

01@Documented
02@Retention(RetentionPolicy.RUNTIME)
03@Target(ElementType.FIELD)
04public @interface TableField {
05  /**
06   * 對應數據庫字段名稱
07   * @return
08   */
09  String fieldName() default "";
10  /**
11   * 是否是主鍵
12   * @return
13   */
14  boolean pk() default false;
15  /**
16   * 是否忽略該字段
17   * @return
18   */
19  boolean ignore() default false;
20  /**
21   * 當數據存在時,是否更新該字段
22   * @return
23   */
24  boolean update() default false;
25}

第二步,給Bean添加注解

給Bean添加注解(為了簡潔省略了import和set/get方法以及其他屬性),@TableField(fieldName = "company_id")表示companyId字段對應DB表的字段名為"company_id",其中updateTime屬性的注解含有ignore=true,表示該屬性值會被忽略。另外serialVersionUID屬性由于沒有@TableField注解,在更新DB時也會被忽略。

代碼如下:

01@Table(dbName = "company", tableName = "company_candidate")
02public class CompanyCandidateModel implements Serializable{
03 private static final long serialVersionUID = -1234554321773322135L;
04 @TableField(fieldName = "company_id")
05 private int companyId;
06 @TableField(fieldName = "user_id")
07 private int userId;
08 //名片id
09 @TableField(fieldName = "card_id")
10 private int cardId;
11 //facebookId
12 @TableField(fieldName = "facebook_id")
13 private long facebookId;
14  @TableField(fieldName="type", update = true)
15 private int type;
16 @TableField(fieldName = "create_time")
17 private Date createTime;
18 @TableField(fieldName = "update_time", ignore=true)
19 private Date updateTime;
20 // 權重
21  @TableField(fieldName="weight", update = true)
22 private int weight;
23 // 分值
24  @TableField(fieldName="score", update = true)
25 private double score;

第三步,讀取注解的反射工具類

讀取第二步Bean類的注解的反射工具類。利用反射getAnnotation(TableField.class)讀取注解信息,為批量SQL的拼接最好準備。

getTableBeanFieldMap()方法里生成一個LinkedHashMap對象,是為了保證生成插入SQL的field順序,之后也能按同樣的順序給參數賦值,避免錯位。getSqlParamFields()方法也類似,是為了給PreparedStatement設置參數用。

代碼如下:

01public class ReflectUtil {
02  /**
03   * <Class,<表定義Field名,Bean定義Field>>的map緩存
04   */
05  private static final Map<Class<?>, Map<string field="">> classTableBeanFieldMap = new HashMap<Class<?>, Map<string field="">>();
06  // 用來按順序填充SQL參數,其中存儲的Field和classTableBeanFieldMap保存同樣的順序,但數量多出ON DUPLICATE KEY UPDATE部分Field
07  private static final Map<Class<?>, List<field>> sqlParamFieldsMap = new HashMap<Class<?>, List<field>>();
08  private ReflectUtil(){};
09  /**
10   * 獲取該類上所有@TableField注解,且沒有忽略的字段的Map。
11   * <br />返回一個有序的LinkedHashMap類型
12   * <br />其中key為DB表中的字段,value為Bean類里的屬性Field對象
13   * @param clazz
14   * @return
15   */
16  public static Map<string field=""> getTableBeanFieldMap(Class<?> clazz) {
17   // 從緩存獲取
18   Map<string field=""> fieldsMap = classTableBeanFieldMap.get(clazz);
19   if (fieldsMap == null) {
20   fieldsMap = new LinkedHashMap<string field="">();
21      for (Field field : clazz.getDeclaredFields()) {// 獲得所有聲明屬性數組的一個拷貝
22       TableField annotation = field.getAnnotation(TableField.class);
23        if (annotation != null && !annotation.ignore() && !"".equals(annotation.fieldName())) {
24          field.setAccessible(true);// 方便后續獲取私有域的值
25         fieldsMap.put(annotation.fieldName(), field);
26        }
27  }
28      // 放入緩存
29      classTableBeanFieldMap.put(clazz, fieldsMap);
30   }
31   return fieldsMap;
32  }
33  /**
34   * 獲取該類上所有@TableField注解,且沒有忽略的字段的Map。ON DUPLICATE KEY UPDATE后需要更新的字段追加在list最后,為了填充參數值準備
35   * <br />返回一個有序的ArrayList類型
36   * <br />其中key為DB表中的字段,value為Bean類里的屬性Field對象
37   * @param clazz
38   * @return
39   */
40  public static List<field> getSqlParamFields(Class<?> clazz) {
41   // 從緩存獲取
42   List<field> sqlParamFields = sqlParamFieldsMap.get(clazz);
43   if (sqlParamFields == null) {
44   // 獲取所有參數字段
45     Map<string field=""> fieldsMap = getTableBeanFieldMap(clazz);
46   sqlParamFields = new ArrayList<field>(fieldsMap.size() * 2);
47     // SQL后段ON DUPLICATE KEY UPDATE需要更新的字段
48     List<field> updateParamFields = new ArrayList<field>();
49   Iterator<Entry<string field="">> iter = fieldsMap.entrySet().iterator();
50   while (iter.hasNext()) {
51    Entry<string field=""> entry = (Entry<string field="">) iter.next();
52    Field field = entry.getValue();
53    // insert語句對應sql參數字段
54    sqlParamFields.add(field);
55        // ON DUPLICATE KEY UPDATE后面語句對應sql參數字段
56        TableField annotation = field.getAnnotation(TableField.class);
57    if (annotation != null && !annotation.ignore() && annotation.update()) {
58    updateParamFields.add(field);
59    }
60   }
61   sqlParamFields.addAll(updateParamFields);
62      // 放入緩存
63   sqlParamFieldsMap.put(clazz, sqlParamFields);
64   }
65   return sqlParamFields;
66  }
67  /**
68   * 獲取表名,對象中使用@Table的tableName來標記對應數據庫的表名,若未標記則自動將類名轉成小寫
69   *
70   * @param clazz
71   * @return
72   */
73  public static String getTableName(Class<?> clazz) {
74    Table table = clazz.getAnnotation(Table.class);
75    if (table != null && table.tableName() != null && !"".equals(table.tableName())) {
76      return table.tableName();
77    }
78    // 當未配置@Table的tableName,自動將類名轉成小寫
79    return clazz.getSimpleName().toLowerCase();
80  }
81  /**
82   * 獲取數據庫名,對象中使用@Table的dbName來標記對應數據庫名
83   * @param clazz
84   * @return
85   */
86  public static String getDBName(Class<?> clazz) {
87    Table table = clazz.getAnnotation(Table.class);
88    if (table != null && table.dbName() != null) {
89      // 注解@Table的dbName
90      return table.dbName();
91    }
92    return "";
93  }

第四步,生成SQL語句

根據上一步的方法,生成真正執行的SQL語句。

1insert into company_candidate(company_id,user_id,card_id,facebook_id,type,create_time,weight,score) VALUES (?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE type=?,weight=?,score=?

代碼如下:

01public class SQLUtil {
02  private static final char COMMA = ',';
03  private static final char BRACKETS_BEGIN = '(';
04  private static final char BRACKETS_END = ')';
05  private static final char QUESTION_MARK = '?';
06  private static final char EQUAL_SIGN = '=';
07  private static final String INSERT_BEGIN = "INSERT INTO ";
08  private static final String INSERT_VALURS = " VALUES ";
09  private static final String DUPLICATE_UPDATE = " ON DUPLICATE KEY UPDATE ";
10  // 數據庫表名和對應insertupdateSQL的緩存
11  private static final Map<string string=""> tableInsertSqlMap = new HashMap<string string="">();
12  /**
13   * 獲取插入的sql語句,對象中使用@TableField的fieldName來標記對應數據庫的列名,若未標記則忽略
14   * 必須標記@TableField(fieldName = "company_id")注解
15   * @param tableName
16   * @param fieldsMap
17   * @return
18   * @throws Exception
19   */
20  public static String getInsertSql(String tableName, Map<string field=""> fieldsMap) throws Exception {
21   String sql = tableInsertSqlMap.get(tableName);
22   if (sql == null) {
23   StringBuilder sbSql = new StringBuilder(300).append(INSERT_BEGIN);
24   StringBuilder sbValue = new StringBuilder(INSERT_VALURS);
25   StringBuilder sbUpdate = new StringBuilder(100).append(DUPLICATE_UPDATE);
26   sbSql.append(tableName);
27   sbSql.append(BRACKETS_BEGIN);
28   sbValue.append(BRACKETS_BEGIN);
29   Iterator<Entry<string field="">> iter = fieldsMap.entrySet().iterator();
30   while (iter.hasNext()) {
31    Entry<string field=""> entry = (Entry<string field="">) iter.next();
32    String tableFieldName = entry.getKey();
33    Field field = entry.getValue();
34    sbSql.append(tableFieldName);
35    sbSql.append(COMMA);
36    sbValue.append(QUESTION_MARK);
37    sbValue.append(COMMA);
38    TableField tableField = field.getAnnotation(TableField.class);
39    if (tableField != null && tableField.update()) {
40    sbUpdate.append(tableFieldName);
41    sbUpdate.append(EQUAL_SIGN);
42    sbUpdate.append(QUESTION_MARK);
43    sbUpdate.append(COMMA);
44    }
45   }
46   // 去掉最后的逗號
47   sbSql.deleteCharAt(sbSql.length() - 1);
48   sbValue.deleteCharAt(sbValue.length() - 1);
49   sbSql.append(BRACKETS_END);
50   sbValue.append(BRACKETS_END);
51   sbSql.append(sbValue);
52   if (!sbUpdate.toString().equals(DUPLICATE_UPDATE)) {
53    sbUpdate.deleteCharAt(sbUpdate.length() - 1);
54    sbSql.append(sbUpdate);
55   }
56   sql = sbSql.toString();
57   tableInsertSqlMap.put(tableName, sql);
58   }
59    return sql;
60  }

第五步,批量SQL插入實現

從連接池獲取Connection,SQLUtil.getInsertSql()獲取執行的SQL語句,根據sqlParamFields來為PreparedStatement填充參數值。當循環的值集合到達batchNum時就提交一次。

代碼如下:

01/**
02 * 批量插入,如果主鍵一致則更新。結果返回更新記錄條數<br />
03 * @param dataList
04 *      要插入的對象List
05 * @param batchNum
06 *      每次批量插入條數
07 * @return 更新記錄條數
08 */
09public int batchInsertSQL(List<? extends Object> dataList, int batchNum) throws Exception {
10 if (dataList == null || dataList.isEmpty()) {
11 return 0;
12 }
13  Class<?> clazz = dataList.get(0).getClass();
14  String tableName = ReflectUtil.getTableName(clazz);
15  String dbName = ReflectUtil.getDBName(clazz);
16  Connection connnection = null;
17  PreparedStatement preparedStatement = null;
18  // 獲取所有需要更新到DB的屬性域
19  Map<string field=""> fieldsMap = ReflectUtil.getTableBeanFieldMap(dataList.get(0).getClass());
20  // 根據需要插入更新的字段生成SQL語句
21  String sql = SQLUtil.getInsertSql(tableName, fieldsMap);
22  log.debug("prepare to start batch operation , sql = " + sql + " , dbName = " + dbName);
23  // 獲取和SQL語句同樣順序的填充參數Fields
24  List<field> sqlParamFields = ReflectUtil.getSqlParamFields(dataList.get(0).getClass());
25  // 最終更新結果條數
26  int result = 0;
27  int parameterIndex = 1;// SQL填充參數開始位置為1
28  // 執行錯誤的對象
29  List<object> errorsRecords = new ArrayList</object><object>(batchNum);//指定數組大小
30  // 計數器,batchNum提交后內循環累計次數
31  int innerCount = 0;
32  try {
33    connnection = this.getConnection(dbName);
34    // 設置非自動提交
35    connnection.setAutoCommit(false);
36    preparedStatement = connnection.prepareStatement(sql);
37    // 當前操作的對象
38    Object object = null;
39    int totalRecordCount = dataList.size();
40    for (int current = 0; current < totalRecordCount; current++) {
41      innerCount++;
42      object = dataList.get(current);
43     parameterIndex = 1;// 開始參數位置為1
44     for(Field field : sqlParamFields) {
45     // 放入insert語句對應sql參數
46        preparedStatement.setObject(parameterIndex++, field.get(object));
47     }
48     errorsRecords.add(object);
49      preparedStatement.addBatch();
50      // 達到批量次數就提交一次
51      if (innerCount >= batchNum || current >= totalRecordCount - 1) {
52        // 執行batch操作
53        preparedStatement.executeBatch();
54        preparedStatement.clearBatch();
55        // 提交
56        connnection.commit();
57        // 記錄提交成功條數
58        result += innerCount;
59        innerCount = 0;
60        errorsRecords.clear();
61      }
62      // 盡早讓GC回收
63      dataList.set(current, null);
64    }
65    return result;
66  } catch (Exception e) {
67    // 失敗后處理方法
68    CallBackImpl.getInstance().exectuer(sql, errorsRecords, e);
69    BatchDBException be = new BatchDBException("batch run error , dbName = " + dbName + " sql = " + sql, e);
70    be.initCause(e);
71    throw be;
72  } finally {
73    // 關閉
74    if (preparedStatement != null) {
75     preparedStatement.clearBatch();
76      preparedStatement.close();
77    }
78    if (connnection != null)
79      connnection.close();
80  }
81}

最后,批量工具類使用例子

在mysql下的開發環境下測試,5萬條數據大概13秒。

1List<companycandidatemodel> updateDataList = new ArrayList<companycandidatemodel>(50000);
2// ...為updateDataList填充數據
3int result = batchJdbcTemplate.batchInsertSQL(updateDataList, 50);

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對服務器之家的支持.

延伸 · 閱讀

精彩推薦
344
主站蜘蛛池模板: 日韩一区二区在线观看 | |级毛片 | 日本丶国产丶欧美色综合 | 中国在线观看片 | 天天看天天爽 | 国内精品嫩模av私拍在线观看 | 日韩一区中文字幕 | 三级在线视频 | 国产裸体bbb视频 | 久久九 | 色黄网站| 91婷婷射 | 欧美日韩在线观看一区二区 | 久久国产一区二区 | 午夜精品一区二区三区免费视频 | 久久se精品一区精品二区 | 中文av在线播放 | 在线免费黄色网址 | 中文字幕在线视频观看 | 亚洲成av人影片在线观看 | 国产精品久久久久久久美男 | 欧美一级片在线观看 | 亚洲视频 欧美视频 | 色视频在线免费观看 | 欧美淫片 | 久久精品国产99国产 | 欧美 日韩 中文 | 国产午夜在线 | 国产综合一区二区 | 亚洲欧美一区二区三区国产精品 | 精品久久一区二区三区 | 亚洲狠狠爱 | 免费欧美 | 国产精品a久久久久 | 免费观看一级特黄欧美大片 | 国产噜噜噜噜噜久久久久久久久 | 久久久久久av | 99亚洲精品 | 国产精品久久久久无码av | 91精品国产高清一区二区三区 | 中国大陆高清aⅴ毛片 |