前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关联表多数据的批量insert (批量导入,测试19W条数据用时46秒)

关联表多数据的批量insert (批量导入,测试19W条数据用时46秒)

作者头像
微风-- 轻许--
发布2022-04-13 09:59:46
1.1K0
发布2022-04-13 09:59:46
举报
文章被收录于专栏:java 微风java 微风

一、业务需求 :作多个批量导入 ,根据业务不同,每条数据导入前作各种验证,

导入后提示验证失败的所有数据和原因。

二、思路: 1. 批量导入用 jdbc 直连数据库 addBatch方法实现 ,不走 Mybatis ,

2. 数据验证用 SQL 语句实现,不走 Mybatis ,

3. 创建临时表记录合格数据并导入正式数据库表

4. 创建临时表记录验证失败的数据,并最终返回

5. 由于业务需求批量导入时是要导入到 2 张主外键关联表 ,

所以一次性获取多个序列值以实现 2 表主外键的一致性。

6. 用 sessionID 区分表名和序列名,实现并发导入时数据无污染 。

三、代码:

代码语言:javascript
复制
 /**
	     * 跳转用户导入页面
	     * @param request
	     * @param session
	     * @return String 
	     */
	    @RequestMapping("userInfoImport")
	    public  String userInfoImport(HttpServletRequest request,HttpSession session){
	    	 return "op/usermgr/userInfoImport";
	    }
        @RequestMapping("userInfoExcelImport")
	    public  String userInfoExcelImport(HttpServletRequest request,HttpSession session,@RequestParam("excelPath")MultipartFile excelPath){
	    	
	    	Long start = System.currentTimeMillis();
	    	String backUrl = "../usermgr/initQuery.do";
	    	
	    	String[] excelTop = new String[]{"msisdn","custname","phoneType","creditamount","payType"}; // 表头
	   
             String tableName = session.getId().substring(0,8);
	    	// 定义-修改数据临时表 
	    	String updateListTemp ="CREATE TABLE updateListTemp"+tableName+"("
			    					+"id integer NOT NULL,"
			    					+"msisdn varchar2(12) NOT NULL,"
			    					+"custname varchar2(50) NOT NULL,"
			    					+"phoneType varchar2(50) NOT NULL,"
			    					+"creditamount varchar2(10) NOT NULL,"
									+"payType varchar2(50) NOT NULL)"
			    					+" tablespace ISMP pctfree 60 initrans 1 maxtrans 255 storage (initial 64K) ";
	    	
			String updateTemp_seq ="CREATE SEQUENCE updateTemp_seq"+tableName+""
				    			   +" INCREMENT BY 1" // 每次加1
				    			   +" START WITH 1 "  // 从1开始计数
				    			   +" NOMAXvalue "    // 不设置最大值
				    			   +" NOCYCLE " 	    // 一直累加,不循环
				    			   +" CACHE 10 "; 	    // 缓存10个
				    			   	    	
  			// 定义-新增数据临时表 
	    	String insertListTemp ="CREATE TABLE insertListTemp"+tableName+"("
				    		  +"id integer NOT NULL,"
				    		  +"msisdn varchar2(12) NOT NULL,"
		    				  +"custname varchar2(50) NOT NULL,"
		    				  +"phoneType varchar2(50) NOT NULL,"
		    				  +"creditamount varchar2(10) NOT NULL,"
							  +"payType varchar2(50) NOT NULL)"
			    			  +" tablespace ISMP pctfree 60 initrans 1 maxtrans 255 storage (initial 64K) ";
	    					 
			String insertTemp_seq ="CREATE SEQUENCE insertTemp_seq"+tableName+""
		    				 +" INCREMENT BY 1" // 每次加1
		    				 +" START WITH 1 "  // 从1开始计数
		    				 +" NOMAXvalue "   // 不设置最大值
		    				 +" NOCYCLE "	     // 一直累加,不循环
		    				 +" CACHE 10 "; 
	    	try {
	    		if(excelPath == null){
	    			OprResult.SetOprResult(request, "导入用户异常","导入用户失败,请选择要导入的内容 !", backUrl,OprResult.FAILURE);
	    			return "/op/oprResult";
	    		}
	    		
	    		// 解析得到的用户表集合
		        List<List<Object>> usrlist = ImportExcelUtil.getExcelList(excelPath.getInputStream(),excelTop);
				if(usrlist == null){
					OprResult.SetOprResult(request, "导入用户异常", "导入用户信息,数据表格不能有空值 !", backUrl,OprResult.FAILURE);
					return "/op/oprResult";
				}
				
				// 验证电话、宽带账号
				List<String> failMsisdnList = new ArrayList<String>();
				List<String> msiddn = new ArrayList<String>();
				for(int y =0; y<usrlist.size(); y++){
					if(String.valueOf(usrlist.get(y).get(2).toString()).equals("电话号码")){ // 验证电话号码
						if(!ValidateUtils.isMobile(usrlist.get(y).get(0).toString())){
							failMsisdnList.add(usrlist.get(y).get(0).toString());
							usrlist.remove(usrlist.get(y));
							y--;
						}
					}else{
						if(ValidateDhmp.validateUserMsisdn(usrlist.get(y).get(0).toString())){ // 验证宽带账号
							failMsisdnList.add(usrlist.get(y).get(0).toString());
							usrlist.remove(usrlist.get(y));
							y--;
						}
					}
				}
				
				int failsize = failMsisdnList.size();
				// 去重
				for(int y =0; y<usrlist.size(); y++){
					if(y<1){
						msiddn.add(usrlist.get(y).get(0).toString());
					}else{
						if(msiddn.contains(usrlist.get(y).get(0).toString())){
							usrlist.remove(usrlist.get(y));
							y--;
						}else{
							msiddn.add(usrlist.get(y).get(0).toString());
						}
					}
				}
				
				// 创建临时表和序列 -正确数据表、问题数据表
	  			BatchInsert.goSql(updateListTemp); 
	  			BatchInsert.goSql(updateTemp_seq); 
	  			BatchInsert.goSql(insertListTemp); 
	  			BatchInsert.goSql(insertTemp_seq); 
	  			
	  			// 导入sql:
	  			String insertSql ="insert into insertListTemp"+tableName+" values(updateTemp_seq"+tableName+".nextval,?,?,?,?,?)";
	  			BatchInsert.exeBatch(insertSql , usrlist); // 批量导入到新增数据表
	  			
	  			// 插入问题数据(用户已存在)-sql
	  			insertSql ="insert into updateListTemp"+tableName+" select updateTemp_seq"+tableName+".nextval,"
	  						+"temp.msisdn,temp.custname,temp.phoneType,temp.creditamount,temp.payType"
	  						+" from insertListTemp"+tableName+" temp where temp.msisdn in (select msisdn from usr_end_user )";
	  			BatchInsert.goSql(insertSql); 
	  			
	  			// 删除问题数据(用户已存在)-sql
	  			String deleteErrorSql ="delete from insertListTemp"+tableName+" where msisdn in (select msisdn from usr_end_user )";
	  			BatchInsert.goSql(deleteErrorSql);
	  			
	  			// 只修改数据
	  			String[] col = new String[] {"id","msisdn","custname","phoneType","creditamount","payType"};
	  			List<List<Object>> failList = BatchInsert.selectToList("select * from updateListTemp"+tableName,col);
	  			failsize += failList.size();
      			StringBuffer failbuffer = new StringBuffer();
	  			failbuffer.append("<br><br>一、请注意保留以下信息,此提示只显示一次  !");
	  			failbuffer.append("<br><br>二、失败数据,");
	  			if(failList != null && failList.size() > 0){
	  				failbuffer.append("<br><br> 账号:"); 
		  			for(List<Object> fail :failList){
		  				failbuffer.append(fail.get(1)+", "); 
		  			}
		  			failbuffer.append("<br><br>原因:用户已经存在,请直接编辑。"); 
	  			}
	  			
      			for(List<Object> fail : failList){
      				fail.remove(0);
      			}
      			usrlist.removeAll(failList); // 求差 
      			
      			if(failMsisdnList != null && failMsisdnList.size() > 0){
      				failbuffer.append("<br><br> 账号:");
      				for(String msisdn :failMsisdnList){ // 电话、宽带账号格式不对
          				failbuffer.append(msisdn+", "); 
          			}
          			failbuffer.append("<br><br>原因:电话号码或宽带账号格式不对。"); 
      			}
      			
      			String squence ="select USR_CUSTOMER_SEQ.nextval cust_id from (select 1 from all_objects where rownum <= "+usrlist.size()+")";
      			List<String> squenceList = BatchInsert.selectSql(squence);
      			
				// 拼装customList
      			List<List<Object>> customList  = new ArrayList<List<Object>>(); 
				Long maxCode = (long) 0; 
				String maxCustCode = "";
				for(int i=0; i< usrlist.size(); i++){
					List<Object> custom = new ArrayList<Object>();
					custom.add(squenceList.get(i)); // usr_customer表的序列值
					custom.add(usrlist.get(i).get(1)); // custname
					customList.add(custom);
					if(i<2){
						maxCustCode = usermgrService.findMaxCustCode();
						if(maxCustCode!= null && !"".equals(maxCustCode)){
				        	maxCode = Long.parseLong(maxCustCode)+1;
				        	custom.add(String.valueOf(maxCode));
				        }else{
				        	custom.add(String.valueOf("10000000")); // setCustCode
				        }
					}else{
						maxCode += 1;
						custom.add(String.valueOf(maxCode)); // setCustCode
					}
				}
				
				// 客户表insert Sql:
				String customerSql ="insert into usr_customer(cust_id,cust_name,cust_code)values(?,?,?)";
					BatchInsert.exeBatch(customerSql , customList); // 插入客户表
				
				// 用户表insert Sql:
				String userSql ="insert into usr_end_user(user_id,cust_id,msisdn,creditamount,phone_type,user_payment_type,"
							  +"user_stat,user_prvc_code,user_trade_type,is_black,is_active,language,user_level,is_test_user,"
							  +"consume_limit,limit_tips,dayfee,monthfee,is_realname)"
							  +" values(usr_end_user_seq.nextVal, ?,?,?,?,? ,?,?,?,?,? ,?,?,?,?,? ,?,?,?) ";
				
				// 拼装userList
				List<List<Object>> userList  = new ArrayList<List<Object>>(); 
				for(int i=0;i<usrlist.size();i++){
					List<Object> user = new ArrayList<Object>();
					user.add(squenceList.get(i)); //cust_id
					user.add(usrlist.get(i).get(0));//msisdn
					user.add(String.valueOf(usrlist.get(i).get(3))); //creditamount
					if("电话号码".equals(String.valueOf(usrlist.get(i).get(2)))){
						user.add("1"); //phone_type
					}else if("宽带号码".equals(String.valueOf(usrlist.get(i).get(2)))){
						user.add("3"); //phone_type
					}
					if("后付费用户".equals(String.valueOf(usrlist.get(i).get(4)))){
						user.add("1"); //user_payment_type
					}else  if("预付费用户".equals(String.valueOf(usrlist.get(i).get(4)))){
						user.add("2"); //user_payment_type
					}
					
					user.add("1"); //user_stat
					user.add("08"); //user_prvc_code
					user.add("0"); //user_trade_type-账户行业类型:默认为家庭
					user.add(0); //is_black
					user.add(0); //is_active
					user.add("01"); //LANGUAGE
					user.add("01"); //USER_LEVEL
					user.add(0); //IS_TEST_USER
					user.add("'3|0|0|0|0|0|0|0|0'");//consumeLimit
					user.add("'0|0|0'");//LIMIT_TIPS
					user.add("0"); //DAYFEE
					user.add("0"); //MONTHFEE
					user.add("1"); //IS_REALNAME
					userList.add(user);
				}
				BatchInsert.exeBatch(userSql ,userList); // 正式插入用户表
      			
				/*try{
					//日志
			        log(session, UsermgrServiceImpl.class,"exeBatch", "批量新增用户");
					log(session, UsermgrServiceImpl.class,"exeBatch", "批量新增客户");
				}catch (Exception e){
				   e.printStackTrace();
				}*/

		    	// 删除临时表
	  			String dropSql ="drop table updateListTemp"+tableName;
	  			String dropSql2 ="drop sequence updateTemp_seq"+tableName;
	  			String dropSql3 ="drop table insertListTemp"+tableName;
	  			String dropSql4 ="drop sequence insertTemp_seq"+tableName;
	  			BatchInsert.goSql(dropSql);
	  			BatchInsert.goSql(dropSql2);
	  			BatchInsert.goSql(dropSql3);
	  			BatchInsert.goSql(dropSql4);
	  			
				Long end = System.currentTimeMillis();
				System.out.println();
				System.out.println("导入用户信息表"+userList.size()+"条数据,总用时==============================:"+ (end-start)/1000+"秒 。");
				System.out.println();
				
				if(failsize > 0){
					OprResult.SetOprResult(request, "提示信息", "部分用户信息导入失败,如下:"+failbuffer, backUrl, OprResult.FAILURE);
				}else{
					OprResult.SetOprResult(request, "提示信息", "用户信息导入成功", backUrl, OprResult.SUCCESS);
				}
				
			} catch (EncryptedDocumentException e) {
				e.printStackTrace();
				OprResult.SetOprResult(request, "导入用户异常","导入用户信息失败,请刷新页面后重试 !", backUrl,OprResult.FAILURE);
			} catch (InvalidFormatException e) {
				e.printStackTrace();
				OprResult.SetOprResult(request, "导入用户异常","导入用户信息失败,请刷新页面后重试 !", backUrl,OprResult.FAILURE);
			} catch (IOException e) {
				e.printStackTrace();
				OprResult.SetOprResult(request, "导入用户异常","导入用户信息失败,请刷新页面后重试 !", backUrl,OprResult.FAILURE);
			} catch (Exception e) {
				e.printStackTrace();
				OprResult.SetOprResult(request, "导入用户异常","导入用户信息失败,请刷新页面后重试 !", backUrl,OprResult.FAILURE);
			}
	    	return "/op/oprResult";
	    }
