Inject Spring repositories and their provided class - java

Is there a way to extract all Repositories as well as the Class<T> they are providing?
I have some Repositories which are annotated with a Qualifier:
#NetworkDataProvider
#Repository
public interface SwitchRepository extends CrudRepository<Switch, SwitchPK>
The beans they provide are annotated with metadata which defines the way they will be displayed in the GUI:
#Entity
#Table(...)
public class Switch implements Serializable {
#Column(name = "switch_name")
#NotNull
#UIName(value = "name of switch")
#UIPrio(value = 2)
private String name;
Now I have to extract all Repositories and their corresponding classes:
#Autowired
#NetworkDataProvider
List<Repository<?>> repositories;
public List<RepositoryClassTuple> getAllNetworkDataProvider() {
return repositories.map(r ->
new RepositoryClassTuple(r, /* how to do this */ r.getProidedClass())).asList();
}
Is there any Way to do this? I really need the Annotations of the repository provided data beans.

Spring Data has a type called Repositories that takes a ListableBeanFactory which can then be used to inspect the repositories:
Repositories repositories = new Repositories(beanFactory);
for (Class<?> domainType : repositories) {
RepositoryInformation info = repositories.getRepositoryInformationFor(domainType);
…
}
I still wonder why you need to mess with this low-level stuff. Really nothing that normal application code should do 🙃.

You can define an interface :
public interface NetworkRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
Class<T> getType();
}
Then you can define your implementions like this :
public interface PersonRepository extends NetworkRepository<Person, Long> {
#Override
default Class<Person> getType() {
return Person.class;
}
}
public interface AnimalRepository extends NetworkRepository<Animal, Long> {
#Override
default Class<Animal> getType() {
return Animal.class;
}
}
Then to get them all :
#Autowired
Collection<NetworkRepository> networkRepositories;
Finally you can use the getType() method to get your class information.
IMPORTANT : you have to declare NetworkRepository in a package not scannable by Spring Data.

You can create an interface named MyRepository
public interface MyRepository{
}
Then, all your repository classes must implement your interface:
#Repository("foo")
public class FooExample implements MyRepository{
}
#Repository("bar")
public class BarExample implements MyRepository{
}
Finally you can have a map of MyRepository beans injected:
#Component
public class ExampleConsumer {
private final Map<String, MyRepository> repositories;
#Autowired
public ExampleConsumer(Map<String, MyRepository> repositories) {
this.examples = examples;
}
}
In this case the map will contain two entries:
"foo" -> FooExample instance
"bar" -> BarExample instance
Another way is to use java Reflection to read the annotation
Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
Here there is a tutorial
http://tutorials.jenkov.com/java-reflection/annotations.html

Related

JPA repository with single table inheritance (hibernate)

