I am working on Spring Boot application.
We have Service Layer,Rest Controller and Dao as repository.
I have 20 to 30 tables in my database and I dont want to create repository for each entity and extends that to CrudRepository.
ex : User is an Entity, to perform persistance operations on User, I have to create UserRepository which extends CrudRepository.
Same with Department, Company etc...
What i want to do is, I will write a BaseRepository which gonna extend CrudRepository, base repository should accept all entities and do persistance operations.
Is there a way to that ??
Don't extend CrudRepository it's functionality is all tied to the generic type, it'd be hacky to extend it for a generic implementation. You probably just want something simple which uses the JPA entity manager directly:
import javax.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
public class GenericRepository {
#Autowired
private EntityManager entityManager;
public <T, ID> T findById(Class<T> type, ID id) {
return entityManager.find(type, id);
}
}
Related
Can you use both annotations on your database tables?
id, just like some clarification on there differences. thank you
#Entity is used to map a class to a relational database, it represents a database table.
#Document is used to map a class to noSQL database (specifically mongoDB), it represents a MongoDB documents.
You can use both JPA or MongoRepository if you are using both databases by creating different entities and repositories for each database.
I recommend you to have a look at spring documentation (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/)
#Document is a Spring Data Mongo annotation while #Entity is part of Java Persistence API (JPA).
You can check both documentations:
Spring Data Mongo docs
JPA docs
Where into "Example 10. Repository definitions using domain classes with annotations" there is this piece of code:
interface PersonRepository extends Repository<Person, Long> { … }
#Entity
class Person { … }
interface UserRepository extends Repository<User, Long> { … }
#Document
class User { … }
And documentation says:
PersonRepository references Person, which is annotated with the JPA #Entity annotation, so this repository clearly belongs to Spring Data JPA. UserRepository references User, which is annotated with Spring Data MongoDB’s #Document annotation.
So you can see here the difference.
#RepositoryRestResource
public interface StudentRepository extends CrudRepository<Student, Long>
{
public List<Student> findById(long id);
//#Query("select s from Student s where s.age <= ?")
public List<Student> findByAgeLessThanEqual (long age);
}
what does <Student, long> mean in the CrudRepository<Student, long> and what are the various parameters that can be passed to cruderepository.
The main idea of CrudRepository is to give you opportunity to use main operations with data without creating your own implementation. You just create needed methods and for most simple cases Spring will create implementation for you (if you use right naming convention for your methods).
First parameter (Student in your case) is a type of entity with which current Repository is working, second parameter (Long in your case) is a type of Id in this entity.
CrudRepository provides methods for the CRUD operations. This interface extends the Repository interface. If you are extending the CrudRepository, there is no need for implementing your own methods.
If do not want data from custom parameter, you have to write custom query for it.
you can use only that parameters which is their in your CrudRepository.
I want to create a "generic" repository that query data from multiple entities. If I do that:
#Repository
public interface MyRepository {
#Query("select r from Role r")
List<Role> getRoles();
}
I get an error because Spring doesn't find an implementation to inject when a MyRepository instance is required. So far, so good. Now, If I do this:
#Repository
public interface MyRepository extends JpaRepository {
#Query("select r from Role r")
List<Role> getRoles();
}
I get an error because Object is not a JPA managed type (JpaRepository is generic). Ok, again. If I do this:
#Repository
public interface MyRepository extends JpaRepository<User, String> {
#Query("select r from Role r")
List<Role> getRoles();
}
It works. Why? I'm declaring a JpaRepository for entity User, not Role. Why does JpaRepository need a concrete entity even when the queries will be against another one?
Every repository in Spring Data has to extend the Repository interface, that is a generic interface, so you always have to specify the entity you are gonna work with and you can't do anything about it because it is how Spring Data is implemented. You can find more information here about creating repositories:
http://docs.spring.io/spring-data/jpa/docs/1.4.0.M1/reference/html/repositories.html
On the other hand, of course you can specify one entity to the repository and then add methods that return other type of entities because in your interface you can add whatever you want (also notice that Repository interface has no methods). But if you want to use the methods of the parent interface you have to use the entity you specified.
In your example, you could do what #M. Deinum suggested and create a JpaRepository<Role, Long> and use the findAll query, that makes much more sense. Using a JpaRepository<User, String> as you are doing is just a misuse of the framework.
I have a Spring project, split into several modules.
data access library (spring-data-jpa; entities and repositories)
security library (spring-security; including an extended repository with #PreAuthorize annotations)
web project (depends on both libraries)
batch project (depends only on data, since there's no user to authenticate in Spring)
So in the data access library, I have this interface:
#Repository
public interface ItemRepository extends PagingAndSortingRepository<Item, Long> {
List<Item> findAll();
Item findById(Long id);
}
And in the security library:
#Repository
public interface SecuredItemRepository extends ItemRepository {
#PreAuthorize("hasRole('ROLE_ADMIN')")
List<Item> findAll();
#PreAuthorize("hasRole('ROLE_ADMIN')")
Item findById(Long id);
}
When I #Autowire an ItemRepository, I would like it to use SecuredItemRepository if it's available, and ItemRepository if not.
Is there a way to declare the SecuredItemRepository as the default choice, or first in the list of ItemRepository implementations to grab? I'd rather not specify the implementation in every location that I need to access the database.
And of course, two seconds later I find the answer. I needed to annotate SecuredItemRepository with this:
#Priority(value = Ordered.HIGHEST_PRECEDENCE)
How can I get access to the Entity Manager in the repository when using Spring Boot and Spring Data?
Otherwise, I will need to put my big query in an annotation. I would prefer to have something more clear than a long text.
You would define a CustomRepository to handle such scenarios. Consider you have CustomerRepository which extends the default spring data JPA interface JPARepository<Customer,Long>
Create a new interface CustomCustomerRepository with a custom method signature.
public interface CustomCustomerRepository {
public void customMethod();
}
Extend CustomerRepository interface using CustomCustomerRepository
public interface CustomerRepository extends JpaRepository<Customer, Long>, CustomCustomerRepository{
}
Create an implementation class named CustomerRepositoryImpl which implements CustomerRepository. Here you can inject the EntityManager using the #PersistentContext. Naming conventions matter here.
public class CustomCustomerRepositoryImpl implements CustomCustomerRepository {
#PersistenceContext
private EntityManager em;
#Override
public void customMethod() {
}
}
In case you have many repositories to deal with, and your need in EntityManager is not specific for any particular repository, it is possible to implement various EntityManager functionality in a single helper class, maybe something like that:
#Service
public class RepositoryHelper {
#PersistenceContext
private EntityManager em;
#Transactional
public <E, R> R refreshAndUse(
E entity,
Function<E, R> usageFunction) {
em.refresh(entity);
return usageFunction.apply(entity);
}
}
The refreshAndUse method here is a sample method to consume a detached entity instance, perform a refresh for it and return a result of a custom function to be applied to the refreshed entity in a declarative transaction context. And you can add other methods too, including query ones...