I am attempting to create a generic abstract service class that provides common crud operations to my service layer by bringing together Service layer Objects (DTO?), Data layer Entities and their corresponding DAOs.
The DAO layer is standard-issue abstraction where my DAOs extend an AbstractJpaImpl:
#Repository
public abstract class AbstractJpaBaseDaoImpl<K extends Serializable, E> implements BaseDao<K, E> {
//Dao implementation
}
The DAOs extend this class and implement their respective dao interface that extends BaseDao.
I wish to create something similar in my service layer, but how would i inject the dao itself?
public abstract class AbstractBaseCrudServiceImpl<K extends Serializable, B extends AbstractBaseCrudBean, P, D extends AbstractJpaBaseDaoImpl<K,P>>
implements BaseCrudService<K, B> {
protected Class<B> businessObject;
protected Class<P> persistObject;
protected Class<D> dao;
#SuppressWarnings("unchecked")
public AbstractBaseCrudServiceImpl() {
//Extract the class type by accessing this classes parameters by index <0,1...> so 0 is K and 1 is E.
this.businessObject = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
this.persistObject = (Class<P>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[2];
this.dao = (Class<D>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[3];
}
//stuff ...
#Transactional
#SuppressWarnings("unchecked")
#Override
public void remove(B businessObject) {
logger.debug("Remove " + getBusinessObjectCanonicalName() + " id= " + businessObject.getId());
try {
getDao().remove(businessObject.getId()); //DOES NOT RECOGNIZE REMOVE METHOD
} catch (Exception e) {
logger.error("Unable to delete " + getBusinessObjectCanonicalName() + " record id=" + businessObject.getId(), e);
}
}
//stuff ...
}
Whats the cleanest way to inject a service within this abstract using generics?
D extends AbstractJpaBaseDaoImpl aint cutting it. Is there a pattern i can follow?
You may try using the #PostConstruct annotated method to wire the variable in the base abstract classes with real instantiated beans. These abstract classes can contacin basic CRUd operation implementations. Example code provided below. I hope I helped your query in some way.
#PostConstruct public void setupService() {
baseDao = userDao;
}
What are the definitions of some of the other types? Such as AbstractBaseCrudBean, BaseCrudService, etc.? There are so many compilation errors it is hard to get down to what your root issue is.
However, the reason the simple generic declaration is not cutting it is that there is no instance on which to invoke the methods of D. You would need something like:
private final D dao;
And then decorate that with #Inject #Named (since after erasure you will likely have many DAO's of the same type). So, each implementation of a DAO will need to have a unique string name, which may or not be a big deal.
Related
Question:
In order to Inject all subclasses of a superclass with no common interface, I created an interface tightly-coupled to said superclass, that every "properly" written subclass is supposed to implement.
This works, but seems insane. Was there a better way?
A simple cast do not work, as the Instance holds only a proxy that do not resolves to any real subclass of the interface when called. This results in a ClassCastException.
Some context:
I was recently tasked to provide framework code for an application. In this application, several data transfer objects are mapping from and to service level POJOs, but their mappings are not always trivial. Dozer is used to do most of the work and to avoid boilerplate code.
In the specific cases requiring explicit mapping instructions, the current recommendation with Dozer is to use the API driven mapping. All the BeanMappingBuilder subclasses, defining the mappings, should be added to the Dozer mapper upon initialisation.
In order to keep all the work needed to add a new BeanMappingBuilder in one place, I came with a convoluted use of dependancy injection that will automatically add it to the Dozer mapper, despite it having no common interface, only a common superclass with the others.
Some code:
The interface:
#Local
public interface DtoBeanMappingBuilder {
BeanMappingBuilder get();
}
Subclass example:
#Stateless
public class SomeDtoMappingBuilder extends BeanMappingBuilder implements DtoBeanMappingBuilder {
#Override
public BeanMappingBuilder get() {
return this;
}
#Override
protected void configure() {
mapping(
// Some mapping...
);
}
}
Mapper with injection point:
#Singleton
#Startup
public class DtoBeanMapper {
private DozerBeanMapper innerMapper;
#Inject
#Any
private Instance<DtoBeanMappingBuilder> mappingBuilders;
public <D> D map(Object source, Class<D> destinationClass) {
return innerMapper.map(source, destinationClass);
}
#PostConstruct
private void init() {
innerMapper = new DozerBeanMapper();
mappingBuilders.forEach(mb -> innerMapper.addMapping(mb.get()));
}
}
I am looking for a way to get an entity type or class name from an instance implementing Spring Data JPA Repository interface.
I have got a number of interfaces extending a base interface that extends Repository interface and that defines some base queries.
#NoRepositoryBean
public interface EnumerationRepository<T extends IDatabaseEnumeration> extends Repository<T, String> {
// ...
}
public interface SampleEnumerationRepository extends EnumerationRepository<SampleEnumeration> {
}
Spring allows me to inject implementations of all these interfaces as a collection into a bean
#Autowired
private Collection<EnumerationRepository<? extends IDatabaseEnumeration>> repositories;
I wanted to put all of these implementations into a Map for an easy access inside a generic method. I wanted to use the entity type or name as the key but I am not sure where to get it. Is there any way to get one of these attributes? Preferably the class type.
I was able to achieve the desired behaviour with a query like this. But I would like to avoid this solution if possible as it creates a dependency on the underlying DB.
#Query(value = "select '#{#entityName}' from dual", nativeQuery = true)
public String getEntityName();
#jens-schauder 's answer did not work in my case but it showed me the right direction.
Spring injected me the implementation of my interface extending the spring Repository interface. Therefore I had to get all interfaces, filter out spring internal ones, so I ended up with the one I defined. This one however was not generic yet so I had to get its super interface that had the generic type.
I don't really care about performance as this method is called only during Spring container initialization.
Fortunatelly polymorphism works quite well in this case. So I only had to implement default method on the super interface. Like this
#NoRepositoryBean
public interface EnumerationRepository<T extends IDatabaseEnumeration> extends Repository<T, String> {
// ...
default Class<T> getEntityClass() {
Type[] interfaces = getClass().getInterfaces();
for (Type t : interfaces) {
if (t instanceof Class<?>) {
Class<?> clazz = (Class<?>) t;
if (clazz.getPackage().getName().startsWith(ApplicationConst.BASE_PACKAGE)) {
// Repositories should implement only ONE interface from application packages
Type genericInterface = clazz.getGenericInterfaces()[0];
return (Class<T>) ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
}
}
}
return null;
}
}
I hope this might be useful to other users facing similar issues.
You can get the type arguments if they are bound to a type from the interface, using this answer: https://stackoverflow.com/a/1901275/66686
Class<T> persistentClass = (Class<T>)
((ParameterizedType)yourRepo.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
Try this
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import org.springframework.aop.support.AopUtils;
import org.springframework.data.repository.Repository;
import org.springframework.util.Assert;
import java.util.List;
public class Repospector {
private static final TypeResolver RESOLVER = new TypeResolver();
public Class<?> getEntityClass(Repository<?, ?> repository) {
Class<?> targetClass = AopUtils.getTargetClass(repository);
List<ResolvedType> types = RESOLVER.resolve(targetClass).typeParametersFor(Repository.class);
Assert.state(types.size() == 2, "Call the whambulance!");
return types.get(0).getErasedType();
}
}
I fix this problem by use typetools https://github.com/jhalterman/typetools
this is my code:
Class<?>[] classes = TypeResolver.resolveRawArguments(JpaRepository.class, jpaRepositoryInstance.getClass());
Class<?> entityClz = classes[0];
I have a typed service interface:
public interface BaseArticleService<T extends BaseArticle> {
T getById(Long id);
}
And have two interfaces which extends it:
public interface AccArticleService extends BaseArticleService<AccArticle> {
}
public interface ExpArticleService extends BaseArticleService<ExpArticle> {
Long getCount();
}
Then I have a similar architecture for controllers:
public abstract class BaseArticleController<T extends BaseArticle> {
#Autowired
BaseArticleService<T> baseArticleService;
}
And:
#Controller
#RequestMapping(value = "/exp/articles")
public class ExpArticleController extends BaseArticleController<ExpArticle> {
}
#Controller
#RequestMapping(value = "/acc/articles")
public class AccArticleController extends BaseArticleController<AccArticle> {
}
Now if I want to get my ExpArticleService instance of the BaseArticleService that is injected in my BaseController, how can I achieve this?
If I do this:
public BaseArticleService<ExpArticle> getExpArticleService() {
return super.baseArticleService;
}
then I cannot call my getCount() method like getExpArticleService().getCount()
And cannot do this neither:
public ExpArticleService getExpArticleService() {
return super.baseArticuloService;
}
So what's the solution? Maybe inject another ExpArticleService in my ExpArticleController?
When using DI you are by definition relying on the interface. If you inject BaseArticleService then the only method available to you are the methods defined in this interface. In your case T getById(Long id);
Your way is not a bad way, you have a generic super class which allow you to initialise the interface with different parameters very easily, but you shouldn't expect to explicitely get any of the specific implementation. (maybe you could check and cast but it is not clean there).
As already stated you should extract the specific implementation in another class and directly inject this one in the controller requiring it. Keep your generic though, it is a nice way to to handle all the parametrized methods that will be shared between your controllers
I'm using Spring Data JPA and I have a bunch of repositories like this one:
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
Under repositories I have services and a lot of them need to have implemented method findOrCreate(String name) like this one:
#Override
#Transactional
public List<Customer> findOrCreate(final String name) {
checkNotNull(name);
List<Customer> result = this.customerRepository.findByName(name);
if (result.isEmpty()) {
LOGGER.info("Cannot find customer. Creating a new customer. [name={}]", name);
Customer customer = new Customer(name);
return Arrays.asList(this.customerRepository.save(customer));
}
return result;
}
I would like to extract method to the abstract class or somewhere to avoid implementing it for each services, testing and so on.
Abstract class can be look like this:
public abstract class AbstractManagementService<T, R extends JpaRepository<T, Serializable>> {
protected List<T> findOrCreate(T entity, R repository) {
checkNotNull(entity);
checkNotNull(repository);
return null;
}
}
And the problem is it due to the fact that I need to find object by name as a string before creating a new one. Of course interface JpaRepository doesn't offer this method.
How can I solve this problem?
Best Regards
Create a custom JpaRepository implementation that includes this behaviour. See this post for an example of writing a custom JpaRepository implementation.
I have a number of simple object types that need to be persisted to a database. I am using Spring JPA to manage this persistence. For each object type I need to build the following:
import org.springframework.data.jpa.repository.JpaRepository;
public interface FacilityRepository extends JpaRepository<Facility, Long> {
}
public interface FacilityService {
public Facility create(Facility facility);
}
#Service
public class FacilityServiceImpl implements FacilityService {
#Resource
private FacilityRepository countryRepository;
#Transactional
public Facility create(Facility facility) {
Facility created = facility;
return facilityRepository.save(created);
}
}
It occurred to me that it may be possible to replace the multiple classes for each object type with three generics based classes, thus saving a lot of boilerplate coding. I am not exactly sure how to go about it and in fact if it is a good idea?
First of all, I know we're raising the bar here quite a bit but this is already tremendously less code than you had to write without the help of Spring Data JPA.
Second, I think you don't need the service class in the first place, if all you do is forward a call to the repository. We recommend using services in front of the repositories if you have business logic that needs orchestration of different repositories within a transaction or has other business logic to encapsulate.
Generally speaking, you can of course do something like this:
interface ProductRepository<T extends Product> extends CrudRepository<T, Long> {
#Query("select p from #{#entityName} p where ?1 member of p.categories")
Iterable<T> findByCategory(String category);
Iterable<T> findByName(String name);
}
This will allow you to use the repository on the client side like this:
class MyClient {
#Autowired
public MyClient(ProductRepository<Car> carRepository,
ProductRepository<Wine> wineRepository) { … }
}
and it will work as expected. However there are a few things to notice:
This only works if the domain classes use single table inheritance. The only information about the domain class we can get at bootstrap time is that it will be Product objects. So for methods like findAll() and even findByName(…) the relevant queries will start with select p from Product p where…. This is due to the fact that the reflection lookup will never ever be able to produce Wine or Car unless you create a dedicated repository interface for it to capture the concrete type information.
Generally speaking, we recommend creating repository interfaces per aggregate root. This means you don't have a repo for every domain class per se. Even more important, a 1:1 abstraction of a service over a repository is completely missing the point as well. If you build services, you don't build one for every repository (a monkey could do that, and we're no monkeys, are we? ;). A service is exposing a higher level API, is much more use-case drive and usually orchestrates calls to multiple repositories.
Also, if you build services on top of repositories, you usually want to enforce the clients to use the service instead of the repository (a classical example here is that a service for user management also triggers password generation and encryption, so that by no means it would be a good idea to let developers use the repository directly as they'd effectively work around the encryption). So you usually want to be selective about who can persist which domain objects to not create dependencies all over the place.
Summary
Yes, you can build generic repositories and use them with multiple domain types but there are quite strict technical limitations. Still, from an architectural point of view, the scenario you describe above shouldn't even pop up as this means you're facing a design smell anyway.
This is very possible! I am probably very late to the party. But this will certainly help someone in the future. Here is a complete solution that works like a charm!
Create BaseEntity class for your entities as follows:
#MappedSuperclass
public class AbstractBaseEntity implements Serializable{
#Id #GeneratedValue
private Long id;
#Version
private int version;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public AbstractBaseEntity() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
// getters and setters
}
Create a generic JPA Repository interface for your DAO persistence as follows:
NB. Remember to put the #NoRepositoryBean so that JPA will not try to find an implementation for the repository!
#NoRepositoryBean
public interface AbstractBaseRepository<T extends AbstractBaseEntity, ID extends Serializable>
extends JpaRepository<T, ID>{
}
Create a Base Service class that uses the above base JPA repository. This is the one that other service interfaces in your domain will simply extend as follows:
public interface AbstractBaseService<T extends AbstractBaseEntity, ID extends Serializable>{
public abstract T save(T entity);
public abstract List<T> findAll(); // you might want a generic Collection if u prefer
public abstract Optional<T> findById(ID entityId);
public abstract T update(T entity);
public abstract T updateById(T entity, ID entityId);
public abstract void delete(T entity);
public abstract void deleteById(ID entityId);
// other methods u might need to be generic
}
Then create an abstract implementation for the base JPA repository & the basic CRUD methods will also be provided their implementations as in the following:
#Service
#Transactional
public abstract class AbstractBaseRepositoryImpl<T extends AbstractBaseEntity, ID extends Serializable>
implements AbstractBaseService<T, ID>{
private AbstractBaseRepository<T, ID> abstractBaseRepository;
#Autowired
public AbstractBaseRepositoryImpl(AbstractBaseRepository<T, ID> abstractBaseRepository) {
this.abstractBaseRepository = abstractBaseRepository;
}
#Override
public T save(T entity) {
return (T) abstractBaseRepository.save(entity);
}
#Override
public List<T> findAll() {
return abstractBaseRepository.findAll();
}
#Override
public Optional<T> findById(ID entityId) {
return abstractBaseRepository.findById(entityId);
}
#Override
public T update(T entity) {
return (T) abstractBaseRepository.save(entity);
}
#Override
public T updateById(T entity, ID entityId) {
Optional<T> optional = abstractBaseRepository.findById(entityId);
if(optional.isPresent()){
return (T) abstractBaseRepository.save(entity);
}else{
return null;
}
}
#Override
public void delete(T entity) {
abstractBaseRepository.delete(entity);
}
#Override
public void deleteById(ID entityId) {
abstractBaseRepository.deleteById(entityId);
}
}
How to use the above abstract entity, service, repository, and implementation:
Example here will be a MyDomain entity. Create a domain entity that extends the AbstractBaseEntity as follows:
NB. ID, createdAt, updatedAt, version, etc will be automatically be included in the MyDomain entity from the AbstractBaseEntity
#Entity
public class MyDomain extends AbstractBaseEntity{
private String attribute1;
private String attribute2;
// getters and setters
}
Then create a repository for the MyDomain entity that extends the AbstractBaseRepository as follows:
#Repository
public interface MyDomainRepository extends AbstractBaseRepository<MyDomain, Long>{
}
Also, Create a service interface for the MyDomain entity as follows:
public interface MyDomainService extends AbstractBaseService<MyDomain, Long>{
}
Then provide an implementation for the MyDomain entity that extends the AbstractBaseRepositoryImpl implementation as follows:
#Service
#Transactional
public class MyDomainServiceImpl extends AbstractBaseRepositoryImpl<MyDomain, Long>
implements MyDomainService{
private MyDomainRepository myDomainRepository;
public MyDomainServiceImpl(MyDomainRepository myDomainRepository) {
super(myDomainRepository);
}
// other specialized methods from the MyDomainService interface
}
Now use your `MyDomainService` service in your controller as follows:
#RestController // or #Controller
#CrossOrigin
#RequestMapping(value = "/")
public class MyDomainController {
private final MyDomainService myDomainService;
#Autowired
public MyDomainController(MyDomainService myDomainService) {
this.myDomainService = myDomainService;
}
#GetMapping
public List<MyDomain> getMyDomains(){
return myDomainService.findAll();
}
// other controller methods
}
NB. Make sure that the AbstractBaseRepository is annotated with #NoRepositoryBean so that JPA does not try to find an implementation for the bean.
Also the AbstractBaseServiceImpl must be marked abstract, otherwise JPA will try to autowire all the children daos of the AbstractBaseRepository in the constructor of the class leading to a NoUniqueBeanDefinitionException since more than 1 daos (repository) will be injected when the bean is created!
Now your service, repository, and implementations are more reusable. We all hate boilerplate!
Hope this helps someone.
I am working a project to create the generic repository for cassandra with spring data.
Firstly create a repository interface with code.
StringBuilder sourceCode = new StringBuilder();
sourceCode.append("import org.springframework.boot.autoconfigure.security.SecurityProperties.User;\n");
sourceCode.append("import org.springframework.data.cassandra.repository.AllowFiltering;\n");
sourceCode.append("import org.springframework.data.cassandra.repository.Query;\n");
sourceCode.append("import org.springframework.data.repository.CrudRepository;\n");
sourceCode.append("\n");
sourceCode.append("public interface TestRepository extends CrudRepository<Entity, Long> {\n");
sourceCode.append("}");
Compile the code and get the class, I use org.mdkt.compiler.InMemoryJavaCompiler
ClassLoader classLoader = org.springframework.util.ClassUtils.getDefaultClassLoader();
compiler = InMemoryJavaCompiler.newInstance();
compiler.useParentClassLoader(classLoader);
Class<?> testRepository = compiler.compile("TestRepository", sourceCode.toString());
And initialize the repository in spring data runtime. This is a little tricky as I debug the SpringData code to find how it initialize a repository interface in spring.
CassandraSessionFactoryBean bean = context.getBean(CassandraSessionFactoryBean.class);
RepositoryFragments repositoryFragmentsToUse = (RepositoryFragments) Optional.empty().orElseGet(RepositoryFragments::empty);
CassandraRepositoryFactory factory = new CassandraRepositoryFactory(
new CassandraAdminTemplate(bean.getObject(), bean.getConverter()));
factory.setBeanClassLoader(compiler.getClassloader());
Object repository = factory.getRepository(testRepository, repositoryFragmentsToUse);
Now you can try the save method of the repository and you can try other methods such as findById.
Method method = repository.getClass().getMethod("save", paramTypes);
T obj = (T) method.invoke(repository, params.toArray());
A full sample code and implementation I have put in this repo
https://github.com/maye-msft/generic-repository-springdata.
You can extend it to JPA with the similar logic.