Im currently creating a spring bean with the following javax inject provider annotation:
#Autowired
Provider<Table> provider;
in one of the init methods i call :
Table table = provider.get();
this throws : java.lang.ClassCastException: $Proxy127
The table is configured as a
#Component
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Table extends Furniture<
Square,
Round> {
...............
...............
}
With the parent class being abstract:
public abstract class Furniture<
E extends Legs,
M extends Corners> {
.............
.............
}
Anyone got any ideas why i cannot get an instance to be created on init?
I know spring has method injection using the lookup method, but i dont really want to use the XML.
It seems that the bean returned by the provider gets proxied. Try to extract an interface that the Table class can implement, and use a Provider<TableInterface> instead. Then you can get an instance like:
TableInterface table = provider.get();
Which would still return a Proxy, but you wouldn't get the ClassCastException on this assignment, because the proxy implements the TableInterface interface.
Another possibility is to enable class proxying with CGLIB, in which case you don't need to extract an interface.
Related
I am trying to get the total number (count) for a given field from an index using a generic repository. The index mapping is huge and I do not wish to have an equivalent mapping on the Springboot side. The query is extremely simple;
http://localhost:9203/type_index/_count?q=person.name:john;
Should I have a generic class for the repository?
Can I use the #Query annotation?
How do I define which type and index that the repository should go to? Normally, this is done by the #Document(indexName="type_index) annotation on the Entity class but in this case, I will not have one.
#Repository
public interface GenericElasticsearchRepository<T, ID> extends ElasticsearchRepository<T, ID> {
#Query("{\"bool\": {\"must\": [{\"match\": {\"person.name\": \"?0\"}}]}}")
long countByName(String name);
}
When I use the above code, I am getting the following error;
ElasticSearchConfiguration: Invocation of init method failed;
nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.Object!
Thanks!
As for building a query method without having the entity you could go for a custom implementation (https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.single-repository-behavior).
Define an interface (not extending ElasticsearchRepository) like
interface CountByNameRepository {
long countByName(String name);
}
and an implementation (just typing this here, not using an IDE, ao there might be errors):
public class CountByNameRepositoryImpl {
private final ElasticsearchOperations operations;
public CountByNameRepositoryImpl(ElasticsearchOperations operations) {
this.operations = operations;
}
public long countByName(String name) {
Query query = new CriteriaQuery(Criteria.where("person.name").is(name));
return operations.count(query, IndexCoordinates.of("type-index"));
}
}
Your respository then will need to extend bot ElasticsearchRepository and CountByNameRepository, Spring Data Elasticsearch will use the provided implementation.
I have the index name hard coded here, as I don't know where you have access to this. You might be able to pass it as a second parameter to the method, but this depends on your concrete use case.
I'm trying to implement Interface-based Projection but I cannot make it work with my custom type column.
Below example of what I'm trying to do:
Repository:
#Query(value = "SELECT customType from TABLE", nativeQuery = true)
List<TestClass> getResults();
Interface projection:
public interface TestClass {
#Convert(converter = MyCustomTypeConverter.class)
MyCustomType getCustomType();
}
Converter:
#Converter
public class MyCustomTypeConverter implements Converter<String, MyCustomType> {
#Override
public MyCustomType convert(String source) {
// whatever
}
}
When I call getResults() on repository I receive list of results as expected, but when I try to call getCustomType() on one of results I get exception:
java.lang.IllegalArgumentException: Projection type must be an interface!
at org.springframework.util.Assert.isTrue(Assert.java:118)
at org.springframework.data.projection.ProxyProjectionFactory.createProjection(ProxyProjectionFactory.java:100)
at org.springframework.data.projection.SpelAwareProxyProjectionFactory.createProjection(SpelAwareProxyProjectionFactory.java:45)
at org.springframework.data.projection.ProjectingMethodInterceptor.getProjection(ProjectingMethodInterceptor.java:131)
at org.springframework.data.projection.ProjectingMethodInterceptor.invoke(ProjectingMethodInterceptor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.ProxyProjectionFactory$TargetAwareMethodInterceptor.invoke(ProxyProjectionFactory.java:245)
I found that problem lies in
org.springframework.data.projection.ProxyProjectionFactory
which uses
org.springframework.core.convert.support.DefaultConversionService
which obviously doesn't have my custom type converter registered.
If I stop on breakpoint in ConversionService and manually add my converter in runtime, projection will work without any problem.
So question is: can I somehow register my custom converter to ConversionService used by spring jpa during interface-based projection?
EDIT:
I added my converter to DefaultConversionService's sharedInstance in InitializingBean like below and it worked.
#Component
public class DefaultConversionServiceInitializer implements InitializingBean {
#Override
public void afterPropertiesSet() {
DefaultConversionService conversionService = (DefaultConversionService) DefaultConversionService.getSharedInstance();
conversionService.addConverter(new MyCustomTypeConverter());
}
}
The ConversionService used is DefaultConversionService.getSharedInstance().
So you should be able to access that and add your converter.
This broke again in Spring Data Jpa 2.4.0 (see this closed GitHub issue).
One workaround, as suggested by user ajobra76 (link), is to specify the converter to use like so:
public interface TestClass {
#Value("#{#myCustomTypeConverter.convert(target.customType)}")
MyCustomType getCustomType();
}
Where myCustomTypeConverter would be a bean in the ApplicationContext. Of course there are other ways of specifying a method using SpEL, including creating a new object, and calling a static method. But this is just to show that it can be done.
target is an identifier that will be bound to the "aggregate root backing the projection" (Spring Data Jpa docs, section "An Open Projection").
I have a requirement that based on profile I need to inject 2 different classes into DAO layer to perform CRUD operation. Let's say we have class A and Class B for profiles a and b respectively. Now in the DAO layer without using if else condition (As I am using that currently based on the profile, I am using service layer to call 2 different methods 1.saveA(), 2.saveB().) But is there any way to make it more generic and based on profile or either by the class reference I can instantiate different entity as well as JPA Classes? I tried to use
<T extends Parent> T factoryMethod(Class<T> clazz) throws Exception {
return (T) clazz.newInstance();
}
but this also will force me to cast the returned object to a class. I tried creating a parent P for both class A and B. and used them instead but got confused when injecting the entity types to JPARepository.
I tried creating a SimpleJPARepository but didnt worked as there are overridden methods in ARepository and BRepository.
Or,
is there a way I can use the same entity class for 2 different tables? that way it can be solved. for 1 profile I have different sets of columns whereas for 2nd profile I have different columns.
this is how I am expecting: Would it be possible? or, how I am doing now is correct?
public void doStuff(Class<T> class){
GenericRepository repo;
if(class instanceof A){
//use ARepository;
repo = applicationContext.getBean(ARepository);
}else{
//use BRepository;
repo = applicationContext.getBean(BRepository);
}
repo.save(class);
repo.flush();
}
You can create a method utility like following: The key is the class type of the entity and the value is the repository.
Map<Class<? extends Parent>, JpaRepository> repoMapping = new HashMap<>();
#PostConstruct
public void init(){
repoMapping.put(A.class, applicationContext.getBean(ARepository));
repoMapping.put(B.class, applicationContext.getBean(BRepository));
}
public JpaRepository getRepo(Class<? extends Parent> classs){
return repoMapping.get(classs);
}
I only have two determine the type,one of is my Basic Class, another is generic class name.
BasicOrmDao.class;
Class cls = Class.forName("com.base.Role");
My injection class is
class RoleDao extends BasicOrmDao<Role> {
}
I don't know bean name roleDao, because of it maybe BasicOrmDao.
So how can I get my bean "roleDao" use BasicOrmDao.class and String name = "com.base.Role";.
I want use java code to do this, like #Autowired, but I only known basic type BasicOrmDao, and generic class name "com.base.Role"
I am writing repository for variables table and wish to access specific rows with it. For this I am trying to autowire main repository into custom implementation, like this:
public interface VariableRepo extends CrudRepository<Variable, Long>, VariableRepoCustom {
Variable getByName(String name);
}
public interface VariableRepoCustom {
...
Variable getPopulationSingle();
...
}
public class VariableRepoCustomImpl implements VariableRepoCustom {
private final VariableRepo variableRepo;
#Autowired
public VariableRepoCustomImpl(VariableRepo variableRepo) {
this.variableRepo = variableRepo;
}
#Override
public Variable getPopulationSingle() {
return getByName("Population single");
}
...
}
Unfortunately, Spring like to go crazy with this, throwing an exception:
Error creating bean with name 'variableRepo': Invocation of init
method failed; nested exception is
org.springframework.data.mapping.PropertyReferenceException: No
property getPopulationSingle found for type Variable!
I.e. it tries to find repository method inside entity class (Variable) which of course should not contained inside.
How to solve this?
What you named VariableRepoCustom/VariableRepoCustomImpl should be in fact a service (not a repository in terms of spring-data-jpa).
The interface VariableRepoCustom should not be present in JPA searchpath, to prevent JPA to generate an implementation
I think might be your repository impl does not have #Repository annotation. Please make both repository and impl as #Repository. it should fix your problem.
For best practice, No need to autowire VariableRepo interface into impl class.
Edit:
Also remove custom from impl. The name will be RepoName by append impl. it is definition like VariableRepoImpl