"No property" error while declaring superclass for Spring Data repositories - spring

I have a simple webapp that uses few spring components - Spring Boot, MVC and Spring Data for persistence management (everything is written in Groovy build up with Gradle).
I am trying to add some common logic for all repositories as it is described in the documentation. I created the following artifacts:
GenericObjectRepository which is an interface defining my common method:
#NoRepositoryBean
interface GenericObjectRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
String sharedCustomMethod(String name)
}
GenericObjectRepositoryImpl implementing above interface:
class GenericObjectRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements GenericObjectRepository<T, ID> {
EntityManager entityManager
GenericObjectRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager)
this.entityManager = entityManager;
}
#Override
String sharedCustomMethod(String name) {
"Hello ${name}"
}
}
and finally the repository (with some random data-specific method)
#Repository
interface MarketingClientRepository extends GenericObjectRepository<MarketingClient, Long> {
List<MarketingClient> findByLastName(String lastName);
}
The problem is that everytime I start the application, and Spring tries to instantinate MarketingClientRepository, i get the following Exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'marketingClientRepository': Invocation of init method failed;
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
...
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property shared found for type MarketingClient!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:359)
...
It seems to me that Spring Data is trying to parse my method (sharedCustomMethod), extracting the shared part and tries to apply all the magic it does to every other Repository interface method (tries to access shared property on my business object - which obviously does not exist).
Thanks in advance,
EDIT 1:
I added the FactoryBean as it is said in step 3, but I failed to compile it. I don't know if it's something wrong with generics (I admit that this construction is quite complicated for me). The class code is as follows:
class GenericRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
new GenericRepositoryFactoryBean(entityManager)
}
private static class GenericRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager
GenericRepositoryFactory(EntityManager entityManager) {
super(entityManager)
this.entityManager = entityManager
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
new GenericObjectRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager)
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
GenericObjectRepository.class
}
}
}
The compiler says:
/.../GenericRepositoryFactoryBean.groovy: 14: unable to resolve class T
# line 14, column 60.
yBean<R extends JpaRepository<T, I>, T,
^
/.../GenericRepositoryFactoryBean.groovy: 14: unable to resolve class I
# line 14, column 63.
an<R extends JpaRepository<T, I>, T, I e
^
2 errors

You basically miss step 3 in the steps required to make this work described in the reference documentation.
As the process is rather awkward, I've filed a ticket to simplify this process.

Related

Custom Base repository function not found

I have generic base repository defined as follows:
#NoRepositoryBean
public interface IBaseRepository<Template extends BaseModel, Id > extends
ElasticsearchRepository<Template,Id>, ICustomRepository{
}
My ICustomRepository repository interface is defined as follows:
#Repository
public interface ICustomRepository {
void someCustomFunction();
}
public class CustomRepositoryImpl implements ICustomRepository{
#Override
void someCustomFunction(){
}
}
Now when I use the baserepository as:
#Repository
public interface OrderRepository extends BaseRepository<OrderModel,Long>{
}
when I Autowired OrdeRepository in my service class it gives me compile error:
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'OrderRepository ' defined in
abc.example.OrderRepository defined in
#EnableElasticsearchRepositories declared on ElkApplication:
Invocation of init method failed; nested exception is
org.springframework.data.mapping.PropertyReferenceException: No
property someCustomFunction found for type OrderModel!
Can someone please suggest whats going wrong here and how to resolve this?
Converting comment to answer
ICustomRepo
#Repository
public interface ICustomRepository {
void someCustomFunction();
}
IBaseRepository
#NoRepositoryBean
public interface IBaseRepository<Template extends BaseModel, Id > extends
ElasticsearchRepository<Template,Id>, ICustomRepository{
}
OrderRepository
#Repository
public interface OrderRepository extends IBaseRepository<OrderModel,Long>{
}
OrderRepository is a repository interface and a bean is created by a spring bean processor but this OrderRepository does not implement someCustomFunction and even spring has no idea about its implementation except it knows it has been implemented by CustomRepositoryImpl so at bootstrap/startup spring throws an error.
So instead of just having the definition of someCustomFunction we need to implement this in ICustomRepository, thanks to Java 8 we can implement the method in the interface as well
#Repository
public interface ICustomRepository {
default void someCustomFunction() {
// some code here
}
}
This code will be working fine, JDK and other proxies will work on this method as well, also we can apply AOP on someCustomFunction.
It is also possible to override this method in any repository given that's also using the default and #Override.
#Repository
public interface OrderRepository extends IBaseRepository<OrderModel,Long>{
#Override
default void someCustomFunction() {
// some code here
}
}

