三種批量增加的性能分析

4年前 (2021-07-05)閱讀1126回復0
弘凌寒
弘凌寒
  • 管理員
  • 發(fā)消息
  • 注冊排名3256
  • 經(jīng)驗值50
  • 級別管理員
  • 主題10
  • 回復0
樓主
印刷廠(chǎng)直印加工●彩頁(yè)1000張只需要69元●名片5元每盒-更多產(chǎn)品印刷報價(jià)?聯(lián)系電話(huà):138-1621-1622(微信同號)

  最近在深入學(xué)習hibernate,在進(jìn)行批量操作時(shí),發(fā)現hibernate批量操作性能非常低.于是就想找一個(gè)性能較高的方法,在對jdbc、jdbcTemplate、hibernate進(jìn)行測試后,發(fā)現jdbc的執行效率是最高的,jdbcTemplate也很相近,hibernate就不考慮了,慘不忍睹啊.下面把代碼寫(xiě)出來(lái),希望大家批評指正.

  首先domain對象.在這里使用的注解的方式,都是比較新的版本.

  User.java

  1 package com.bao.sample.s3h4.domain; 2 3 import javax.persistence.Column; 4 import javax.persistence.Entity; 5 import javax.persistence.GeneratedValue; 6 import javax.persistence.GenerationType; 7 import javax.persistence.Id; 8 import javax.persistence.Table; 9 10 import com.bao.sample.base.domain.BaseDomain;11 12 @Entity13 @Table(name = "t_user")14 public class User extends BaseDomain {15 16 private static final long serialVersionUID = 1L;17 private int id;18 private String username;19 private String password;20 21 /**22 * @Description 注解最好標記在get方法上.注意:采用一致的標記方式,注解是以Id的標記方式為準的,如果標記在get方法上,則忽略property上的注解.23 * @return24 */25 @Id26 @GeneratedValue(strategy = GenerationType.IDENTITY)27 public int getId() {28 return id;29 }30 31 public void setId(int id) {32 this.id = id;33 }34 35 @Column(nullable = false)36 public String getUsername() {37 return username;38 }39 40 public void setUsername(String username) {41 this.username = username;42 }43 44 @Column(nullable = false)45 public String getPassword() {46 return password;47 }48 49 public void setPassword(String password) {50 this.password = password;51 }52 53 public User() {54 super();55 }56 57 public User(int id, String username, String password) {58 super();59 this.id = id;60 this.username = username;61 this.password = password;62 }63 64 }

  接下來(lái)是Dao接口,繼承一個(gè)BaseDao接口.

  UserBatchDao

  1 package com.bao.sample.s3h4.dao; 2 3 import java.util.List; 4 5 import com.bao.sample.base.dao.BaseDao; 6 import com.bao.sample.s3h4.domain.User; 7 8 public interface UserBatchDao extends BaseDao<User> { 9 10 /**11 * @Description 批量增加操作12 * @return -1:操作失敗;0:執行正常;>0:執行成功的數目13 */14 public int batchAddUsingJdbc(List<User> users);15 16 public int batchAddUsingHibernate(List<User> users);17 18 public int batchAddUsingJdbcTemplate(List<User> users);19 20 }

  UserBatchDao的實(shí)現:

  UserBatchDaoImpl

021yin.com 021yin.com mit(); 59 conn.setAutoCommit(true); 60 61 } catch (SQLException e) { 62 if (conn != null) { 63 try { 64 conn.rollback(); 65 } catch (SQLException e1) { 66 e1.printStackTrace(); 67 } 68 } 69 } finally { 70 if (pstmt != null) { 71 try { 72 pstmt.close(); 73 } catch (SQLException e) { 74 e.printStackTrace(); 75 } 76 } 77 78 if (conn != null) { 79 try { 80 conn.close(); 81 } catch (SQLException e) { 82 e.printStackTrace(); 83 } 84 } 85 } 86 87 return result; 88 } 89 90 /** 91 * 執行10W條記錄,大致耗時(shí)131203ms,大致是jdbc或jdbcTemplate的10倍. 92 */ 93 @Override 94 // @Transactional(noRollbackFor = RuntimeException.class) 95 @Transactional 96 public int batchAddUsingHibernate(List<User> users) { 97 98 Session session = this.getSession(); 99 100 for (int i = 0; i < users.size(); i++) {101 102 session.save(users.get(i));103 // 添加20條以后,強制入庫104 // clear()清空緩存105 // postgres數據庫的隔離級別是已提交讀(Read committed),106 // 所以flush以后,數據看不到,只有commit后才能看到數據,107 // 如果失敗,rollback,前面的flush的數據不會(huì )入庫108 if (i % 20 == 0) {109 session.flush();110 session.clear();111 }112 }113 114 return 0;115 }116 117 /**118 * 執行10W條記錄,大致耗時(shí)15671ms119 */120 // @Transactional(noRollbackFor = RuntimeException.class)121 @Transactional122 public int batchAddUsingJdbcTemplate(List<User> users) {123 124 String sql = "insert into t_user (username,password) values (?,?)";125 126 final List<User> tempUsers = users;127 final int count = users.size();128 129 BatchPreparedStatementSetter pss = new BatchPreparedStatementSetter() {130 // 為prepared statement設置參數。這個(gè)方法將在整個(gè)過(guò)程中被調用的次數131 public void setValues(PreparedStatement pstmt, int i) throws SQLException {132 133 int j = 1;134 pstmt.setString(j++, tempUsers.get(i).getUsername());135 pstmt.setString(j++, tempUsers.get(i).getPassword());136 }137 138 // 返回更新的結果集條數139 public int getBatchSize() {140 return count;141 }142 };143 144 jdbcTemplate.batchUpdate(sql, pss);145 146 return 0;147 }148 149 public JdbcTemplate getJdbcTemplate() {150 return jdbcTemplate;151 }152 153 }

  外圍的框架沒(méi)有附上,有需要可以留言,我提供打包下載.

  另,批量增加可以改造為泛型

  BaseDaoImpl

