Why is the #Repository annotation necesssary on a JpaRepository? - spring-boot

I have the following repository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
Employee findByName(String name);
}
Assume Employee is your usual entity class with id, name and surname.
I wire this class to my EmployeeService and use it like this:
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository repository;
public Employee saveEmployee(Employee employee) {
return repository.save(employee);
}
}
Would it make any difference to add the #Repository annotation to the repository?
Example:
import org.springframework.data.jpa.repository.JpaRepository;
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
Employee findByName(String name);
}

The #Repository annotation is not required on Spring Data repositories. Spring Boot detects repository beans based on the fact that they extend the Repository interface.
In fact, you need to suppress this behavior using #NoRepositoryBean if you want the repository bean to not be created.
The #Repository annotation is a specialization of #Component. If you were implementing repositories without the help of Spring Data, you could annotate them with #Repository to declare them as beans as well as hint at their role in your app.

Related

Custom sql statement using Spring JPA

I am trying to insert a record into a table called user. The method that I am trying to troubleshoot is insertNewUser which is located in my class UserRepositoryCustom. I am using JPARepository and EntityManger to accomplish this. The I am still unable to insert a user into the table. Any help with this would be very much appreciated.
UserRepositoryImpl
public class UserRepositoryImpl implements UserRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public void insertNewUser(User user) {
entityManager.createNativeQuery("INSERT INTO user (username,password) VALUES (?,?)")
.setParameter(1,user.getUserName())
.setParameter(2,user.getPassword());
}
}
UserRepositoryCustom
public interface UserRepositoryCustom {
public void insertNewUser(User user);
}
UserRepository
#Repository
public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom {
User findById(int id);
User findByUserName(String userName);
}
The issue was that I was not using #Autowired on my service class's contructor to inject the repository beans. When I invoked the Repository bean methods nothing was happening. Essentially the Repository beans were just null objects.

Spring Data Repository without specifying Entity

With Spring Data you can make a Repository for a given entity:
#Repository
public interface MyRepo extends CrudRepository<MyEntity, Long> {...}
But what if you have a lot of custom queries not tied to a specific Entity?
None of the below work:
#Repository
public interface MyRepo {...}
#Repository
public interface MyRepo extends CrudRepository {...}
#Component
public interface MyRepo extends Repository {...}
And so on..
Essentially what I want, is to be able to encapsulate some #Querys into an injectable class or interface.
You can use a generic entity superclass instead of a concrete entity. It's very usual to have an abstract superclass to declare the id of the entities or other common stuff.
#MappedSuperclass
public abstract class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Then you can create a repository like this and autowire where you need it:
public interface MyRepo extends JpaRepository<AbstractEntity, Long> {
#Query("...")
myQueryMethod();
}
That said, Spring Data interfaces are designed to work with a root entity. I think that if you want to avoid it, you should use the underlying JPA layer (that is, use the EntityManager to execute queries instead of a Spring Data Repository).

#Autowired is not working for CrudRepository

I have problems injecting a repository, there is no problem injecting a Service. Im injecting the repo in a service:
#Service
public class AuthorService {
#Autowired
private AuthorRepository repository;
public String getAll(){return "XXXXX";}
}
and the repository is:
public interface AuthorRepository extends CrudRepository<Author, Integer> {
}
And my code structure is the following:
with the main class:
#SpringBootApplication
public class AuthorBookGraphqlApplication {
public static void main(String[] args) {
SpringApplication.run(AuthorBookGraphqlApplication.class, args);
}
}
the error is thrown on start:
Field repository in com.author.book.graphql.demo.service.AuthorService required a bean of type 'com.author.book.graphql.demo.repository.AuthorRepository' that could not be found.
Update code as below
Spring will automatically import the beans into the container and inject to dependencies with these annotations.
#Component, #Controller, #Service and #Repository - Helps to define the beans so the container is aware of them and can inject them for you with #Autowired.
#Autowired - Handles only wiring part here.
#Service
public class AuthorService {
#Autowired
private AuthorRepository repository;
public String getAll(){return "XXXXX";}
}
#Repository
public interface AuthorRepository extends CrudRepository<Author, Integer> {}
Before class AuthorRepository, Let's put more annotation #Repository.

Best practise when using Querydsl with Spring Data

Using Spring Data nad Querydsl we can just declare repository interface and skip the implementation class. Some methods with a specific name or using #Query annotation and that's all.
But sometimes I'd like to use JPAQuery and define method's body by myself, let's say
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> someMethod(String arg) {
JPAQuery query = new JPAQuery(em);
...
}
but this way I would have to implement other MyRepository interface methods, which ruins all Spring Data's advantages!
I can see two options:
Declare another interface per each repository and then normally implement it (which doubles number of interfaces)
Inject EntityManager into #Service class and implement my custom methods there
I like option #2 more, but as far I as know, in #Service class we should only call repository methods, so it's not a perfect solution as well.
So how does programmers deal with it?
You should not implement the actual Spring Data repository, instead you have to declare another custom interface where you can put your custom methods.
Let's say you have a MyRepository, defined as
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long> {}
Now you want to add your custom findTuplesByMyArg(), for a sake of purpose you need to create custom repository interface
public interface MyRepositoryCustom {
List<Tuple> findTuplesByMyArg(String myArg);
}
Afterwards comes the implementation of custom interface
public class MyRepositoryImpl implements MyRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = new JPAQuery(em);
...
}
}
And we need to change MyRepository declaration, so it extends custom repository, so that
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long>, MyRepositoryCustom {}
And you can easily access your findTuplesByMyArg() by injecting MyRepository, e.g.
#Service
public class MyService {
#Autowired
private MyRepository myRepository;
public List<Tuple> retrieveTuples(String myArg) {
return myRepository.findTuplesByMyArg(myArg);
}
}
Pay attention that names are important here (you need to have Impl postfix by default configs in repo implementation).
You can find all needed information here
I would suggest a minor rectification to the answer above, which tries to use JPAQueryFactory. It is good to make use of the provided factory class.
public class MyRepositoryImpl implements MyRepositoryCustom {
#Autowired
private JPAQueryFactory factory;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = factory.query();
...
}}
#Configuration
public class Config {
#Autowired
private EntityManager em;
#Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}

Conflict beeween JPA #Entity and Spring #Component/#Resource/#Configurable

I want to Autowire a class which is annotated via JPA #Entity, so i added Spring's #Component/#Resource/#Configurable annotations but it will not allow me to autowire that class.
Is there any other annotation required ?.
Sample is:
#Component
#Entity
#Table
public class Employee{
#Autowired
TestService testService;
...
}
Where class TestService is annotated with #Service .
You can easily do that, make a constructor and pass the injected Service as perameter:
#Component
#Entity
#Table
public class Employee{
Employee(TestService testService){
//Do some work
}
...
}
now caller class..
#Service
public class CallerClass{
#Autowired
TestService testService;
Employee employee =new Employee(testService);
}
You need to use #Configurable
#Entity
#Table
#Configurable
public class Employee{
#Autowired
TestService testService;
...
}
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-atconfigurable

Resources