怎么利用JAVA导入大数据量的文本文件数据到Oracle数据库

时间:2020-12-17 23:21:17
有一个文本文件,差不多有100万条数据,如何利用JAVA把这些数据导入ORACLE数据库?

文本文件格式如下:
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7319-1
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7323-501
RTC 07|CHG D|SPL 0RBN1|MFR 81205

数据字段并不等长,就是有的行有4个数据内容,有可能有6个...
前面的英文为字段的名称?

有没有大虾指点下?

22 个解决方案

#1


首先从文件中读取数据,每当读完一条纪录是,把它转换成oracle的插入语句。你可以执行,也可以存在一个大字符串里,最后一起执行

#2


用字符串拆分split()方法把名称过滤掉。并且根据判断来生成SQL语句

#3


我现在就是按照两位所说的那样做,可是在导入过程当中,大概需要一个多小时,结果JSP页面因为等待时间过长而超时.

一个是看大家有什么成功的经验,或者是有好的处理方法,能够不超时,或者是加快导入的时间?

#4


用EJB把,分布式处理。

#5


http://www.builder.com.cn/2002/0806/57323.shtml
kankanasdf

#6


用消息驱动bean

#7


不知道Spring+Hibernate+Struts这方面有什么思路吗?

#8


我以前寫過的
可參考修改一下
應該可以湊合著用

package com.XXX;

import com.my.db.myDb;
import com.my.file.*;
import com.my.schedule..myOperaEdiRec;
import com.my.schedule..myXa;
import com.my.util.myDate;
import com.my.util.myUtil;
import org.apache.log4j.Logger;

import javax.servlet.ServletContext;
import java.io.BufferedReader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

public class myXBPersocards {
  private PreparedStatement psmtInsPersocard;
  private myDb oDb;
  private myFile oFile;
  private myXBlog oLoger;
  private BufferedReader oReader;
  private Logger oLogger;

  private int iAllRecNum;//所有的記錄
  private int iOkRecNum;//成功的記錄

  private final int iChkBegin = 3;
  private final int iChkEnd = 10;
  private final int iChkLen = 418;
  //字段總長度
  private String sFields[];
  private final int iFieldsLen[] = {8, 16, 8, 11};

  private ServletContext Application;   //Context對象
  /**
   * 定義相慶欄位*
   */
      
  private String FILE_NO;
  private String INF_DATE;
  private String LINK_NO_N;
  private String END_DATE;
    
  private int ERR_NO;//錯語代碼
  private String RTN_MSG;//處理訊息

  private int iCount=0;

  public myXBPersocards(myFile file, myDb db, myXBlog loger, ServletContext application, Logger logger_)
      throws Exception {
    oReader = null;
    this.oDb = db;
    this.oFile = file;
    this.oLoger = loger;
    this.oLogger = logger_;
    this.Application = application;
    iAllRecNum = 0;
    iOkRecNum = 0;
    initSql();
    try {
      oReader = oFile.getReader();
      readFile(oReader);
      oDb.commit();
    } catch (Exception e) {
      oLoger.error("讀取文件時出錯:" + e.getMessage());
      oLogger.error("讀取文件時出錯:" + e.getMessage());
      throw new Exception("讀取文件時出錯:" + e.getMessage());
    } finally {
      if (oReader != null) oReader.close();
      if (psmtInsPersocard != null) oDb.closePrepareStmt(psmtInsPersocard);

    }
  }

  /**
   * 初如化sql.
   *
   * @throws Exception
   */
  private void initSql() throws Exception {
    String sqlInsPersocard = "insert into Persocard \n" +
        "(FILE_NO,INF_DATE,LINK_NO_N,END_DATE \n" +
        "values \n " +
        "(?,?,?,?)";
    psmtInsPersocard = oDb.prepareStmt(sqlInsPersocard);
  }

  /**
   * 切分fields的字段長度
   *
   * @param line
   * @return
   * @throws Exception
   */
  protected String[] tokenizerLine(String line) throws Exception {
    int iBegin = 0;
    String str[] = new String[iFieldsLen.length];
    byte[] buf = line.getBytes();

    for (int i = 0; i < iFieldsLen.length; i++) {
      str[i] = new String(buf, iBegin, iFieldsLen[i]);
      iBegin += iFieldsLen[i];
    }
    return str;
  }

