views:

450

answers:

1

I'm pretty new to DDD and JPA.

I'm working on a generic Repository with JPA and Spring. I really like the approaches exposed in the articles DDD: The Generic Repository and JPA implementation patterns: Data Access Objects. My aim is to build the perfect Repository in Domain-Driven Design with JPA and Spring.

I use an internal generic Repository to respect the domain’s contract with the data store, following the first article concepts.

public interface IInternalGenericRepository<K, E> {
    List<E> read(String query, Object[] params);
    void persist(E entity);
    void remove(E entity);
}

public class InternalGenericRepository<K, E> implements IInternalGenericRepository<K, E> {

    // Injected through DI in Spring
    @PersistenceContext
    private EntityManager em;

    private final Class<E> entityClass;

    public List<E> read(String query, Object[] params) {
        Query q = em.createQuery(query);
        for (int i = 0; i < params.length; i++) {
            q.setParameter(i + 1, params[i]);
        }
        return q.getResultList();
    }

    public void persist(E entity) {
        em.persist(entity);
    }

    // ...
}

Then, a Repository for a particular entity (for example: Organization) looks like this.

public interface IOrganizationRepository {
    List<Organization> organizationByCityName(String city);

    void create(Organization o);
}


@Repository
public class OrganizationRepository implements IOrganizationRepository {

    @Autowired
    IInternalGenericRepository<Long, Organization> internalRepository;

    public List<Organization> organizationByCityName(String city) {
        Object[] params = new Object[1];
        params[0] = city;
        return internalRepository.read("select o from Organization o where o.city.name like ?1",
                params);
    }

    @Override
    public void create(Organization o) {
        internalRepository.persist(o);
    }
}

It looks like a good way to implement DDD Repository with JPA and Spring. The OrganizationRepository is then injected in my services layer.

I'd like to have external views to avoid flaws or misconception. What do you think and how could it be improved?

Thanks.


Edit:

  • @Autowired on internalRepository - thanks to axtavt for pointing it out.
  • read() can be improved
A: 

First of all, it wouldn't work, because Spring can't inject EntityManager into internal object created with new. So, you have to write something like this:

public class OrganizationRepository implements IOrganizationRepository { 

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
        internalRepository.em = em;
    }
    ...
}

Also your read method looks a bit too generic. It misses some important use cases, such as getSigleResult and setFirstResult/setMaxResults.

Personally I prefer the second article apporach, because using composition you'll end up with having EntityManager in OrganizationRepository in order to implement features missed in IInternalGenericRepository.

axtavt
thanks for your answer. The aim is to inject the EntityManager only to the internal generic repository. I don't want to inject it in every specific repository. There should be a way to do that with Spring.
rochb
About the second approach, it has a big limit which is explained in the first article:Some objects have different requirements than others. A customer object may not be deleted, a PurchaseOrder cannot be updated, and a ShoppingCart object can only be created. When one is using the generic IRepository<T> interface this obviously causes problems in implementation.
rochb