代码语言:javascript
复制
	/**
	 * 判断是不是合法手机号码
	 * @param mobile
	 * @return
	 */
	public static boolean isMobile(String mobile) {
		Pattern pattern = Pattern.compile("^((13[0-9])|(15[0-9])|(18[0-9]))\\d{8}$");
		return pattern.matcher(mobile).matches();
	}

批量导入、表格解析工具类:

代码语言:javascript
复制
package com.mc.common.util;

import java.sql.Connection;

import java.sql.DriverManager;
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 com.zznode.ismp.mc.common.MspException;

/**
 * 批量导入工具类
 * @author JiangYu
 */
public class BatchInsert {
	
//    private static String url="jdbc:oracle:thin:@127.0.0.1:9521:orcl";    
	private static String url = MspConfiguration.getInstance().getParaValue("DBUrl");  
    // oracle数据库用户名     
    private static String user = MspConfiguration.getInstance().getParaValue("DBUser");    
    // oracle数据库密码     
    private static String password = MspConfiguration.getInstance().getParaValue("DBPassword");    
    public static Connection conn;    
    public static PreparedStatement ps;    
    public static ResultSet rs;    
    public static Statement st ;  
    
    public static Connection getConnection(){ //连接数据库的方法        
    	
        try {    
            Class.forName("oracle.jdbc.driver.OracleDriver"); //初始化驱动包         
            conn = DriverManager.getConnection(url, user, password);    
        } catch (Exception e) {    
            e.printStackTrace();    
        }  
        return conn;
    }    
    
