I'm trying to create a custom MongoDB Repository that will handle transactions that are related to the business context only.
Since I want to expose these functions trough a interface, I'm having some trouble trying to inject the Interface on the service instead of the actual implementation, here's how it looks like ATM:
Interface:
public interface MongoRepositoryActions<T> {
<S extends T> S save(S entity);
}
Implementation
import com.mongodb.client.MongoClient;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
#ApplicationScoped
public class BaseMongoRepository<T> implements MongoRepositoryActions<T> {
#Inject
MongoClient mongoClient;
public <O extends T> O save(O object) {
// Do some stuff related to the business.
insertOperation(object);
return object;
}
}
Here's where I can't figure out how to use it.
The interface that I'm trying to expose:
public interface FruitRepository extends MongoRepositoryActions<Fruit> {
}
The service layer that has the repository:
import com.abinbev.b2b.core.MongoRepositoryActions;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
#ApplicationScoped
public class FruitService {
#Inject
MongoRepositoryActions<Fruit> fruitRepository; // This works fine
#Inject
FruitRepository repository; // This one doesn't
public Fruit saveFruit(Fruit fruit) {
return fruitRepository.save(fruit);
}
}
So if I try to run the application by injecting the interface, I get this error:
javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type com.abinbev.b2b.core.fruit.FruitRepository and qualifiers [#Default]
I've also tried to annotate the interface, but didn't work as well.
Maybe I'm missing something.
Further information can be provided!
If necessary I can also push a reproducible example on my Github account.
Related
I have a class that uses a custom json serializer via #JsonAdapter annotation:
import com.google.gson.annotations.JsonAdapter;
#JsonAdapter(IFooAdapter.class)
public interface IFoo {
//...
}
The IFooAdapter class has a dependency on IMyFactory that should be injected.
Injection is configured via Guice.CreateInjector() and bind(IMyFactory.class).to(MyFactoryImpl.class);.
But how can I get guice to inject this into my IFooAdapter class (shown below), which is only used in the #JsonAdapter annotation (shown above)?
import com.google.gson.JsonElement;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.inject.Inject;
public class IFooAdapter implements TypeAdapter<IFoo> {
#Inject IMyFactory myFactory; // <-- THIS INJECTION DOES NOT WORK
#Override public IFoo read(JsonReader in) {
return myFactory.create(/*...*/);
}
#Override public void write(JsonWriter out, IFoo value) {
//...
}
}
After injection via guice.InjectMembers(myMainClass), when I try to parse json via GsonBuilder().create().fromJson(), and debug IFooAdapter.read(), myFactory is Null.
(Which is really not that surprising, after all the IFooAdapter is not a member of myMainClass)
But how do I inject this properly?
You could try registering InstanceCreator for your adapter via something like
gsonBuilder.registerTypeAdapter(IFooAdapter.class, type -> injector.getInstance(type))
You would need to create Gson instance in a place, where you have access to injector (I would do it as #Provides method providing Gson instance with Injector parameter, then inject Gson wherever needed).
Repository Class
#Repository
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
#Query("SELECT a From AllFile a where a.guid = :guid")
AllFile findByGuid(#Param("guid") String guid);
}
Service Class
#Service
#Data
public class PipelineService implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Autowired
AllFileRepository allFileRepository;
}
Creating object SimpleJpaRepository<AllFile, Long>
PipelineService pipelineService = new PipelineService();
void methodX() {
AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext)
ApplicationContextUtils.getApplicationContext();
AutowireCapableBeanFactory autowireCapableBeanFactory =
applicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(pipelineService);
JpaRepository<AllFile, Long> jpaRepository = (SimpleJpaRepository<AllFile, Long>)
AopProxyUtils.getSingletonTarget(pipelineService.getAllFileRepository());
// (AllFileRepository) jpaRepository casting causes ClassCastException
}
Exception
java.lang.ClassCastException: org.springframework.data.jpa.repository.support.SimpleJpaRepository cannot be cast to ....repository.AllFileRepository
How to get an object of AllFileRepository interface? where I can access findByGuid("");
UPDATE
I am able to get the object of SimpleJpaRepository<AllFile, Long> belonging to interface AllFileRepository which inturn extends JpaRepository
public void initialize() {
AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext) ApplicationContextUtils
.getApplicationContext();
EntityManager em = applicationContext.getBean(EntityManager.class);
AllFileRepository allFileRepository = new JpaRepositoryFactory(em).getRepository(AllFileRepository.class);
allFileRepository.findByGuid(""); // Method is accessible here but the object is proxy
SimpleJpaRepository<AllFile, Long> simpleJpaRepository = (SimpleJpaRepository<AllFile, Long>) (AopProxyUtils
.getSingletonTarget(allFileRepository)); // But proxy resolution yeilds SimpleJpaRepository cannot
// explicitly cast to AllFileRepository
((AllFileRepository) simpleJpaRepository).findByGuid(""); // class
// org.springframework.data.jpa.repository.support.SimpleJpaRepository
// cannot be cast to class
// com.example.spingaoprepository.repository.AllFileRepository
// (org.springframework.data.jpa.repository.support.SimpleJpaRepository
// and
// com.example.spingaoprepository.repository.AllFileRepository
// are in unnamed module of loader 'app'
}
Here is a link to sample program
There is no use in trying to cast incompatible types into each other. This is not a Spring problem, it is Java basics. I already told you how to solve that in Spring, too: You need to provide a class actually implementing AllFileRepository and make sure Spring Data JPA uses it as a repository instead of the interface. In order to do that, you need to
change the interface annotation from #Repository to #NoRepositoryBean,
create class #Repository AllFileRepositoryImpl and there provide an implementation of AllFile findByGuid(String guid) doing something meaningful.
Then you can easily cast the way you want because your proxy's target object will be an AllFileRepositoryImpl instance.
I sent you a pull request which you just need to accept. The classes changed in the relevant commit #5705cbb look as follows:
package com.example.spingaoprepository.repository;
import com.example.spingaoprepository.model.AllFile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.NoRepositoryBean;
#NoRepositoryBean
public interface AllFileRepository extends JpaRepository<AllFile, Long> {
#Query("SELECT a From AllFile a where a.guid = :guid")
AllFile findByGuid(#Param("guid") String guid);
}
package com.example.spingaoprepository.repository;
import com.example.spingaoprepository.model.AllFile;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
#Repository
public class AllFileRepositoryImpl extends SimpleJpaRepository<AllFile, Long> implements AllFileRepository {
public AllFileRepositoryImpl(EntityManager em) {
super(AllFile.class, em);
}
#Override
public AllFile findByGuid(String guid) {
System.out.println("Finding AllFile by GUID " + guid);
return null;
}
}
package com.example.spingaoprepository.serializable;
import com.example.spingaoprepository.repository.AllFileRepository;
import com.example.spingaoprepository.service.PipelineService;
import com.example.spingaoprepository.utils.ApplicationContextUtils;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
public class PipelineConfigurer {
private ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext();
private PipelineService pipelineService = applicationContext.getBean(PipelineService.class);
public void initialize() {
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
autowireCapableBeanFactory.autowireBean(pipelineService);
AllFileRepository allFileRepository = (AllFileRepository) AopProxyUtils
.getSingletonTarget(pipelineService.getAllFileRepository());
allFileRepository.findByGuid("XY-123");
}
}
You are instantiating your PipelineService using the new keyword and then manually getting the Spring context in your methodX().
In principle, of course you 'could' do it all manually without Spring. But given both your repository and service are Spring beans, then it's better to let Spring handle the bean lifecyle. It's certainly easier anyway.
As PipelineService is a Spring bean, you should really make any class that uses it Spring aware too. So just annotate the class that with a Spring annotation like #Component, #Service, or perhaps #RestController if it's a controller.
If you insist on creating the bean manually then try -
ApplicationContext context = new AnnotationConfigApplicationContext();
PipelineService pipelineService = context.getBean(PipelineService.class);
Using #Autowired you are injecting an object implementing AllFileRepository. This singleton was created at startup hence you can just use is with allFileRepository.findByGuid("");
I'm working with Crud and Spring MVC, and get stuck with duplicating the same code with different repositories;
f.e. if i use Customers, Items and Logins i have
public interface CustomerRepository extends CrudRepository<Customer, Long>{
}
but with Item i have the same stuff:
public interface ItemRepository extends CrudRepository<Item, Long>{
}
Furthermore there are same methods in repositories, like findAll(), findDistinctBy...(), deleteAll(), etc.
As I know the good practice is escaping of code duplication, but where i should do it? In service, using special service interface and implement it by special class, and then specialise the service for each entity? Or I should do it in the my custom repository as they said in official Spring Data JPA reference documentation spring data reference $4.3.1?
My variant for code with service is below.
Common interface for all entities
import org.springframework.data.repository.CrudRepository;
public interface MyCrudService<T>{
<S extends CrudRepository<T, Long>> Iterable<T> findAll(S s);
}
Common class that realises the service
import org.springframework.data.repository.CrudRepository;
public class MyCrudServiceImpl<T> implements MyCrudService<T>{
#Override
public <S extends CrudRepository<T, Long>> Iterable<T> findAll(S s) {
return s.findAll();
}
}
Special service class for Unit entity
#Service
#Transactional
#AllArgsConstructor
public class MyUnitServiceTwo{
UnitRepository unitRepository;
MyCrudServiceImpl crudService;
public List<Unit> findAll1() {
return Lists.newArrayList(crudService.findAll(unitRepository));
}
}
I upgrade Jersey in my project to 2.26 version.
My code is:
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
public class ClassA extends AbstractContainerRequestValueFactory<ClassB> {
#Override
public ClassB provide() {
.....
}
}
AbstractContainerRequestValueFactory class was removed, and I didn't found how to fix this.
From https://github.com/jersey/jersey/commit/1f4614787c4cfddb5d9177c6c2a663b96ab673cc#diff-bcd9d3f0cfac8ea5e8e9a6b00119237b
commit we can see we should use below code instead.
private static final class BeanParamValueProvider implements Function<ContainerRequest, Object> {
Alternatively, we can use custom HK2 bindings, that are configured as part of Jersey application. Add jersey-hk2 dependency dependency in the classpath org.glassfish.jersey.inject:jersey-hk2
Define the Factory class to generate the instance based on the resource scopes
import org.glassfish.hk2.api.Factory;
import javax.ws.rs.ext.Provider;
#Provider
public class ClassA implements Factory<ClassB> {
#Override
public ClassB provide() {
// construct ClassB instance based on your requirement
//here I am simply returning the object
return new ClassB();
}
#Override
public void dispose(ClassB instance) {/**Noop**/}
}
Registering the custom factory class
For instance, I have to inject ClassB instance for every request then I can register the above factory with the scope of RequestScoped, in such case, for every request ClassA#provide will be called to create the value of ClassB instance that can be retrieved as #Context ClassB classB
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.ext.Provider;
#Provider
class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ClassA.class)
.to(ClassB.class)
.in(RequestScoped.class);
}
});
}
}
The following scopes are currently supported by Jersey
I have some highly repetitive code that i decided to move into an abstract class, for the sake of simplicity let's assume that the class looks like:
class AbstractEntityService<E extends MyEntity, REPO extends MyRepository<E>> {
#Autowired
private REPO repo;
... // methods
}
and I have about 10 separate classes that need this functionality
so i have
#Service
class MyService1 extends AbstractEntityService<MyEntity1, MyEntity1Repo> {}
#Service
class MyService2 extends AbstractEntityService<MyEntity2, MyEntity2Repo> {}
...
MyService1 ... MyService10 don't really do anything other than to cement the types for spring's autodetection to work.
I'm wondering if i can get away without defining them and injecting
#Autowired
private AbstractEntityService<MyEntity2, MyEntity2Repo> myEntity2Repo;
directly without defining the classes myself.
No, I'm pretty sure you cannot.
I think the best you can do is if you create the beans yourself in a config file.
import javax.inject.Inject;
import org.springframework.context.annotation.Bean;
#org.springframework.context.annotation.Configuration
public MyConfic {
#Inject MyEntity1Repo myEntity1Repo;
#Inject MyEntity2Repo myEntity2Repo;
#Bean(name="myEntity1Service")
AbstractEntityService<MyEntity1, MyEntity1Repo> myEntity2Service(){
return new AbstractEntityService<> myEntity1Service( myEntity2Repo );
}
#Bean(name="myEntity2Service")
AbstractEntityService<MyEntity2, MyEntity2Repo> myEntity2Service(){
return new AbstractEntityService<> myEntity2Service( myEntity2Repo );
}
}
At least this way you don't have to create a separate subclass per entity.
Mind you, now you cannot do injection by type since after erasure your myEnity1Service you will have the same type as myEnity2Service: AbstractEntityService. Using something like
#Inject #Named("myEntity2Service" )
AbstractEntityService<MyEntity2, MyEntity2Repo> myEntity2Service;