I have created two entites (RegularEmployee and ContactEntity) that extends the Employee entity.
#Entity
#Table(name="employees")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue(value="employee")
public class Employee {
#Id
#GeneratedValue
private Long id;
private String name;
...
Im using SINGLE_TABLE inheritance for this implementations, and created a generic JpaRepository for manipulating data:
#Repository
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
}
I've created also the Service class that autowire three instance of these generic repositories, and specific methods for each class.
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository<Employee> employeeRepo;
#Autowired
private EmployeeRepository<RegularEmployee> regularRepo;
#Autowired
private EmployeeRepository<ContractEmployee> contractRepo;
public List<Employee> getAllEmployee() {
return employeeRepo.findAll();
}
public List<RegularEmployee> getAllRegularEmployee(){
return regularRepo.findAll();
}
public List<ContractEmployee> getAllContractEmployee() {
return contractRepo.findAll();
}
...
My problem is, that when I try to find all regular employees or contract employees, I always get all type of employees (employees, regular employees and contract employees all together).
I do not know why it behaves like this, even though the method's signature says it returns the appropriate type.
One option is to use #Query in EmployeeRepository:
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
#Query("from RegularEmployee")
List<RegularEmployee> findAllRegularEmployees();
}
A second option is to create an additional repository for each subclass of Employee. For RegularEmployee would be:
public interface RegularEmployeeRepository extends EmployeeRepository<RegularEmployee>{}
This is how to use both options in EmployeeService:
#Service
public class EmployeeService {
#Autowired EmployeeRepository<Employee> employeeRepo;
#Autowired EmployeeRepository<RegularEmployee> regularRepoT;
#Autowired RegularEmployeeRepository regularRepo;
#PostConstruct
public void init(){
employeeRepo.save(new ContractEmployee("Mark"));
employeeRepo.save(new RegularEmployee("Luke"));
employeeRepo.findAll().forEach(System.out::println); // prints Mark and Luke
regularRepo.findAll().forEach(System.out::println); // prints only Luke
regularRepoT.findAllRegularEmployees().forEach(System.out::println); // prints only Luke
}
//...
}
Also you can omit #Repository on top of EmployeeRepository. Spring already knows that is a Repository because it extends JpaRepository.
Side note: if you don't need EmployeeRepository to be created by Spring add #NoRepositoryBean on top of its class.
I've been able to replicate what you've encountered using your generic EmployeeRepository. As an alternative I created two separate repositories: ContractualEmployeeRepository and RegularEmployeeRepository.
public interface ContractualEmployeeRepository extends JpaRepository<ContractualEmployee, String> {
}
public interface RegularEmployeeRepository extends JpaRepository<RegularEmployee, String> {
}
Then, I created an integration test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Main.class})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class})
#TestPropertySource(locations="classpath:application-test.properties")
#DatabaseSetup("classpath:SingleTableDataSet.xml")
public class IntegrationTest {
#Autowired
private RegularEmployeeRepository regularEmployeeRepository;
#Autowired
private ContractualEmployeeRepository contractualEmployeeRepository;
#Test
public void test() {
Assert.assertEquals(6, regularEmployeeRepository.findAll().size());
Assert.assertEquals(4, contractualEmployeeRepository.findAll().size());
}
}
and it works.
As for the usage and limitations of Generics in Spring Data JPA repositories: https://stackoverflow.com/a/19443031/14180014 He had done a great job explaining it.

Autowiring any of an Interface using Spring Boot

