Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DAO methods parameters , object-references vs ids

Question

What is the best practice for the type of paramaters of dao/repository method, entity-objects or entity-ids?

Example code

@Entity
class Product {
    // ...

    @ManyToOne
    Seller seller;
}

@Entity
class Seller {
    @Id @GeneratedValue
    Long id;
}

class ProductDao {
    // ...

    // Using ids
    public List<Product> getProductsOf(long sellerId) {
        return getSession()
            .createQuery("from Product where seller.id = ?")
            .setLong(0, sellerId)
            .list();    
    }

    // Using object-references
    public List<Product> getProductsOf(Seller seller) {
        return getSession()
            .createQuery("from Product where seller = ?")
            .setEntity(0, seller)
            .list();    
    }

    // Using object-references using merge() on a detached object
    public List<Product> getProductsOf2(Seller seller) {
        Seller persistentSeller = getSession().merge(seller);

        return getSession()
            .createQuery("from Product where seller = ?")
            .setEntity(0, seller)
            .list();    
    }

    // Using object-references using lock() on a detached object
    public List<Product> getProductsOf3(Seller seller) {
        getSession().buildLockRequest(LockOptions.NONE).lock(seller);

        return getSession()
            .createQuery("from Product where seller = ?")
            .setEntity(0, seller)
            .list();    
    }
}

Pros and cons

I have found the following pros and cons, but I was wondering if there is a best practice among experienced Spring/Hibernate/JPA users.

Pros of: getProductsOf(Seller seller)

  • Easy to use from a client perspective when you already have a seller that is in the persistent context (persistent state).

Cons of: getProductsOf(Seller seller)

  • You have to verify that seller is in persistent or detached state, which can make its implementation verbose. You'll have to use merge() or locking, see getProductsOf2() and getProductsOf3().
  • Even if you know the id of the seller, you first have to query the seller object separatly. (load() can be used to use a proxy instead to avoid the extra query to Seller, but you still have to call the session object.)
  • the parameter can be null.

Pros of: getProductsOf(long sellerId)

  • When you don't have a seller object yet, but know the sellerId, this may be faster when you only need to query for a sellerId once in the unit-of-work.
  • Avoids the null reference problems

Cons of: getProductsOf(long sellerId)

  • When multiple "long" parameters exist in the method, you may mistake in the argument call order, causing you to query using wrong ids.
  • Feels like a less object-oriented approach than using an object as parameter.
  • The method call looks less clean:
    getProductsOf(seller.getId())
    instead of:
    getProductsOf(seller)

My preference

I'm using getProductsOf(Seller seller), but having to verify whether seller is in persistent or detached state is very cumbersome. Therefor, I'm thinking of using ids instead.

like image 964
Devabc Avatar asked Jan 28 '15 14:01

Devabc


1 Answers

The best way is to avoid writing down your own DAO and use Spring Data instead.

I prefer the Spring Repository API, which looks like this:

public interface CrudRepository<T, ID extends Serializable>
    extends Repository<T, ID> {                                                                                                                     
    <S extends T> S save(S entity);

    T findOne(ID primaryKey);

    Iterable<T> findAll();

    Long count();

    void delete(T entity);

    boolean exists(ID primaryKey);

}
  • the save and delete methods take an entity
  • the findOne and exists take an identifier because it assumes you don't have the entity that you want to fetch

As for findOne, it's better to have it take an identifier. This way you can call it even if you have an entity. If it were taken an entity, then you'd have to create a transient entity with a populated identifier just for the sake of fetching the associated managed entity.

like image 109
Vlad Mihalcea Avatar answered Sep 18 '22 15:09

Vlad Mihalcea