Naariom Met
Naariom Met

Reputation: 241

Ideas to optimize a few subsequent Hibernate load / save calls

I have a simple repository backed up by Hibernate

@Repository
@Transactional(rollbackFor = Exception.class)
public class WidgetDAOImpl implements WidgetDAO {
    @Autowired
    private SessionFactory sf;

    @Override
    public Widget getWidget(long id) {
        return (Widget) sf.getCurrentSession().load(Widget.class, id);
    }

    @Override
    public void saveWidget(Widget w){
        sf.getCurrentSession().saveOrUpdate(w);
    }
}

This is really simple and is OK. Unfortunately the requirements changed and now I will need to read a lot of and save a lot of widgets - during single business call. Also, the number of business calls will grow.

For instance, this could be the business logic in a separate class:

@Scheduled(fixedDelay = 100L)                       // lets say this is "often"
public void updateWidgets(List<Long> ids){          // lets say the list is ~10 ids
    for(long id: ids){
        Widget w = widgetDAO.getWidget(id);
        doSomeStuff(w);
        widgetDAO.saveWidget(w);
    }   
}

I am affraid this will kill the performance. What can I do to save the performance?

Some ideas / snippets are really welcome, since I am a Hibernate novice.

Upvotes: 1

Views: 76

Answers (1)

M. Deinum
M. Deinum

Reputation: 125252

Your current solution is creating a lot of transactions. 2 transactions for each item, 1 for the read and 1 for the write.

Creating a transaction is a heavy and time consuming operation (generally) as it will open a connection to the database, start a hibernate session and do some synchronization.

Preferably your whole method is a single transaction and this can be accomplished by annotating your method with @Transactional.

@Scheduled(fixedDelay = 100L)              
@Transactional
public void updateWidgets(List<Long> ids){ ... }        

When doing this and you have large amounts of data you might want to flush and clear your current session once in a while.

If you don't want this you should at least make your read and write in a single transaction. For this you can use a TransactionTemplate and use this inside your for loop.

@Autowired
private TransactionTemplate transactionTemplate;

@Scheduled(fixedDelay = 100L)   
public void updateWidgets(List<Long> ids) {  
    for(long id: ids){
       transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          protected void doInTransactionWithoutResult(TransactionStatus status) {
              Widget w = widgetDAO.getWidget(id);
               doSomeStuff(w);
               widgetDAO.saveWidget(w);
          }
    }   
}

Or move the code in your for loop to a service method which is annotated with @Transactional.

Upvotes: 1

Related Questions