I have a spring boot application and connected to Mongo DB.
I know all most all of documents or blogs said the sample code should like this:
#Repository
public interface ProductRepository extends MongoRepository<Product, String> {
}
#Document
public class Product {
private String id;
private String name;
private int price;
}
But I found even if I remove #Repository and #Document annotations. The application still can start without error. Spring still can know ProductRepository is spring bean and also can CRUD Product collection.
So does these not necessary to add #Repository and #Document? Or is there any difference add or not add?
Not necessery.
Spring can found it, because you extends the MongoRepository interface, and add Product as it type.
#Repository is useful anyway, for example if you create a custom repository.
#Document is also, if you want to specify custom property values, for example collection name..
The annotation #Repository registers a class as a Spring bean which makes it autowirable. Spring Data doesn't use annotations but provides functionality through extending reposotory classes such as JpaRepository or MongoRepository.
Related
I have the #Controller, #Service and #Repository classes.
The application works fine, but I think I'm not using the annotations properly for the "entity" and "repository" classes.
I'm actually not using a db(not even an in-memory db) and don't intend to.
I'm currently annotating the repository with #Repository and the entities with #Service and this is my concern: am I doing this correctly?
How should I design and use the Spring annotations to wire the entity and repository classes to the service if I don't want to persist the data?
Currently it looks like this:
Service class
#Service
public class ServiceClass{
#Autowired
RepositoryClass repositoryClass;
public ServiceClass(RepositoryClass repositoryClass) {
this.repositoryClass = repositoryClass;
}
}
Repository class
#Repository
public class RepositoryClass{
#Autowired
private Entity entity;
public DocumentRepository(Entity entity) {
this.entity = entity;
}
}
Entity class
#Service
public class Entity {
private Map<String, List<Integer>> entityMap;
public Entity (Map<String, List<Integer>> entityMap) {
this.entityMap = entityMap;
}
}
Annotating an entity class with #Service is wrong.
A class annotated with #Service is usually stateless, and for that reason, there is usually only one object of such a class.
A class annotated with #Entity is usually stateful, and for that reason, there are usually many objects of such a class.
An example scenario is a simple news service:
There is one NewsService that contains interesting code to fetch news from the repository.
For each news item, there is a NewsEntity object, holding the data of the individual news item.
The question is: what's the role of each class here. Usually a repository is the point to access data. An entity is a data object, not a logic component, so it is usually created and managed by the repository, in this example, not Spring.
It's hard to firmly say anything with only that code (no information about how every component is used), but I would remove the #Service from the Entity class. The other classes are ok with those annotations.
You can't use #Entity from data-jpa w/o setting up some db, so your entities don't need any annotation. They aren't beans that you need to wire in anywhere.
But the general idea that 'when you don't know' it's probably a service is a good one. XD
The other annotations are right.
You can basically annotate them with #Component, #Service, #Config, #Repository ... It wouldn't break your code, the names are mostly just to be more clear for the people working on the code.
I have created a spring project which has a controller and all of its logic is written in Service Interface, which is implemented by ServiceImpl class. I have a repository with which has a model.
//Service Interface
public interface Service{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class ServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me an empty list.
}
}
//KPIRepository
#Repository
public inerface KPIRepository extends MongoRepository<KPIModel, String>
{
}
//Another Service Interface in another package
public interface AnotherService{
List<Model> getAllKpiData();
}
//ServiceImpl Class
#Service
public class AnotherServiceImpl implements Service{
#Autowired
private KPIRepository kpiRepository;
#override
private List<Model> getAllKpiData()
{
this.kpiRepository.findAll();
//gets me list of values, which are inside the repo(master data).
}
}
Both of them are pointing to same repo, but in AnotherService class i am able to get values inside the repository, whereas i am not able to get any values inside Service, on doing this.kpiRepository.findAll().
Do you have spring-boot-starter-data-mongodb dependency on classpath? If yes, then is KPIRepository in the same package as your main class? If not then in your main class put this annotation #EnableMongoRepositories(basePackageClasses=KPIRepository.class) to safely tell Spring Data MongoDB to scan a different root package by type if your project layout has multiple projects and its not finding your repositories. Or you can use #EnableMongoRepositories(basePackages = "com.acme.repositories.mongo") to specify the package that contains all of your repositories.
The presense of spring-boot-starter-data-mongodb will automatically enable #EnableMongoRepositories. And Spring will automatically create proxies of all classes implementing Repository<T,ID> (your class implements MongoRepository, which itself implements Repository) and create beans of them and make them available for injection. And when your repository is in a different package then it is unable to create proxies of your repository, hence fails to create a bean of it. And since there is no bean, it cannot inject it, hence you see the error.
Did you use the #EnableMongoRepositories annotation? Take a look to this link: https://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html. Review the "6.2 usage" point.
Regards
This question already has answers here:
How are Spring Data repositories actually implemented?
(1 answer)
How does Spring Data JPA work internally
(1 answer)
Closed 3 years ago.
I have a code like this:
Repository
#Repository
public interface EquipmentRepository extends JpaRepository<Equipment, Integer>{
Equipment findById(int id);
}
Service
#Service
public class EquipmentServiceImpl implements EquipmentService {
#Autowired
EquipmentRepository equipmentRepository;
#Override
public Equipment findById(int id) {
return equipmentRepository.findById(id);
}
}
I wonder that why i can call a method of 'interface EquipmentRepository'. EquipmentRepository is a interface, Right ?
Spring Repository is responsible for importing the DAO's into the DI container and also it makes the unchecked exceptions into Spring DataAccessException. The Spring Repository annotation is meta annotated with the #Component annotation so that the repository classes will be taken up for component scanning.
Teams implementing traditional Java EE patterns such as "Data Access
Object" may also apply this stereotype to DAO classes, though care
should be taken to understand the distinction between Data Access
Object and DDD-style repositories before doing so. This annotation is
a general-purpose stereotype and individual teams may narrow their
semantics and use as appropriate.
A class thus annotated is eligible for Spring DataAccessException
translation when used in conjunction with a
PersistenceExceptionTranslationPostProcessor. The annotated class is
also clarified as to its role in the overall application architecture
for the purpose of tooling, aspects, etc.
Source: JavaDoc
but in your case you are also extending the JpaRepository of Spring Data JPA. Spring Data automatically provides implementations of common CRUD operations. The JpaRepository extends the interface CrudRepository which has the methods declared for all basic crud operations.
public interface EquipmentRepository extends JpaRepository<Account, Long> { … }
Defining this interface serves two purposes:
First, by extending JpaRepository we get a bunch of generic CRUD
methods into our type that allows saving Equipments, deleting them and
so on.
Second, this will allow the Spring Data JPA repository infrastructure
to scan the classpath for this interface and create a Spring bean for
it.
The #EnableJpaRepositories scans all packages below com.acme.repositories for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository (spring data provides default imlpementations of CRUD repository through this class).
So that is why even when you haven't defined the method , you are able to do crud operations through this setup.
Refer : https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.repositories
I'd like to add a cache using Spring and annotations to a repository that uses Spring Data but I'd prefer not to add the cache annotations to the same interface that extends the JpaRepository interface to separate concerns.
I'd like to create a wrapper around the Spring JPA repository defined as follows:
#NoRepositoryBean
public interface MyRepository extends JpaRepository<MyEntity, Long>{
MyEntity findOneByMyAttribute(String myAttribute);
}
public interface MyRepositorySpring extends MyRepository {}
Spring by default creates an implementation and adds it to the beans context even without an #Repository annotation present.
My problem is that I would like to have full control of the beans in the context. I'd like to create a wrapper like this:
public class MyCachedRepository extends MyRepository {
private final MyRepository wrappedRepository;
#Cacheable
MyEntity findOneByMyAttribute(String myAttribute){
wrappedRepository.get(myAttribute);
}
}
where wrappedRepository is the implementation created by Spring Data and then create the configuration as follows:
#Configuration
public class MyConf {
#Bean
public MyRepository myRepository() {
return new MyCachedRepository(myJpaRepository());
}
private MyRepository myJpaRepository() {
//here I would have the code that generates the implementation
}
}
Like this I could always have my code injecting a myRepository bean and let the configuration decide whether the bean in the context is the wrapped repository or the jpa one as there would be only one at all times.
Is there any way to do this?
I'm hoping someone could shed some more light on my confusion with JPA entities in a Spring Boot project. I've heard that one should never call new in a Spring project. I understand that this is to allow Spring to manage all of the beans, and getting a bean can be done through injection or through the application context explicitly.
However, it's not clear to me how to get a new JPA Entity. If I have a class annotated with #Entity and a repository class that handles my data access, how do I obtain a new entity object in my service layer?
I've included #EntityScan in my application's main class so I would assume that Spring is aware of the entity. But when I try to get it through the ApplicationContext an exception is raised. This makes sense because I don't believe the #Entity annotated classes are Spring Beans, and I don't think it would be correct to also annotate it with #Component. Any clarification would be greatly appreciated.
I'm currently using the new keyword and creating the entity objects myself in the service layer. A very simple example is below:
entities/User.java
#Entity
#Table(name = "users")
public class User {
#Id
private Long id;
private String username;
// Getters & Setters ...
}
repositories/UserRepository.java
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
}
services/UserServiceImpl.java
#Service
public class UserServiceImpl implements UserService {
UserRepository userRepository;
#Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void createAndSaveUser(String username) {
User user = new User();
user.setUsername(username);
userRepository.save(user);
}
}
And we could assume that there was some other controller classes that would utilize the service layer.
In this example I am explicitly calling the new keyword in the service class method createAndSaveUser. Is this the correct way to do it or should I be getting some prototype bean from Spring that maps to my JPA entity?
In spring you can autowire/inject your beans, components or services. However the entity should not be autowired since these interactions are done through your repository. Your repository can be autowired.
When you want to create a new instance of your entity you are allowed to call new, because this does not need to be managed by spring. You can simply use the autowired repository to save it in the database. This also works the other way around because obviously you would need the autowired repository to retrieve your entity.
So yes, your method is correct.
I hope this makes it clearer for you, if you have any questions feel free to ask :)
Whatever you are doing is completely valid in spring. In example you have provided above I could figure out that you want your entity class object itself to store the values. Its absolutely correct.
You have to use new keyword to achieve that.
If you still wish to not create a new object for your Entity you have another option to do it through Bean/POJO/VO classes and mapping your entity object with these classes.
But Still i will tell that whatever you have done is completely fine.
Actually the object you are creating is for storing value purpose not just because you have some method is there in your class and so you are bound to create new Object to be able to call that method(As we do in normal java project).In spring that is handle by #Autowired annotation to create object.
Simple example is you will be auto-wiring your repositories in your service classes.
I hope this help.
It sounds good to me: you create your entity and then ask the repository to store it.. no problem with Spring.
have you checked this out? :
http://spring.io/guides/gs/accessing-data-jpa/
have fun