     public static void main(String[] args) {   
    	 
        getConnection();    
        if(conn==null){    
            System.out.println("与oracle数据库连接失败!");    
        }else{    
            System.out.println("与oracle数据库连接成功!");   
            
        }    
     } 
     
     /**
      * 批量运行sql
      * @param con
      * @param sql
      * @param list
      */
     public static void exeBatch(String sql,List<List<Object>> list) throws Exception{
    	 
    	 try {
    		   StringBuffer sqlbuffer = new StringBuffer();
    		   sqlbuffer.append(sql);
    		   Connection con = getConnection();
    		   con.setAutoCommit(false);// 关闭事务自动提交
    		   final int batchSize = 1000; // 每满1000条数据运行一次
    		   int count = 0;
    		   Long startTime = System.currentTimeMillis();
    		   PreparedStatement pst = (PreparedStatement) con.prepareStatement(String.valueOf(sql));
    		   
    		   if(list != null && list.size() > 0){
    			   for (int i = 0; i < list.size(); i++) {
        			   for(int x =0;x<list.get(i).size();x++){
        				   pst.setObject(x+1,list.get(i).get(x));
        			   }
        			   pst.addBatch();// 把一个SQL命令加入命令列表
        			   if(++count % batchSize == 0 ){
        	   		       pst.executeBatch();
        	   		       count = 0;
        	   		   }
        		   }
    		   }
    		   
    		   pst.executeBatch();
    		   con.commit();
    		   pst.close();
    		   con.close();
    		   
    		   Long endTime = System.currentTimeMillis();
    		   System.out.println("单纯inserrt用时:" + (endTime - startTime));
    		   
		  } catch (Exception e) {
			  e.printStackTrace();
			  throw new MspException("网络不畅,请刷新页面后重试 !");
		  }
     } 
 