Is it possible to combine Spring JpaRepository and some CustomRepository in one reusable interface?

I know how to customize one individual repository (from here), but I'm curious is it possible to combine JpaRepository and one or more custom repositories (with implementations) and then extend from this one repository.
public interface MyCustomRepository<E> {
void customMethod();
}
#Repository
public class MyCustomRepositoryImpl<E> implements MyCustomRepository<E> {
void customMethod() {
// impl
}
}
public interface MyReusableRepository<E> extends JpaRepository<E, Long>,
MyCustomRepository<E> {
}
#Repository
public interface UserRepository extends MyReusableRepository<User> {
}
This example brings me the following error:
Error creating bean with name 'userRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException:
Failed to create query for method public abstract void com.my.pckg.MyCustomRepository.customMethod()!
No property customMethod found for type User!

How to autowire SimpleJpaRepository in a spring boot application?

I'm working on a project that uses SpringBoot 2.0.5 version, Spring Data JPA to persists and retrieve records using JPA. I autowired SimpleJpaRepository in the service layer. But while starting my application, it failed with
"NoSuchBeanDefinitionException"- No qualifying bean of type
'org.springframework.data.jpa.repository.support.SimpleJpaRepository<?, ?>'
available: expected at least 1 bean which qualifies as autowire candidate.
My controller, service and DAO are like below
Controller class:
#Controller
public class MyController{
#Autowired
private MyService<Person,PersonPK> service;
Service layer as
public interface MyService<V,K>{
methods defined
}
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private SimpleJpaRepository<V,K> repository; // This dependency is failing
}
Application as :
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
#EnableJpaRepositories
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Is my approach incorrect? Is that not the correct way of autowiring the SimpleJpaRepository.
There is no need for me to extend SimpleJpaRepository as Spring provided JPARepository is good for me, for now.
Thanks
You still need to create a repository interface that extends JpaRepisitory, or the spring repository type of your choice.
To quote the spring data documentation:
1.2.1 Defining repository interfaces
As a first step you define a domain class-specific repository interface. The interface must extend
Repository and be typed to the domain class and an ID type. If you
want to expose CRUD methods for that domain type, extend
CrudRepository instead of Repository.
Once you do create a new repository type, you will autowire by that type rather than the SimpleJpaRepository.
One way to get an implementation of the SimpleJpaRepository is by using a Configuration class to create an instance as a bean that you will use inside your service.
#Configuration
public class PersistanceConfiguration {
#PersistenceContext
private EntityManager entityManager;
#Bean
public SimpleJpaRepository<YourEntity, Long> getYourEntitySimpleRepository() {
return new SimpleJpaRepository<>(YourEntity.class, entityManager);
}
}
And inject it to your service as you would do with a JpaRepository, for example:
#Service
public class YourEntityServiceImpl<YourEntity, Long> implements YourEntityService {
private JpaRepository<YourEntity, K> repository;
private SimpleJpaRepository<YourEntity, K> simpleRepository;
#Autowired
public YourEntityServiceImpl(YourEntityRepository repository, SimpleJpaRepository<YourEntity, Long> simpleRepository) {
this.repository = repository;
this.simpleRepository = simpleRepository;
}
}
You should create a repository interface that extending JpaRepisitory.
#Repository
public interface MyRepository extends JpaRepisitory<T, ID> {
//
}
And the you should Autowired in your service class.
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private MyRepository myRepository;
}