  /**
   * 讀文件
   *
   * @param oReader
   * @throws Exception
   */
  private boolean readFile(BufferedReader oReader) throws Exception {
    int curNum = 0;             //該檔案的總行數
    int readLineTot = 0;        //首行的第3~10碼為資料筆數(E)
    ArrayList oList = null;
    boolean isBadLen = false; //首行的長度是否OK
    String sLine;
    String sEDI_NO = myOperaEdiRec.getEdiNO(this.oDb);
    //轉檔記錄編號
    while ((sLine = oReader.readLine()) != null) {
      if (curNum == 0) {
        oLogger.info(oFile.getFileName() + "序號" + sEDI_NO);
        readLineTot = Integer.parseInt(sLine.substring(iChkBegin - 1, iChkEnd).trim());
        if (sLine.getBytes().length != iChkLen) {//如果記錄的長度不對,不處理.
          isBadLen = true;
          oLoger.error("文檔資料長度不對");
          break;
        }

      } else {
        if (oList == null) oList = new ArrayList();
        oList.add(curNum - 1, sLine);
      }
      curNum++;
    }
    if (isBadLen) return false;
    if (readLineTot > 0) {
      if ((curNum - 1) != readLineTot || oList == null) {
        return false;
      } else {
        iAllRecNum = readLineTot;//處理的總筆數
      }
    } else {
      return false;
    }
    //處理可以用的資料
    try {
      for (int i = 0; i < oList.size(); i++) {
        try {
          processLine((String) oList.get(i));
          iOkRecNum++;
        } catch (Exception e) {
          oLoger.error("處理一行記錄時出錯:" + e.getMessage());
          oLogger.error("處理一行記錄時出錯:" + e.getMessage());
          e.printStackTrace();
        }
      }
    } catch (Exception e) {
      oLogger.error("轉檔錯誤:" + e.getMessage());
    }
    oLogger.info("轉檔處理結束");
    return true;
  }

  /**
   * 處理一行記錄.
   *
   * @param sLine
   * @throws Exception
   */
  private void processLine(String sLine) throws Exception {
    sFields = tokenizerLine(sLine);
    iCount++;
    oLoger.info("第幾筆資料"+iCount);
    System.out.println("第幾筆資料"+iCount);
    setLineField();//設定欄位
    insertPersocard();//新增記錄
  }

  /**
   * 新增資料
   *
   * @throws Exception
   */
  private void insertPersocard() throws Exception {
    try {
      oDb.setCurrentPrepareStmt(psmtInsPersocard);
      psmtInsPersocard.clearParameters();
      psmtInsPersocard.setString(1, FILE_NO);
      psmtInsPersocard.setString(2, INF_DATE);
      psmtInsPersocard.setString(3, LINK_NO_N);
      psmtInsPersocard.setString(4, END_DATE);

      oDb.prepareUpdate();
    } catch (Exception e) {
      oLoger.error("新增資料檔時出錯:"  + e.getMessage());
      oLogger.error("新增資料檔時出錯:" + e.getMessage());
      e.printStackTrace();
      throw e;
    }
  }

  /**
   * 設定欄位的記錄.
   *
   * @throws Exception
   */
  private void setLineField() throws Exception {
    FILE_NO = oFile.getFileExt();
    INF_DATE = sFields[1].trim();
    LINK_NO_N = sFields[2].trim();
    END_DATE = sFields[3].trim();
  }
}

#9


看来我们得在服务器外单开一个线程了。

#10


用一个随服务器自动启动的servlet每隔一段时间去读取servletContext中的Vector();
Vector里放上我们的语句。然后在这个servlet里执行。不知道能不能行得通

#11


多谢!

我仔细研究一下各位的思路,再把结果放上来!

#12


倒个数据跟JSP有什么关系?
这也要用SSH架构? 寒。。

#13


用 sqlldr 吧,很简单的。

#14