021yin.com /tingyuxuan007/archive/2012/08/14/ meta.getPropertyValue(temp.get(i), properties[j]);28 jdbcUtils.setParameter(fieldValue, pstmt, j + 1, false);29 }30 }31 32 // 返回更新的結果集條數33 public int getBatchSize() {34 return count;35 }36 };37 38 jdbcTemplate.batchUpdate(sql.toString(), pss);39 }

  需要用到兩個(gè)工具類(lèi):

  JdbcUtils

021yin.com 021yin.com 021yin.com 021yin.com 021yin.com 021yin.com 021yin.com 021yin.com 021yin.com 021yin.com /tingyuxuan007/archive/2012/08/14/ (Object[]) fieldValue;42 StringBuffer sb = new StringBuffer();43 for (int j = 0; j < arrayValue.length; j++) {44 sb.append(arrayValue[j]).append("、");45 }46 ps.setString(index, sb.deleteCharAt(sb.length() - 1).toString());47 } else {48 ps.setObject(index, fieldValue, Types.NUMERIC);49 }50 }51 52 }

  MetadataUtils

  1 package com.bao.sample.base.util.dao; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6 7 import org.hibernate.cfg.Configuration; 8 import org.hibernate.mapping.Column; 9 import org.hibernate.mapping.PersistentClass; 10 import org.hibernate.mapping.PrimaryKey; 11 import org.hibernate.mapping.Property; 12 import org.springframework.stereotype.Repository; 13 14 @Repository("metadataUtils") 15 public class MetadataUtils { 16 17 /** 18 * @Description 獲取PersistentClass對象 19 * @param clazz 20 * @return 21 */ 22 public PersistentClass getPersistentClass(Class<?> clazz) { 23 24 PersistentClass result = null; 25 Configuration config = new Configuration().addAnnotatedClass(clazz);// .addAnnotatedClass(ClassUAO.class); 26 27 config.buildMappings();// 此步不可少,如果沒(méi)有創(chuàng )建,將報空指針 28 29 result = config.getClassMapping(clazz.getName()); 30 return result; 31 } 32 33 /** 34 * @Description 獲取實(shí)體對應的表名 35 * @param clazz 36 * @return 37 */ 38 public String getTableName(Class<?> clazz) { 39 return getPersistentClass(clazz).getTable().getName(); 40 } 41 42 /** 43 * @Description 獲取實(shí)體對應表的主鍵字段名稱(chēng) 44 * @param clazz 45 * @return 46 */ 47 public PrimaryKey getPrimaryKey(Class<?> clazz) { 48 return getPersistentClass(clazz).getTable().getPrimaryKey(); 49 } 50 51 /** 52 * @Description 獲取實(shí)體對應表的主鍵字段名稱(chēng),只適用于唯一主鍵的情況 53 * @param clazz 54 * @return 55 */ 56 public String getUniqueKeyName(Class<?> clazz) { 57 return getPrimaryKey(clazz).getColumn(0).getName(); 58 } 59 60 /** 61 * @Description 通過(guò)實(shí)體類(lèi)和屬性,獲取實(shí)體類(lèi)屬性對應的表字段名稱(chēng) 62 * @param clazz 63 * @param propertyName 64 * @return 65 */ 66 public String getColumnName(Class<?> clazz, String propertyName) { 67 68 Property property = getPersistentClass(clazz).getProperty(propertyName); 69 Iterator<?> it = property.getColumnIterator(); 70 if (it.hasNext()) { 71 Column column = (Column) it.next(); 72 return column.getName(); 73 } 74 return null; 75 } 76 77 /** 78 * @Description 獲取實(shí)體類(lèi)對應的表所有字段名稱(chēng) 79 * @param clazz 80 * @param primaryKeyFlag 81 * true:包括主鍵;false:不包括主鍵 82 * @return 83 */ 84 public List<String> getAllColumns(Class<?> clazz, boolean primaryKeyFlag) { 85 List<String> columnNames = new ArrayList<String>(); 86 87 @SuppressWarnings("unchecked") 88 Iterator<Column> it = getPersistentClass(clazz).getTable().getColumnIterator(); 89 90 for (Iterator<Column> iterator = it; iterator.hasNext();) { 91 Column column = iterator.next(); 92 String columnName = column.getName(); 93 94 if (primaryKeyFlag) {// 得到這張表的所有字段名 95 columnNames.add(column.getName()); 96 } else if (!columnName.equals(getUniqueKeyName(clazz))) {// 得到這張表除主鍵外的字段名 97 columnNames.add(column.getName()); 98 } 99 // 兩者效果相同,不上上面方式較于理解100 // if (primaryKeyFlag || (!columnName.equals(getUniqueKeyName(clazz)))) {// 得到這張表的字段名101 // columnNames.add(column.getName());102 // }103 }104 105 return columnNames;106 107 }108 109 }

  經(jīng)過(guò)測試,發(fā)現向mysql數據庫增加1000條數據,直接使用jdbc耗時(shí)1609ms,使用jdbcTemplate耗時(shí)1625ms,使用泛型的批量增加耗時(shí)1719ms.開(kāi)發(fā)人員可以根據具體情況選擇.

0
0
收藏0
回帖

三種批量增加的性能分析 期待您的回復!

取消
載入表情清單……
載入顏色清單……
插入網(wǎng)絡(luò )圖片

取消確定

圖片上傳中
編輯器信息
提示信息