I am using Spring Data in my project and I have plenty of repositories. Now I wanted to add a method to some of the repositories, but not all of them, so I have created an interface LoggingRepositoryCustom, that (simplified) looks like this:
#NoRepositoryBean
public interface LoggingRepositoryCustom<T extends IEntity, ID extends Serializable> {
<S extends T> S save(S entity, AppUser author);
}
As I need to have a custom implementation of this, I have created also LoggingRepositoryImpl, that implements this interface:
#NoRepositoryBean
public class LoggingRepositoryImpl<T extends IEntity, ID extends Serializable> implements LoggingRepository {
#Override
public <S extends T> S save(S entity, AppUser author) {
//impl
}
}
Lastly, I have some repositories, that should have the functionity above, e.g. AppUserRepo:
#Repository
public interface AppUserRepo extends PagingAndSortingRepository<AppUser, Long>, LoggingRepositoryCustom<AppUser, Long> {
//methods of this repo
}
However, when I try to deploy this application, I get the following exception:
org.springframework.data.mapping.PropertyReferenceException: No property save found for type AppUser!
It seems that the custom implementation is not reflected and Spring Data tries to create a magical method from the name convention, thus looking for property "save" of AppUser, which does not exist. Is there a way to implement an interface, that is further extended by other interfaces?
I add the same issue in one of my project ... and i did as follow to get it working :
1 - create your "parent" interfaces and implementations :
Repository :
#NoRepositoryBean
public interface LoggingRepository<T extends IEntity, ID extends Serializable> extends PagingAndSortingRepository<T, Long>, LoggingRepositoryCustom<T, ID> {
}
Repository custom
#Transactional(readOnly = true)
public interface LoggingRepositoryCustom<T extends IEntity, ID extends Serializable> {
<S extends T> S save(S entity, AppUser author);
}
Implementation of the repository custom :
public class LoggingRepositoryImpl<T extends IEntity, ID extends Serializable> implements LoggingRepositoryCustom<T, ID> {
#Override
public <S extends T> S save(S entity, AppUser author) {
//impl
}
}
2 - Create your specific interfaces and implementations :
repository :
#Repository
public interface AppUserRepo extends LoggingRepository<AppUser, Long>, AppUserRepoCustom {
}
repository custom :
public interface AppUserRepoCustom<AppUser, Long> {
}
repository implementation :
public class AppUserRepoImpl extends LoggingRepositoryImpl<AppUser, Long> implements AppUserRepoCustom {
}
hope this helps
Related
I have an strange error, I have this classes:
AbstractRepository
#NoRepositoryBean
public interface abstractRepository<T extends someEntity,ID> extends JpaRepository<T, ID>{
CustomRepository
#Repository
public interface customRepository extends abstractRepository<classThatExtendSomeEntity, Integer> {
AbstractService
#Service
#Transactional
public abstract class AbstractService<Entity extends someEntity, DTO extends someDTO>
implements ComboWithSubproductsService<DTO>{
protected AbstractRepository<Entity,Long> genericRepository;
#Autowired
public AbstractService(AbstractRepository<Entity, Long> repository) {
super();
this.genericRepository = repository;
}
}
CustomService
#Service
#Transactional
public class customServiceImpl extends abstractService<classThatExtendSomeClass, classThatExtendsSomeDTO> implements customService{
private CustomRepository repository;
public customServiceImpl (customRepository repository) {
super(repository); //ERROR: the constructor AbstractRepository(customRepository) is undefined
}
}
This are my main class, my error is in customService class because I don't know why I can't do a upcast customRepository to AbstractRepository when customRepository extends abstractRepository.
I am absolutly blocked whit this error, unti I know, it is always possible cast subclass to superclass. I don't know how solve this error.
Thanks for your time and sorry for my english, it is not my native languaje.
public interface AccountRepository extends CrudRepository<AccountDBModel, Long> {
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_INSERT)
void insert(#Param("exchangeId") Long exchangeId, #Param("name") String name, #Param("siteAccount") String siteAccount,
#Param("memo") String memo, #Param("createdAt") Long createdAt, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Modifying
#Query(value = PortfolioQuery.ACCOUNT_UPDATE)
void update(#Param("id") Long id, #Param("exchangeId") Long exchangeId, #Param("name") String name,
#Param("siteAccount") String siteAccount, #Param("memo") String memo, #Param("updatedAt") Long updatedAt,
#Param("isActive") Boolean isActive);
#Query
Optional<AccountDBModel> findByName(#Param("name") String name);
}
#Service
public class AccountService {
private final AccountRepository repository;
#Autowired
public AccountService(AccountRepository repository) {
this.repository = repository;
}
public void postAccount(AccountBaseModel baseModel) throws Exception {
Long now = System.currentTimeMillis();
this.repository.insert(baseModel.getExchangeId(), baseModel.getName(), baseModel.getSiteAccount(),
baseModel.getMemo(), now, now, baseModel.getIsActive());
}
}
#SpringBootTest
class WaveBackofficeApiApplicationTests {
#Autowired
private ApplicationContext applicationContext;
#Test
public void contextLoads() throws Exception {
if (applicationContext != null) {
String[] beans = applicationContext.getBeanDefinitionNames();
for (String bean : beans) {
System.out.println("bean : " + bean);
}
}
}
}
As you can see in AccountRepository interface I didn't use #Repository in AccountRepository interface.
But why is it registered as a bean in Spring Container?
There are no other class like AppConfig.
The interface itself is not registered as a bean. spring framework provides existing implementation of a repository bean (default impl is the class SimpleJpaRepository), which gets injected based on the specifications you provide in your interface. This specific class has the #Repository annotation and will be picked up by spring as a bean.
A simple overview:
#Repository
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
// code
}
public interface MyRepository extends CrudRepository<T, ID> {}
#Service
public MyService() {
#Autowired private MyRepository myRepository;
}
In the example above, our own repository interface extends CrudRepository, which has an implementation class named SimpleJpaRepository (provided in the framework), and SimpleJpaRepository is registered as a bean. In MyService, we just tell that we want a bean of type MyRepository, and Spring will inject an instance of SimpleJpaRepository.
You created interface called AccountRepository and extended (thus inherited) CrudRepository.
Now just do Ctrl + Left mouse click on CrudRepository, you will end up in it:
#NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
Intellij actually gives you oportunity to find the implementations of all those methods with arrow down mark on the left side.
So there is a huge class called SimpleJpaRepository that has all the implementations, the actual code.
AND THE THING IS...
SimpleJpaRepository.class does have #Repository in it:
#Repository
#Transactional(
readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
I've three JPA entity classes A, B and C with the following hierarchy:
A
|
+---+---+
| |
C B
That is:
#Entity
#Inheritance
public abstract class A { /* ... */ }
#Entity
public class B extends A { /* ... */ }
#Entity
public class C extends A { /* ... */ }
Using Spring Data JPA, what is the best way to write repositories classes for such entities?
I know that I can write these:
public interface ARespository extends CrudRepository<A, Long> { }
public interface BRespository extends CrudRepository<B, Long> { }
public interface CRespository extends CrudRepository<C, Long> { }
but if in the class A there is a field name and I add this method in the ARepository:
public A findByName(String name);
I've to write such method also in the other two repositories, and this is a bit annoying.. Is there a better way to handle such situation?
Another point I would like to have is that ARespository should be a read-only repository (i.e. extend the Repository class) while the other two repositories should expose all the CRUD operations.
Let me know possible solutions.
I used the solution also described in this post from Netgloo's blog.
The idea is to create a generic repository class like the following:
#NoRepositoryBean
public interface ABaseRepository<T extends A>
extends CrudRepository<T, Long> {
// All methods in this repository will be available in the ARepository,
// in the BRepository and in the CRepository.
// ...
}
then I can write the three repositories in this way:
#Transactional
public interface ARepository extends ABaseRepository<A> { /* ... */ }
#Transactional
public interface BRepository extends ABaseRepository<B> { /* ... */ }
#Transactional
public interface CRepository extends ABaseRepository<C> { /* ... */ }
Moreover, to obtain a read-only repository for ARepository I can define the ABaseRepository as read-only:
#NoRepositoryBean
public interface ABaseRepository<T>
extends Repository<T, Long> {
T findOne(Long id);
Iterable<T> findAll();
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
and from BRepository extend also the Spring Data JPA's CrudRepository to achieve a read/write repository:
#Transactional
public interface BRepository
extends ABaseRepository<B>, CrudRepository<B, Long>
{ /* ... */ }
I have a method defined in an implementing #Repository class that is an extension of an abstract base class and implements an interface. However, a method defined in the #Repository class is not visible in the #Service class, and I am not sure why.
I have a #Service class defined as
#Service
#Transactional
public class CategoryService {
#Autowired
private IJpaRepository categoryRepository;
public CategoryService(){ }
/* service methods */
public List<Category> findTopLevelCategories(){
//findTopLevelCategories is not visible here
return categoryRepository.findTopLevelCategories();
}
}
where IJpaRepository is an interface defined by
public interface IJpaRepository<T> {
T findOne(int id);
List<T> findAll();
T create(T entity);
T update(T entity);
void delete(T entity);
void deleteById(int id);
}
and the #Repository is defined as an extension of an abstract class implementing the interface above:
#Repository
public class CategoryRepository extends AbstractJpaRepository<Category> implements IJpaRepository<Category> {
public List<Category> findTopLevelCategories(){
Query queryCategoryTopLevel = getEntityManager().createNamedQuery("findTopLevelCategories");
return queryCategoryTopLevel.getResultList();
}
/* Other overriding methods */
}
My best guess is that it is due to the #Autowired binding to the IJpaRepository, rather than the actual implementing CategoryRepository class. Without making any changes to IJpaRepository interface, how can I make the new method defined in CategoryRepository visible to the #Service class?
I'm building a small application using Spring and Spring Data JPA and I need to use the CrudRepository methods in the service layer, so I made 2 classes: GenericService and GenericServiceImpl. But I don't know if this is the right or even the best approach.
Here is an example:
POJO:
#Entity
public class User {
#Id
private Long id;
private String username;
}
DAO:
public interface UserDAO extends CrudRepository<User, Long> {
User findOneByUsername(String username);
}
Generic service
public interface GenericService<T, ID extends Serializable> {
<S extends T> S save(S entity);
}
Service
public interface UserService extends GenericService<User, Long> {
User findOneByUsername(String username);
}
Generic service impl.
public class GenericServiceImpl<T, ID extends Serializable> implements GenericService<T, ID> {
#Autowired
private CrudRepository<T, ID> repository;
#Override
public <S extends T> S save(S entity) {
return repository.save(entity);
}
}
Service Impl.
#Service
#Transactional
public class UserServiceImpl extends GenericServiceImpl<User, Long> implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User findOneByUsername(String username) {
userDAO.findOneByUsername(username);
}
}
Yes, you're providing your own impl that's may do custom things while still reusing the Derived queries from Spring JPA's CrudRepository. I've seen that a lot. :) We need to do that if we want to do our own computation before calling the Derived methods. After all, that computation may very well be part of a repository, so it doesn't make sense to put that logic in the service. But in your scenario, if there are no such custom computations, then this indirection isn't needed. As a commenter mentioned, you should directly use the UserDao interface. :)