呵呵`遇到相同的问题了`我的数据量没有你的大`

#15


除了数据量大,没别的瓶颈了吧,也就是:
1.读文件
2.拼sql
3.写数据库

但是我觉得,要实现的比较合理,还是要仔细定义些东西的:
首先定义一个bean,包括所有字段
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7319-1
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7323-501
RTC 07|CHG D|SPL 0RBN1|MFR 81205

从数据看,关键是拼sql
以下是我的思路:
public class Test
{
    private String template = "insert into xxxTable (RTC,CHG,SPL,MFR,PNR) values "
+ "('value_RTC','value_CHG','value_SPL','value_MFR','value_PNR');";

    private Connection conn = null;
    private PreparedStatement pstmt = null;

    public static void main(String[] args)
    {
        try
        {
            Test t = new Test();
            t.parseFile();
        }
        catch (SQLException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void parseFile() throws SQLException
    {
        String sss = "RTC 07|CHG D|SPL 0RBN1|MFR 81205";
        Scanner scanner = new Scanner(sss);
        String token = null;

        token = scanner.findInLine("RTC\\s+\\w+\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_RTC", token.replaceAll(
                                        "RTC\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("CHG\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_CHG", token.replaceAll(
             "CHG\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("SPL\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_SPL", token.replaceAll(
"SPL\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("MFR\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_MFR", token.replaceAll(
             "MFR\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("PNR\\s+[\\w-]*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_PNR", token.replaceAll(
"PNR\\s+", "").replaceAll("\\|", ""));
        }
        template = template.replaceAll("value_\\w+", "");

        pstmt.addBatch(template);
    }
}

象这种比较短的sql,addBatch上3、5万次,再pstmt.executeBatch();一次应该就可以

当然更好的办法应该是:用ultraedit之类的工具将文本文件弄成规范的格式,然后改改文件扩展名,直接用数据库导入功能,呵呵

#16


汗~~~~~~~导数据不需要用SSH吧,直接用写个Java类运行就可以啦~

#17


如果你非要用JSP,那么请单独启动一个线程来导入,或者写一个存储过程供Java调用,或者如楼上所说写个Java类单独运行,在JSP中直接写这种逻辑,嗯,应该是比较业余的做法。

#18


用消息驱动Bean异步执行,然后jsp不停刷新好了

#19


关注下,记得 pl/sql developer 有个倒文本的工具的不知道用不用的上

#20


按照我的经验,JSP提交时间过长的问题应该这样解决:页面仅提供上传功能,将该文本上传到服务器,然后由后台完成导入。

#21


以前我做過這樣的程序,用正則表達式拆分一行的數據,拆分成一組值,然后拼湊出SQL,可以每讀取200行拼湊出一個SQL,再執行(因為sql過長會執行失敗的):
比如:
insert into tablename (col1,col2,col3,col4)
select values1,value2,value3,value4
union
select value1,value2,value3,value4
union
select value1,value2,value3,value4
...
然后execute

#22


其实文件导库的实现原理大多数人都知道,都最烦人的就是效率问题,如何做到导入100万条的数据所需要的时间最少呢,个人觉得如果你插库时所需的数据不都是来自文件,也就是说要根据文件的某些数据去找另一些表再插入指定表的话,建议采用存储过程,把与数据库相关所有操作都写到存储过程里面,这样速度较快。除些之外,应想尽方法减少与数据库的通信次数,最好就利用批处理。最后说一点,像你这样大数据导库的,肯定是采用异步方法进行处理。

#1


首先从文件中读取数据,每当读完一条纪录是,把它转换成oracle的插入语句。你可以执行,也可以存在一个大字符串里,最后一起执行

#2


用字符串拆分split()方法把名称过滤掉。并且根据判断来生成SQL语句

#3


我现在就是按照两位所说的那样做,可是在导入过程当中,大概需要一个多小时,结果JSP页面因为等待时间过长而超时.

一个是看大家有什么成功的经验,或者是有好的处理方法,能够不超时,或者是加快导入的时间?

#4


用EJB把,分布式处理。

#5


http://www.builder.com.cn/2002/0806/57323.shtml
kankanasdf

#6


用消息驱动bean

#7


不知道Spring+Hibernate+Struts这方面有什么思路吗?

#8


我以前寫過的
可參考修改一下
應該可以湊合著用

package com.XXX;

import com.my.db.myDb;
import com.my.file.*;
import com.my.schedule..myOperaEdiRec;
import com.my.schedule..myXa;
import com.my.util.myDate;
import com.my.util.myUtil;
import org.apache.log4j.Logger;

import javax.servlet.ServletContext;
import java.io.BufferedReader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

public class myXBPersocards {
  private PreparedStatement psmtInsPersocard;
  private myDb oDb;
  private myFile oFile;
  private myXBlog oLoger;
  private BufferedReader oReader;
  private Logger oLogger;

  private int iAllRecNum;//所有的記錄
  private int iOkRecNum;//成功的記錄

  private final int iChkBegin = 3;
  private final int iChkEnd = 10;
  private final int iChkLen = 418;
  //字段總長度
  private String sFields[];
  private final int iFieldsLen[] = {8, 16, 8, 11};

  private ServletContext Application;   //Context對象
  /**
   * 定義相慶欄位*
   */
      
  private String FILE_NO;
  private String INF_DATE;
  private String LINK_NO_N;
  private String END_DATE;
    
  private int ERR_NO;//錯語代碼
  private String RTN_MSG;//處理訊息

  private int iCount=0;

  public myXBPersocards(myFile file, myDb db, myXBlog loger, ServletContext application, Logger logger_)
      throws Exception {
    oReader = null;
    this.oDb = db;
    this.oFile = file;
    this.oLoger = loger;
    this.oLogger = logger_;
    this.Application = application;
    iAllRecNum = 0;
    iOkRecNum = 0;
    initSql();
    try {
      oReader = oFile.getReader();
      readFile(oReader);
      oDb.commit();
    } catch (Exception e) {
      oLoger.error("讀取文件時出錯:" + e.getMessage());
      oLogger.error("讀取文件時出錯:" + e.getMessage());
      throw new Exception("讀取文件時出錯:" + e.getMessage());
    } finally {
      if (oReader != null) oReader.close();
      if (psmtInsPersocard != null) oDb.closePrepareStmt(psmtInsPersocard);

    }
  }

  /**
   * 初如化sql.
   *
   * @throws Exception
   */
  private void initSql() throws Exception {
    String sqlInsPersocard = "insert into Persocard \n" +
        "(FILE_NO,INF_DATE,LINK_NO_N,END_DATE \n" +
        "values \n " +
        "(?,?,?,?)";
    psmtInsPersocard = oDb.prepareStmt(sqlInsPersocard);
  }

  /**
   * 切分fields的字段長度
   *
   * @param line
   * @return
   * @throws Exception
   */
  protected String[] tokenizerLine(String line) throws Exception {
    int iBegin = 0;
    String str[] = new String[iFieldsLen.length];
    byte[] buf = line.getBytes();

    for (int i = 0; i < iFieldsLen.length; i++) {
      str[i] = new String(buf, iBegin, iFieldsLen[i]);
      iBegin += iFieldsLen[i];
    }
    return str;
  }

  /**
   * 讀文件
   *
   * @param oReader
   * @throws Exception
   */
  private boolean readFile(BufferedReader oReader) throws Exception {
    int curNum = 0;             //該檔案的總行數
    int readLineTot = 0;        //首行的第3~10碼為資料筆數(E)
    ArrayList oList = null;
    boolean isBadLen = false; //首行的長度是否OK
    String sLine;
    String sEDI_NO = myOperaEdiRec.getEdiNO(this.oDb);
    //轉檔記錄編號
    while ((sLine = oReader.readLine()) != null) {
      if (curNum == 0) {
        oLogger.info(oFile.getFileName() + "序號" + sEDI_NO);
        readLineTot = Integer.parseInt(sLine.substring(iChkBegin - 1, iChkEnd).trim());
        if (sLine.getBytes().length != iChkLen) {//如果記錄的長度不對,不處理.
          isBadLen = true;
          oLoger.error("文檔資料長度不對");
          break;
        }

      } else {
        if (oList == null) oList = new ArrayList();
        oList.add(curNum - 1, sLine);
      }
      curNum++;
    }
    if (isBadLen) return false;
    if (readLineTot > 0) {
      if ((curNum - 1) != readLineTot || oList == null) {
        return false;
      } else {
        iAllRecNum = readLineTot;//處理的總筆數
      }
    } else {
      return false;
    }
    //處理可以用的資料
    try {
      for (int i = 0; i < oList.size(); i++) {
        try {
          processLine((String) oList.get(i));
          iOkRecNum++;
        } catch (Exception e) {
          oLoger.error("處理一行記錄時出錯:" + e.getMessage());
          oLogger.error("處理一行記錄時出錯:" + e.getMessage());
          e.printStackTrace();
        }
      }
    } catch (Exception e) {
      oLogger.error("轉檔錯誤:" + e.getMessage());
    }
    oLogger.info("轉檔處理結束");
    return true;
  }

  /**
   * 處理一行記錄.
   *
   * @param sLine
   * @throws Exception
   */
  private void processLine(String sLine) throws Exception {
    sFields = tokenizerLine(sLine);
    iCount++;
    oLoger.info("第幾筆資料"+iCount);
    System.out.println("第幾筆資料"+iCount);
    setLineField();//設定欄位
    insertPersocard();//新增記錄
  }

  /**
   * 新增資料
   *
   * @throws Exception
   */
  private void insertPersocard() throws Exception {
    try {
      oDb.setCurrentPrepareStmt(psmtInsPersocard);
      psmtInsPersocard.clearParameters();
      psmtInsPersocard.setString(1, FILE_NO);
      psmtInsPersocard.setString(2, INF_DATE);
      psmtInsPersocard.setString(3, LINK_NO_N);
      psmtInsPersocard.setString(4, END_DATE);

      oDb.prepareUpdate();
    } catch (Exception e) {
      oLoger.error("新增資料檔時出錯:"  + e.getMessage());
      oLogger.error("新增資料檔時出錯:" + e.getMessage());
      e.printStackTrace();
      throw e;
    }
  }

  /**
   * 設定欄位的記錄.
   *
   * @throws Exception
   */
  private void setLineField() throws Exception {
    FILE_NO = oFile.getFileExt();
    INF_DATE = sFields[1].trim();
    LINK_NO_N = sFields[2].trim();
    END_DATE = sFields[3].trim();
  }
}

#9


看来我们得在服务器外单开一个线程了。

#10


用一个随服务器自动启动的servlet每隔一段时间去读取servletContext中的Vector();
Vector里放上我们的语句。然后在这个servlet里执行。不知道能不能行得通

#11


多谢!

我仔细研究一下各位的思路,再把结果放上来!

#12


倒个数据跟JSP有什么关系?
这也要用SSH架构? 寒。。

#13


用 sqlldr 吧,很简单的。

#14


呵呵`遇到相同的问题了`我的数据量没有你的大`

