2017-02-19 33 views
1

У меня есть файл csv данных, который содержит всего 100 000 записей. Я повторяю записи и пытаюсь обновить 5 таблиц для каждой записи. Вот образец данных:JPA: EntityManager занимает слишком много времени, чтобы сохранить данные.

EAN Code,Site,Genric Material,Material,Sap Ean Code,Style,Color,Size,MRP,Gender,EAN Code,Season,Collection,BRAND,Color revision,Category (L5),Category (L6) 
123456789,6001,000000000061000102,000000061000102001,61000102001,03/BE100,SC/TG,L/112 cm,850.00,MENS,123456789,AW12,Colors,XXXXXX,RD/TG,Tee Shirt,Graphic 

Эти пять таблиц, которые будут обновляемых для каждой итерации следующим образом:

  1. Мастер
  2. MasterDescription
  3. Атрибуты
  4. AttributeValues ​​
  5. AssociationTable

Соотношение между указанными выше таблиц заключаются в следующем:

Мастер ММ AttributeValues ​​

Мастер M-1 MatserDescription

Мастер ММ Атрибуты

Атрибуты 1-M AttributeValues ​​

Вот код, который я должен сохранить CSV-данные в 5 таблицах за один сеанс, используя пакетную технику:

Класс обслуживания

@Service 
public class EanService{ 

@AutoWired 
public EanRepository eanrepository; 

// Method that saves data from CSV to DataBase 
@Transactional 
public void saveEANMasterData1(BufferedReader br, String userName, 
     List<EanAttributes> attributes, String eanMasterName,String description) { 
    int i =1; 

    EanMasterDiscription eanDes = new EanMasterDiscription(); 
    User user = userRepository.findUserByUsername(userName); 
    EanMasterDiscription deciption = null; 
    eanDes.setDescription(description); 
    eanDes.setMasterName(eanMasterName); 
    eanDes.setDate(new Timestamp(Calendar.getInstance() 
      .getTimeInMillis())); 
    String line; 
    try { 
     List<Ean> eans = new ArrayList<Ean>(); 
     // iterating over each record in the CSV and saving the data into DB    
     while (((line = br.readLine()) != null)) { 
      String[] cols = line.split(","); 
      // Style Keeping Unit 
      Ean ean = new Ean(); 
      for(EanAttributes attr : attributes){ 
       EanAttributeValues eanAttributeValues = new EanAttributeValues(); 
       if(attr.getAttrInferredType().equalsIgnoreCase("EAN")){ 
         ean.setEAN(cols[attr.getAttributeOrder()]); 
       }else if(attr.getAttrInferredType().equalsIgnoreCase("Season")){ 
        ean.setSeason(cols[attr.getAttributeOrder()]); 
       }else { 
        if(attr.getAttrInferredType().equalsIgnoreCase("Attribute")){ 
         EanAttributes eanAttr = eanrepository.loadAttrsListByAttName(attr.getAttributeName()); 
         if(eanAttr == null){ 
          eanAttributeValues.setAttributeValue(cols[attr.getAttributeOrder()]); 
          eanAttributeValues.setEanAttributes(attr); 
          ean.getEanAttributeValues().add(eanAttributeValues); 
          ean.getEanAttributes().add(attr); 
          attr.getEan().add(ean); 
         }else{ 
          ean.getEanAttributes().add(eanAttr); 
          eanAttr.getEan().add(ean); 
          if(eanrepository.isAttributeValueAvailable(cols[attr.getAttributeOrder()])){ 
           eanAttributeValues.setAttributeValue(cols[attr.getAttributeOrder()]); 
           eanAttributeValues.setEanAttributes(eanAttr); 
           ean.getEanAttributeValues().add(eanAttributeValues); 
          }else{ 
           EanAttributeValues values = eanrepository.loadDataByAttrValue(cols[attr.getAttributeOrder()]); 
           ean.getEanAttributeValues().add(values); 
           values.getEan().add(ean); 
          } 
         } 
         eanAttributeValues.getEan().add(ean); 
        } 
       } 
      } 
      if(!eanrepository.isEanMasterNameAvailable(eanMasterName)){ 
       EanMasterDiscription eanMasterDes = eanrepository.loadDataByMasterName(eanMasterName); 
       ean.setEanMasterDesciption(eanMasterDes); 
      }else{ 
       ean.setEanMasterDesciption(eanDes); 
      } 
      ean.setUser(user); 
      if(eanrepository.isEanWithSeasonAvailable(ean.getEAN(),ean.getSeason())){ 
        // Persisting Ean; I think there is some problem with this method 
        eanrepository.saveEanData(ean,i); 
      }else{ 
       System.out.println("************ EAN ALREADY EXIST ******************** "); 
      } 

      i++; 
     } 
    } catch (NumberFormatException | IOException e) { 
     e.printStackTrace(); 
    }  
    } 
} 