I want autowire a class which implements in a Component. Here is a part of the interface:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class GenericResourceHandlerService<T extends ResourceRequest, A extends ResultType, B extends ResourceService<T, A>> {
private final B service;
public Response get(String x) {
various checks(x, service.getType());
B res = service.get(x);
if (res!= null) {
return Response.status(Response.Status.OK).entity(res).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
}
Then, a class which implements ResourceService would look like this:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TypeOneService implements EntityService<CityRequest, City> {
private final Repository repository;
#Override
public ResultType getType() {
return ResultType.TYPE_ONE;
}
#Timed
public TYPE_ONE get(String resource) {
return repository.get(resource);
}
}
And the interface itself, looks like this:
public interface EntityService<T extends EntityRequest, A extends ReturnableEntity> {
ResourceType getResourceType();
A get(String resource);
}
Now, I have a set of controllers which tries to autowire GenericResourceHandlerService and call it's get method. Which looks like this:
public class TypeOneController {
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
Or this:
public class TypTwoController {
private final TypeTwoService typeTwoService;
private final GenericResourceHandlerService<TypeTwoRequest, TypeTwo, TypeTwoService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
This compiles but when the app starts then, then I get the following error message:
Parameter 0 of constructor in path.GenericResourceHandlerService required a single bean, but 2 were found:
- typeOneSerivce: defined in file [C:\Path\TypeOneService.class]
- typeTwoService: defined in file [C:\Path\TypeTwoService.class]
I think this is because, Spring Boot can't work out which one to service to autowire with. Is it possible what I am trying to do?
Spring tries to inject a bean to resolve the GenericResourceHandlerService.service but service has type B and B extends ResourceService. And spring found 2 beans implementing this interface so doesn't know which implementation to autowire..
You can put #Qualifier on field service but I imagine you will lost the genericity of this type GenericResourceHandlerService
Maybe the best way is to let the controller pass through the implementation in the GenericResourceHandlerService and let this last as a simple pojo..not a spring bean (so remove #Service on GenericResourceHandlerService
Like this
public class TypeOneController {
#Autowired
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService = new GenericResourceHandlerService(typeOneService);
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}

restrict user to autowire only interface

I have the following interface
public interface MTS{
public SrvResponse sendMessage(MTSModel msg);
public SrvResponse sendMessage(MTSModel msg, Type type);
}
the implementation
#Component
public class MTSImpl implements MTS {...}
Can I restrict user to autowire only interface private #Autowired MTS service; not MTSImpl in spring boot ?
Can I restrict user to autowire only interface private #Autowired MTS
service; not MTSImpl in spring boot ?
Spring doesn't provide such restrictions for injections.
As alternative you could "protected" your implementation class from clients by making it package private (the default access modifier) in a package that they don't use :
package foo.internal;
class MTSImpl implements MTS {
...
}
Or to hide it completely you could define it as an anonymous class :
#Bean
public MTS getMts() {
return new MTS() {
#Override
public SrvResponse sendMessage(MTSModel msg){
...
}
#Override
public SrvResponse sendMessage(MTSModel msg, Type type){
...
}
};
}

Spring Data JPA method + REST: Enum to Integer conversion

I've got an endpoint:
/api/offers/search/findByType?type=X
where X should be an Integer value (an ordinal value of my OfferType instance), whereas Spring considers X a String and will be applying its StringToEnumConverterFactory with the StringToEnum convertor.
public interface OfferRepository extends PagingAndSortingRepository<Offer, Long> {
List<Offer> findByType(#Param("type") OfferType type);
}
So I wrote a custom Converter<Integer, OfferType> which simply get a instance by the given ordinal number:
public class IntegerToOfferTypeConverter implements Converter<Integer, OfferType> {
#Override
public OfferType convert(Integer source) {
return OfferType.class.getEnumConstants()[source];
}
}
Then I registered it properly with a Configuration:
#EnableWebMvc
#Configuration
#RequiredArgsConstructor
public class GlobalMVCConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new IntegerToOfferTypeConverter());
}
}
And I was expected that all requests to findByType?type=X will pass through my converter, but they do not.
Is any way to say that all enums defined as a request parameters have to be provided as an Integer? Furthermore, is any way to say it globally, not just for a specific enum?
EDIT: I've found IntegerToEnumConverterFactory in my classpath that does all I need. And it is registered with DefaultConversionService which is a default service for conversion. How can that be applied?
EDIT2: It's such a trivial thing, I was wondering if there is a property to turn enum conversion on.
EDIT3: I tried to write a Converter<String, OfferType> after I had got String from TypeDescriptor.forObject(value), it didn't help.
EDIT4: My problem was that I had placed custom converter registration into a MVC configuration (WebMvcConfigurerAdapter with addFormatters) instead of a REST Repositories one (RepositoryRestConfigurerAdapter with configureConversionService).
Spring parses the query parameters as Strings. I believe it always uses Converter<String, ?> converters to convert from the query parameters to your repository methods parameters. It uses an enhanced converter service, since it registers its own converters such as Converter<Entity, Resource>.
Therefore you have to create a Converter<String, OfferType>, e.g.:
#Component
public class StringToOfferTypeConverter implements Converter<String, OfferType> {
#Override
public OfferType convert(String source) {
return OfferType.class.getEnumConstants()[Integer.valueOf(source)];
}
}
And then configure this converter to be used by the Spring Data REST, in a class extending RepositoryRestConfigurerAdapter:
#Configuration
public class ConverterConfiguration extends RepositoryRestConfigurerAdapter {
#Autowired
StringToOfferTypeConverter converter;
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(converter);
super.configureConversionService(conversionService);
}
}
I tried to add this to the basic tutorial, added a simple enum to the Person class:
public enum OfferType {
ONE, TWO;
}
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private OfferType type;
public OfferType getType() {
return type;
}
public void setType(OfferType type) {
this.type = type;
}
}
And when I call:
http://localhost:8080/people/search/findByType?type=1
I get the result without errors:
{
"_embedded" : {
"people" : [ ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/search/findByType?type=1"
}
}
}
To implement a global Enum converter, you have to create a factory and register it in the configuration using the method: conversionService.addConverterFactory(). The code below is a modified example from the documentation:
public class StringToEnumFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum(targetType);
}
private final class StringToEnum<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
Integer index = Integer.valueOf(source);
return enumType.getEnumConstants()[index];
}
}
}

