Google

Nov 7, 2013

Common Hibernate mistakes - accessing objects outside of a transaction

Hibernate does require the same EntityManager to be available in order to lazily load objects. If you have no EntityManager, then you have no knowledge of the datastore. Once the transaction is commited the objects become detached, and you can't lazy load detached objects. So, you need to lazily load your objects within the same transaction in your service layer.

For example: Account has one-to-many relationship with AccountAttributes. The AccountAttributes are lazily loaded via getAccountAttributes() method invocation.

The DAO class


@Repository("portfolioDao")
public class PortfolioDaoImpl implements PortfolioDao
{
    @PersistenceContext
    private EntityManager em;
    
    @Override
    public Portfolio getAccountById(Long accountId)
    {
        Query query = em.createQuery("from Portfolio as pf where pf.portfolioId = :portfolioId");
        query.setParameter("accId", accountId);
        
        return (Account) query.getSingleResult();
    }
}


Wrong:

  public class AccountServiceImpl implemnts AccountService {
      
   public void processAcoount(Long accountId) {
   Account account = accountDao.getSccountById(accountId); //good
   // exception is thrown
   List<AccountAttributes> accountAttributes = account.getAccountAttributes(); 
   }
  } 


Right: as it is lazily loaded within the same transaction

  public class AccountServiceImpl implemnts AccountService {
       
      @Transactional    
   public void processAcoount(Long accountId) {
   Account account = accountDao.getSccountById(accountId); //good
    // fine, as it is within the same txn, and the EntityManager will still be open.
   List<AccountAttributes> accountAttributes = account.getAccountAttributes(); 
   }
  } 


Eager fetching is often not a good solution.  Having said this there are downsides to lazy loading. The developer has to be aware of his/her fetching strategy and must be capable of differentiating when and where each strategy is most applicable. Otherwise you can end up with serious performance issues like the N+1 Select Problem.

Fetching too much will run into the opposite problem, the cartesian product issue: instead of executing to many SQL statements, you may end up creating statements that retrieve too much data.

Tips:


  • Firstly, activate SQL logging at Hibernate's level by turning show_sql to true.
  • Run the critical use cases to identify suspicious queries and optimize the fetch plan. Check if indices are used appropriately, etc


Also, watch-out for other pitfalls like




Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home