#15


除了数据量大,没别的瓶颈了吧,也就是:
1.读文件
2.拼sql
3.写数据库

但是我觉得,要实现的比较合理,还是要仔细定义些东西的:
首先定义一个bean,包括所有字段
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7319-1
RTC 07|CHG D|SPL 0RBN1|MFR 81205|PNR AAM7323-501
RTC 07|CHG D|SPL 0RBN1|MFR 81205

从数据看,关键是拼sql
以下是我的思路:
public class Test
{
    private String template = "insert into xxxTable (RTC,CHG,SPL,MFR,PNR) values "
+ "('value_RTC','value_CHG','value_SPL','value_MFR','value_PNR');";

    private Connection conn = null;
    private PreparedStatement pstmt = null;

    public static void main(String[] args)
    {
        try
        {
            Test t = new Test();
            t.parseFile();
        }
        catch (SQLException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void parseFile() throws SQLException
    {
        String sss = "RTC 07|CHG D|SPL 0RBN1|MFR 81205";
        Scanner scanner = new Scanner(sss);
        String token = null;

        token = scanner.findInLine("RTC\\s+\\w+\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_RTC", token.replaceAll(
                                        "RTC\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("CHG\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_CHG", token.replaceAll(
             "CHG\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("SPL\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_SPL", token.replaceAll(
"SPL\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("MFR\\s+\\w*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_MFR", token.replaceAll(
             "MFR\\s+", "").replaceAll("\\|", ""));
        }
        token = scanner.findInLine("PNR\\s+[\\w-]*\\|?");
        if (token != null)
        {
            template = template.replaceAll("value_PNR", token.replaceAll(
"PNR\\s+", "").replaceAll("\\|", ""));
        }
        template = template.replaceAll("value_\\w+", "");

        pstmt.addBatch(template);
    }
}

象这种比较短的sql,addBatch上3、5万次,再pstmt.executeBatch();一次应该就可以

当然更好的办法应该是:用ultraedit之类的工具将文本文件弄成规范的格式,然后改改文件扩展名,直接用数据库导入功能,呵呵

#16


汗~~~~~~~导数据不需要用SSH吧,直接用写个Java类运行就可以啦~

#17


如果你非要用JSP,那么请单独启动一个线程来导入,或者写一个存储过程供Java调用,或者如楼上所说写个Java类单独运行,在JSP中直接写这种逻辑,嗯,应该是比较业余的做法。

#18


用消息驱动Bean异步执行,然后jsp不停刷新好了

#19


关注下,记得 pl/sql developer 有个倒文本的工具的不知道用不用的上

#20


按照我的经验,JSP提交时间过长的问题应该这样解决:页面仅提供上传功能,将该文本上传到服务器,然后由后台完成导入。

#21


以前我做過這樣的程序,用正則表達式拆分一行的數據,拆分成一組值,然后拼湊出SQL,可以每讀取200行拼湊出一個SQL,再執行(因為sql過長會執行失敗的):
比如:
insert into tablename (col1,col2,col3,col4)
select values1,value2,value3,value4
union
select value1,value2,value3,value4
union
select value1,value2,value3,value4
...
然后execute

#22


其实文件导库的实现原理大多数人都知道,都最烦人的就是效率问题,如何做到导入100万条的数据所需要的时间最少呢,个人觉得如果你插库时所需的数据不都是来自文件,也就是说要根据文件的某些数据去找另一些表再插入指定表的话,建议采用存储过程,把与数据库相关所有操作都写到存储过程里面,这样速度较快。除些之外,应想尽方法减少与数据库的通信次数,最好就利用批处理。最后说一点,像你这样大数据导库的,肯定是采用异步方法进行处理。