Sharath Karnati
Sharath Karnati

Reputation: 11

Spring Data JPA performnace issue with batch save

I have the following Entity

@Entity
@Table(name = "APP_ITEM")
public class AppItem implements Serializable {
private static final long serialVersionUID = 1L;

@EmbeddedId
private AppItemPK AppItemPK;

 public AppItemPK getAppItemPK() {
    return appItemPK;
}

public void setAppItemPK(
        AppItemPK appItemPK) {
    this.appItemPK = appItemPK;
}
} 


@Embeddable
public class AppItemPK implements Serializable {

private static final long serialVersionUID = 1L;
@Column(name = "app_id")
private Long appId;
@Column(name = "item_id")
private Long itemId;

public Long getAppId() {
    return appId;
}

public void setAppId(Long appId) {
    this.appId = appId;
}

public Long getItemId() {
    return itemId;
}

public void setItemId(Long itemId) {
    this.itemId = itemId;
}

public boolean equals(Object obj) {
    if (obj instanceof AppItemPK) {
        AppItemPK appItemPK = (AppItemPK) obj;
        if (appItemPK.getItemId().equals(this.itemId)
                && appItemPK.getAppId().equals(
                        this.appId)) {
            return true;
        }
    }
    return false;
}

public int hashCode() {
    return this.itemId.hashCode() + this.applicationId.hashCode();
}

}

Using below code to insert record into app_item table

@Transactional(readOnly = false)
public boolean saveItemSelection(PageHeaderViewData pageHeaderViewData, Map<Long, Boolean> selectedItems,String savedItems){
    long millisSaveStart = Calendar.getInstance().getTimeInMillis();
    log.debug("Inside saveItemSelection appId:"+pageHeaderData.getAppId());
    boolean saveStatus = false;     
    List<AppItem> appItemInsList = new ArrayList<SavedApplicationItem>();

    if (pageHeaderData.getAppId() != null) {

         for (Entry<Long, Boolean> idEntry : selectedItems.entrySet() ) {
             if (idEntry!= null){                    

                     if (idEntry.getValue() && !savedItems.contains(idEntry.getKey().toString())){                           
                         //log.debug("Inside saveItemSelection SAVED itemId:"+idEntry.getKey()+" , Value:"+idEntry.getValue());          
                         AppItem appItem = new AppItem();
                         AppItemPK appItemPK = new AppItemPK();
                         appItemPK.setAppId(pageHeaderData.getAppId());
                         appItemPK.setItemId(idEntry.getKey());
                         appItem.setAppItemPK(appItemPK);
                         appItem.setUpdateInd(ToggleEnum.Y);                        
                         appItemInsList.add(appItem);
                         //appItemRepository.saveAndFlush(appItem);                          
                     }
                 }
             }


         } 


         if (appItemInsList.size() != 0){
             long millisJPASaveStart = Calendar.getInstance().getTimeInMillis();
             appItemRepository.save(appItemInsList);    
             long millisJPASaveEnd = Calendar.getInstance().getTimeInMillis();
             log.debug("JPA save time:"+(millisJPASaveEnd-millisJPASaveStart));
         }

         saveStatus = true;

         long millisSaveEnd = Calendar.getInstance().getTimeInMillis();
         log.debug("Total save time:"+(millisSaveEnd-millisSaveStart));
    }

    return saveStatus;

}//end of saveItemSelection

For inserting 5000 records it is taking 13826 milliseconds.

Can someone please let me know, how to improve the performance in above JPA code. We are using hibernate for jpa implementation.

Upvotes: 1

Views: 11399

Answers (1)

Nitin Arora
Nitin Arora

Reputation: 2668

To improve the performance your inserts, you should implement batch insert using custom code. The method below will make sure that batches are flushed to the database. Tweak the batch size based on your performance tests. Generally 50 is a good number to start.

@Transactional
public void bulkPersist(List<Entity> entities) {
  int i = 0;
  for (Entity entity : entities) {
    em.persist(entity);
    i++;

    if (i % batchSize == 0) {
      flush();
      clear();
    }
  }
}

Above change will create multiple insert statements. You can further optimize the insert query by setting up the hibernate configuration.

<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.order_updates">true</prop> 

Tip: Enable Debug log to see 1 insert query is being generated per batch.

Upvotes: 5

Related Questions