mardi 10 mai 2016

shall I use Composition or inheritance in a generic repository

recently I created a generic base repository collecting all the needed data access layer methods in it (findall, save, exist,count....etc)

the interface is as follows

/**
 * Interface for generic CRUD operations on a repository.
 * 
 * @author Farouk_Elabady
 */
public interface BaseRepository<T> {


/**
 * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
 * entity instance completely.
 * 
 * @param entity
 * @return the saved entity
 */
<E> E save(E entity);

/**
 * Saves all given entities.
 * 
 * @param entities of type {@link Iterable}
 * @return {@link List} of saved entities 
 * @throws IllegalArgumentException in case the given entity is {@literal null}.
 */
<E> List<E> save(Iterable<E> entities);

/**
 * Saves an entity and flushes changes instantly.
 * 
 * @param entity
 * @return the saved entity
 */
<E> E saveAndFlush(E entity);


/**
 * Saves all given entities.
 * 
 * @param entities of type {@link Iterable}
 * @return {@link List} of saved entities 
 * @throws IllegalArgumentException in case the given entity is {@literal null}.
 */
<E> List<E> saveAndFlush(Iterable<E> entities);

/**
 * Updates a given entity. Use the returned instance for further operations 
 * as the update operation might have changed the
 * entity instance completely.
 *  
 * @param entity
 * @return the saved entity
 */
<E> E update(E entity);

/**
 * Updates all given entities.
 * 
 * @param entities of type {@link Iterable}
 * @return {@link List} of saved entities 
 * @throws IllegalArgumentException in case the given entity is {@literal null}.
 */
<E> List<E> update(Iterable<E> entities);

/**
 * Check whether an entity with the given id exists.
 * 
 * @param entityClass must not be {@literal null}.
 * @param id must not be {@literal null}.
 * @return true if an entity with the given id exists, {@literal false} otherwise
 * @throws IllegalArgumentException if {@code id} is {@literal null}
 */
<E,ID> boolean exists(Class<E> entityClass, ID id);

/**
 * Returns an entity by its id.
 * 
 * @param entityClass must not be {@literal null}.
 * @param id must not be {@literal null}.
 * @return the entity with the given id or {@literal null} if none found
 * @throws IllegalArgumentException if {@code id} is {@literal null}
 */
<E,ID> E find(Class<E> entityClass, ID id);

/**
 * Returns an entity by its id.
 * 
 * @param id must not be {@literal null}.
 * @return the entity with the given id or {@literal null} if none found
 * @throws IllegalArgumentException if {@code id} is {@literal null}
 */
<ID> T find(ID id);

/**
 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param entityClass must not be {@literal null}.
 * @param pageable  can be {@literal null}
 * @return a page of entities
 */
<E> Page<E> findAll(Class<E> entityClass, Pageable pageable);

/**
 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param pageable  can be {@literal null}
 * @return a page of entities
 */
Page<T> findAll(Pageable pageable);

/**
 * Returns a {@link Page} of entities meeting single parameter and paging restriction provided in the {@code Pageable} object.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param entityClass {@link Class} must not be {@literal null}.
 * @param pageable {@link Pageable} can be {@literal null}
 * @param parameter string to determine the parameter type
 * @param value of the parameter
 * @return a page of entities
 */
<E> Page<E> findAllByParameter(Class<E> entityClass, Pageable pageable, String parameter, Object value);

/**
 * Returns a {@link Page} of entities meeting single parameter and paging restriction provided in the {@code Pageable} object.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param pageable {@link Pageable} can be {@literal null}
 * @param parameter string to determine the parameter type
 * @param value of the parameter
 * @return a page of entities
 */
 Page<T> findAllByParameter(Pageable pageable, String parameter, Object value);

/**
 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object with the given IDs.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param entityClass must not be {@literal null}.
 * @param pageable  can be {@literal null}
 * @return a page of entities
 */
<E,ID> Page<E> findAllByIds(Class<E> entityClass, Pageable pageable , Iterable<ID> ids);

/**
 * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object with the given IDs.
 * if {@code Pageable} is null there will be no paging restriction 
 * 
 * @param pageable  can be {@literal null}
 * @return a page of entities
 */
<ID> Page<T> findAllByIds(Pageable pageable , Iterable<ID> ids);

/**
 * Returns the number of entities available.
 * 
 * @param entityClass {@link Class} must not be {@literal null}.
 * @return the number of entities
 */
<E> long count(Class<E> entityClass);

/**
 * Refreshes the cached entity with recent one from the database
 * 
 * @param entity object to refresh
 */
<E> void refresh(E entity);

/**
 * Initialize lazy mapping entity associations 
 * 
 * @param entity object to initialize
 */
void initializeCollection(Object entity);

/**
 * lock entity object row in db on given LockMode this action depends on database engine capabilities.
 * 
 * @param entity
 * @param lockMode
 */
void lock(final Object entity, LockModeType lockModeType);}

and an implementation ,

public abstract class JpaBaseRepository<T> implements BaseRepository<T> { .... }

currently if I want to use this generic dao I do the following

public interface AuditRepository extends BaseRepository<AuditableClass>{
//@Transactional
//@Query("SELECT ac FROM AuditableClass ac WHERE LOWER(ac.className) = LOWER(:className)")
AuditableClass getAuditableClassByClassName(@Param("className")  String className);

 Page<AuditingRecord> getAuditingRecordByUsername(String username);
}

and repository class

    @Repository
public class JpaAuditRepository extends JpaAuditBaseRepository<AuditableClass> 
implements AuditRepository {
@Override
public AuditableClass getAuditableClassByClassName(String className) {
    Specifications<AuditableClass> spec = where(
            (root, query, cb) -> {
                Path<String> p =  root.get(getEntityManager().getMetamodel().entity(AuditableClass.class).getSingularAttribute("className" , String.class));
                    return cb.equal(cb.lower(p), className.toLowerCase());
                });
    return find(AuditableClass.class, spec);//query.getSingleResult();
}


/* (non-Javadoc)
 * @see net.onecard.common.dataaccess.repository.audit.AuditRepository#getAuditingRecordByUsername(java.lang.String)
 */
@Override
public Page<AuditingRecord> getAuditingRecordByUsername(String username) {
    return findAll(AuditingRecord.class, new PageRequest(0, 10), " where username = :username", username);
}

}

is using inheritance in this case is the right way or should I use composition? and what are the pros and cons for each approach.

what I can think of as a pros for inheritance is that I can use this methods directly when injecting the auditRepository, but if I use composition I will need to write more code in the audit repository to expose the generic repository methods.

Thanks in advance

Aucun commentaire:

Enregistrer un commentaire