     // 解析数据表
     public static List<List<Object>> selectToList(String sql,String[] col){
        Connection conn = null;//定义为空值
        Statement stmt = null;
        ResultSet rs = null;
        conn = getConnection();
        List<Object> list = null;
        List<List<Object>> resultList = new ArrayList<List<Object>>(); 
        try {
         stmt = conn.createStatement();//创建一个Statement语句对象
         rs = stmt.executeQuery(sql);//执行sql语句
         while(rs.next()){
        	 list = new ArrayList<Object>();
        	 for(int i=0; i< col.length; i++){
        		 list.add(rs.getObject(col[i]));
        	 }
        	 resultList.add(list);
         }
     } catch (SQLException e) {
         e.printStackTrace();
     }finally{
    	 try {
			conn.close();
			stmt.cancel();
	    	rs.close();
		 }catch (SQLException e) {
			e.printStackTrace();
		 }
     }
	 return resultList;
   }
     
     // 执行增、删、改sql
     public static void goSql(String sql){
         Connection conn = null;//定义为空值
         Statement stmt = null;
         conn = getConnection();
	     try {    
	         stmt = conn.createStatement();    
	     } catch (SQLException e) {    
	         e.printStackTrace();    
	     }    
	     //4、执行语句    
	     try {    
	         stmt.executeUpdate(sql);    
	     } catch (SQLException e) {    
	         e.printStackTrace();    
	     }    
	     //5、关闭操作    
	     try {    
	             stmt.close();    
	             conn.close();    
	     } catch (SQLException e) {    
	             e.printStackTrace();    
	     }
    }
     
     // 单纯查询 
     public static List<String> selectSql(String sql){
        Connection conn = null;//定义为空值
        Statement stmt = null;
        ResultSet rs = null;
        conn = getConnection();
        List<String> list = new ArrayList<String>();
        try {
         stmt = conn.createStatement();//创建一个Statement语句对象
         rs = stmt.executeQuery(sql);//执行sql语句
         while(rs.next()){
        		 list.add(rs.getString("cust_id"));
         }
     } catch (SQLException e) {
         e.printStackTrace();
     }finally{
    	 try {
			conn.close();
			stmt.cancel();
	    	rs.close();
		 }catch (SQLException e) {
			e.printStackTrace();
		 }
     }
	 return list;
   }
     
}

说明:此句是为了一次性获取 多个序列值,方便 2 表关联:

String squence ="select USR_CUSTOMER_SEQ.nextval cust_id from (select 1 from all_objects where rownum <= "+usrlist.size()+")";

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-07-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档