Spring 3.2 Autowire generic types

So I have a number of generics in Spring 3.2 and ideally my architecture would look something like this.
class GenericDao<T>{}
class GenericService<T, T_DAO extends GenericDao<T>>
{
// FAILS
#Autowired
T_DAO;
}
#Component
class Foo{}
#Repository
class FooDao extends GenericDao<Foo>{}
#Service
FooService extends GenericService<Foo, FooDao>{}
Unfortunately with multiple implementations of the generics the autowiring throws an error about multiple matching bean definitions. I assume this is because #Autowired processes before type erasure. Every solution I've found or come up with looks ugly to me or just inexplicably refuses to work. What is the best way around this problem?
How about adding a constructor to the GenericService and move the autowiring to the extending class, e.g.
class GenericService<T, T_DAO extends GenericDao<T>> {
private final T_DAO tDao;
GenericService(T_DAO tDao) {
this.tDao = tDao;
}
}
#Service
FooService extends GenericService<Foo, FooDao> {
#Autowired
FooService(FooDao fooDao) {
super(fooDao);
}
}
Update:
As of Spring 4.0 RC1, it is possible to autowire based on generic type, which means that you can write a generic service like
class GenericService<T, T_DAO extends GenericDao<T>> {
#Autowired
private T_DAO tDao;
}
and create multiple different Spring beans of it like:
#Service
class FooService extends GenericService<Foo, FooDao> {
}
Here is a closest solution. The specialized DAOs are annotated at the business layer. As in the question from OP, the best effort would be having an annotated DAO in the EntityDAO generic template itself. Type erasure seems to be not allowing the specialized type information to get passed onto the spring factories [resulting in reporting matching beans from all the specialized DAOs]
The Generic Entity DAO template
public class EntityDAO<T>
{
#Autowired
SessionFactory factory;
public Session getCurrentSession()
{
return factory.getCurrentSession();
}
public void create(T record)
{
getCurrentSession().save(record);
}
public void update(T record)
{
getCurrentSession().update(record);
}
public void delete(T record)
{
getCurrentSession().delete(record);
}
public void persist(T record)
{
getCurrentSession().saveOrUpdate(record);
}
public T get(Class<T> clazz, Integer id)
{
return (T) getCurrentSession().get(clazz, id);
}
}
The Generic Entity Based Business Layer Template
public abstract class EntityBusinessService<T>
implements Serializable
{
public abstract EntityDAO<T> getDAO();
//Rest of code.
}
An Example Specialized Entity DAO
#Transactional
#Repository
public class UserDAO
extends EntityDAO<User>
{
}
An Example Specialized Entity Business Class
#Transactional
#Service
#Scope("prototype")
public class UserBusinessService
extends EntityBusinessService<User>
{
#Autowired
UserDAO dao;
#Override
public EntityDAO<User> getDAO()
{
return dao;
}
//Rest of code
}
You can remove the #autowire annotation and perform delayed “autowire” using #PostConstruct and ServiceLocatorFactoryBean.
Your GenericService will look similar to this
public class GenericService<T, T_DAO extends GenericDao<T>>{
#Autowired
private DaoLocator daoLocatorFactoryBean;
//No need to autowried, autowireDao() will do this for you
T_DAO dao;
#SuppressWarnings("unchecked")
#PostConstruct
protected void autowireDao(){
//Read the actual class at run time
final Type type;
type = ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[1];
//figure out the class of the fully qualified class name
//this way you can know the bean name to look for
final String typeClass = type.toString();
String daoName = typeClass.substring(typeClass.lastIndexOf('.')+1
,typeClass.length());
daoName = Character.toLowerCase(daoName.charAt(0)) + daoName.substring(1);
this.dao = (T_DAO) daoLocatorFactoryBean.lookup(daoName);
}
daoLocatorFactoryBean does the magic for you.
In order to use it you need to add an interface similar to the one below:
public interface DaoLocator {
public GenericDao<?> lookup(String serviceName);
}
You need to add the following snippet to your applicationContext.xml
<bean id="daoLocatorFactoryBean"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface"
value="org.haim.springframwork.stackoverflow.DaoLocator" />
</bean>
This is a nice trick and it will save you little boilerplate classes.
B.T.W I do not see this boilerplate code as a big issue and the project I working for uses matsev approach.
Why do you want a generic service ? Service classes are meant for specific units of work involving multple entities. You can just inject a repository straight into a controller.
Here is an example of generic repository with constructor argument, you could also make each method Generic instead and have no constructor argument. But each method call would require class as parameter:
public class DomainRepository<T> {
#Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
public DomainRepository(Class genericType) {
this.genericType = genericType;
}
#Transactional(readOnly = true)
public T get(final long id) {
return (T) sessionFactory.getCurrentSession().get(genericType, id);
}
Example of bean definition for the generic repository - you could have multple different beans, using different contstructor args.
<bean id="tagRepository" class="com.yourcompnay.data.DomainRepository">
<constructor-arg value="com.yourcompnay.domain.Tag"/>
</bean>
Depdncy injection of bean using resource annotation
#Resource(name = "tagRepository")
private DomainRepository<Tag> tagRepository;
And this allows the Domainreposiroty to be subclassed for specific entities/methods, which woul dallow autowiring :
public class PersonRepository extends DomainRepository<Person> {
public PersonRepository(){
super(Person.class);
}
...
You should use autowiring in classes which extends these generics
For this question one needs to understand about what autowire is. In common terms we can say that through autowire we create a object instance/bean at the time of deployment of the web app. So now going with the question if you are declaring autowiring in multiple places with the same name. Then this error comes. Autowiring can be done in multiple ways so if you are using multiple type of autowiring technique, then also one could get this error.
Complete Generic Solution using Spring 4:
Domain Class
#Component
class Foo{
}
#Component
class Bar{
}
DAO Layer
interface GenericDao<T>{
//list of methods
}
class GenericDaoImpl<T> implements GenericDao<T>{
#Autowired
SessionFactory factory;
private Class<T> domainClass; // Get Class Type of <T>
public Session getCurrentSession(){
return factory.getCurrentSession();
}
public DaoImpl() {
this.domainClass = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), DaoImpl.class);
}
//implementation of methods
}
interface FooDao extends GenericDao<Foo>{
//Define extra methods if required
}
interface BarDao extends GenericDao<Bar>{
//Define extra methods if required
}
#Repository
class FooDao extends GenericDaoImpl<Foo> implements FooDao{
//implementation of extra methods
}
#Repository
class BarDao extends GenericDaoImpl<Bar> implements BarDao{
//implementation of extra methods
}
Service Layer
interface GenericService<T>{
//List of methods
}
class GenericServiceImpl<T> implements GenericService<T>{
#Autowire
protected GenericDao<T> dao; //used to access DAO layer
}
class FooService extends GenericService<Foo>{
//Add extra methods of required
}
class BarService extends GenericService<Bar>{
//Add extra methods of required
}
#Service
class FooServiceImpl extends GenericServiceImpl<Foo> implements GenericService<Foo>{
//implementation of extra methods
}
#Service
class BarServiceImpl extends GenericServiceImpl<Bar> implements GenericService<Bar>{
//implementation of extra methods
}

Categories

Resources