Best way of handling entities inheritance in Spring Data JPA - java

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>
{ /* ... */ }

Related

Is it possible to register with Container without using #Repository or #Bean?

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> {

Extending existing spring jpa repository functions

I'm using Spring to communicate with mysql db.
I've got an Entity class and an interface that extends CrudRepository.
Everything works great - read/write etc.
I want to 'extend' the findAll methods. I want to manipulate the received data from findAll before returning it.
user class:
#Entity
#Table(name = "user")
public class User
{
private String name;
private String age;
private String type;
getters/setters
}
repo:
#Repository
public interface UserRepo extends CrudRepository<User, Long> {
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
I want that afterManipulatedFindAllData() will manipulate findAll data as I like.
Is that possible?
added
after reviewing #BoristheSpider link:
interface UserRepository {
Map<String, String> afterManipulatedFindAllData();
}
class UserRepositoryImpl implements UserRepository{
public Map<String, String> afterManipulatedFindAllData() {
////how this method receive the 'findAll' data?//////
}
}
public interface UserRepo extends CrudRepository<User, Long>, UserRepository
{
List<User> findAll();
Map<String, String> afterManipulatedFindAllData();
}
Thanks a lot,
Avi
You can always define new methods in the repository by specifying a JPA query for them:
#Component
public interface UsersRepository extends JpaRepository<User, UUID> {
public List<User> findByEntityStatus(EntityStatus status);
#Query("from User user left outer join fetch user.areas where user.code = :code")
public User findByCode(#Param("code") String code);
}
Maybe this could work for you?

Custom implementation of intermediate repository in Spring Data

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

Method in interface implementing class not visible in service class

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?

Crud methods in service layer using Spring Data JPA

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. :)

Categories