How to access Entity Manager within a service in spring boot project?

I've googled so many times for how to access entity manager in spring boot, and did what posts said, but it didn't work. I want to access Entity Manager so that i can do some custom query operation. Here i defined the custom interface in a dependent package named 'customrepository':
public interface PostRepositoryCustom {
void refresh(Post post);
}
Then I implemented this interface in another package named 'customrepositoryimpl':
public class CustomPostRepositoryImpl implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public void refresh(Post post) {
em.refresh(post);
}
}
Finally, I defined a standard repository interface which extends 'CrudRepository' and the custom repository:
public interface PostRepository extends CrudRepository<Post,Long>,PostRepositoryCustom {}
Every steps i followed What i googled and Official Documents, BUT when i run my application, i get this:
Error creating bean with name 'postRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract void com.example.demo.customrepository.PostRepositoryCustom.refresh(com.example.demo.model.Post)! No property refresh found for type Post!
Why? Anyone tell me where should i correct my mistakes?
Spring cannot locate the beans, so try annotate with #Repository
#Repository
public class CustomPostRepositoryImpl implements PostRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public void refresh(Post post) {
em.refresh(post);
}
}
and
#Repository
public interface PostRepository extends CrudRepository<Post,Long>{
}

How to do inheritance in Dao layer?

I am working with SpringMVC+Hibernate, I want to apply Inheritance in DAO layer, I am doing like below:
BaseDao.java
public interface BaseDao
{
public Serializable save(Object object) throws DataAccessException,
HibernateException;
public void merge(Object object) throws DataAccessException,
HibernateException;
public void flush() throws DataAccessException,HibernateException;
}
EmpDao.java
public interface EmpDao extends BaseDao{
}
BaseDaoImpl.java
#Repository
public class BaseDaoImpl implements BaseDao{
// Implementation for baseDao methods
}
EmpDaoImpl .java
#Repository
public class EmpDaoImpl extends BaseDaoImpl implements EmpDao{
// Implementation
}
But I am getting below error:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type
[BaseDao] is defined: expected single matching bean but found 2
What am I missing here?
If you really want to have both BaseDaoImpl and EmpDaoImpl as two beans in your Spring container, you need to tell Spring which one to use wherever you have an #Autowired field of type BaseDao using #Qualifier annotation.
Related: Understanding Spring #Autowired usage
It seems like you're trying to inject BaseDao and Spring is complaining there are two candidates.
I think this is actually a design problem. You wanted to use BaseDaoImpl both as a concrete bean that you use directly and also as a base class for other DAOs. This is bad because the sub-classes does not actually extend but simply uses their parent class. The better pattern would be to get rid of the extends and simply inject the BaseDaoImpl into the other DAOs.
Also, the interfaces looks superfluous. If you're working around the proxy problem, just use proxyTargetClass.
You can use generic types like this
BaseDao.java
public interface BaseDao<EntityType extends Object>
{
public Serializable save(EntityType entity) throws DataAccessException,
HibernateException;
public void merge(EntityType entity) throws DataAccessException,
HibernateException;
public void flush() throws DataAccessException,HibernateException;
}
BaseDaoImpl.java
#Repository
public abstract class BaseDaoImpl<EntityType extends Object> implements BaseDao<EntityType>{
// Implementation for baseDao methods
}
EmpDao.java
public interface EmpDao extends BaseDao<Employee>{
}
EmpDaoImpl .java
#Repository
public class EmpDaoImpl extends BaseDaoImpl<Employee> implements EmpDao{
// Implementation
}
You need to add the #NoRepositoryBean on the BaseDao interface so spring would not create a bean for it, as well as for EmpDao I assume

Resources