Repository класс

@Repository 
public class EanRepository{ 

@PersistanceContext 
EntityManager em; 

public void saveEanData(Ean ean , int recordNum){ 
    em.merge(ean); 
    if(recordNum % 50 == 0){ 
     em.flush(); 
     em.clear(); 
     // em.getEntityManagerFactory().getCache().evictAll(); 
    } 
} 

}

Но это занимает слишком много времени (около 10hrs), чтобы закончить сохранение всех 100 000 записей. Как мы можем сократить время и то, что мне не хватает?

+0

Возможный дубликат [Как сохранить много объектов (JPA)] (http://stackoverflow.com/questions/5649345/how-to-persist-a-lot-of-entities-jpa) – eis

+0

или [ Пакетные вставки с использованием JPA EntityManager] (http://stackoverflow.com/a/31020939/365237) – eis

ответ

1

У меня были те же проблемы в моем пакетного приложения, и мы включили два способа, которые значительно ускорить процесс импорта данных:

1) Многопоточность - Вы должны воспользоваться обработки нескольких потоков вашего данные файла и сохранение.

То, как мы это делали, было первым, прочитать все данные из файла и упаковать его в набор объектов POJO.

Тогда, основываясь на количестве возможных потоков, которые мы можем создать, мы разделили бы равномерно и подавали потоки определенным диапазоном данных.

Затем каждый комплект будет обрабатываться параллельно.

Я не буду вдаваться в детали, поскольку это выходит за рамки этого вопроса. Просто совет, который я могу дать, это то, что вы должны попытаться воспользоваться java.util.concurrent и функциями, которые он предлагает.

2) Batch Saving - Второе усовершенствование, которое мы сделали, чтобы воспользоваться данной партии сохранить особенности спящего режима (вы добавили тег гибернации, поэтому я полагаю, что это ваш основной поставщик сохранения):

Вы можете попробовать воспользоваться функцией массовой вставки.

Существует зимуют свойство, которое можно определить, чтобы включить эту функцию:

<property name="jdbc.batch_size">250</property> 

С этой партии установки вы должны иметь выход как:

insert into Table(id , name) values (1, 'na1') , (2, 'na2') ,(3, 'na3').. 

вместо

insert into Table(id , name) values (1, 'na1'); 
insert into Table(id , name) values (2, 'na2'); 
insert into Table(id , name) values (3, 'na3'); 

3) Кол-во флешей - у вас есть купе nt установлен на 50, прежде чем вы начнете flush to db .. теперь с включенными пакетными вставками, возможно, вы могли бы поднять его немного до немногих. Попробуйте поэкспериментировать с этим номером, чтобы найти сладкое пятно.

0

Может быть много причин, по которым загрузка данных требует времени (так что только от кода) и - вы должны оптимизировать меньшие фрагменты данных.

Так что я бы просто снимать штору из моего опыта:

  • , если это возможно, использование сохраняются() вместо слияния(), слияние() производит один более выбор и делает какое-то значение копирования элементов

  • при загрузке огромного объема данных - не используйте транзакции. Я вижу, ты вровень только на каждой 50-й записи, но накладные расходы сделка может быть по-прежнему очень дорого в любом случае

  • как написано на он предыдущем посте, установите насыпной атрибут вставки (в зависимости от JPA используется)

0

Поскольку у вас есть файл CSV с большим количеством записей, я бы рассмотрел еще одну альтернативу: конкретные для конкретной базы данные, отдельные инструменты.

Например, для MySQL у вас должно быть these tools, а также load data infile; для PostgreSQL, this syntax, который может быть использован from command line; для Oracle, sqlldr. Они более оптимизированы для такого рода вещей.

+0

На самом деле проблема заключается в том, что это веб-приложение. Пользователь должен загрузить данные i, которые мы выводим схему на эту схему пользователь будет выбирать столбцы, в которых ему нужны эти столбцы, только нам нужно сохранить их в базе данных. спасибо –

+0

@RamkumarDM ничего не мешает веб-приложению сохранять файл на диск, а затем вызывать некоторые из этих инструментов с помощью пути к файлу в качестве параметра (и затем удалять файл). Это было бы также более удобным для пользователя, так как пользователю не нужно ждать продолжения. – eis