Spring Boot with MongoTemplate - java

I am new to Spring Boot and MongoDb.
Trying some examples with Mongo Repositories and Spring Boot.
But after going through some of the documents found that Mongo Template is will be a better option. Unable to get a proper Spring Boot with Mongo Template example.
Can someone please help me out with an example for the same.
Do we need to create a User defined Repositories interface and extend Repositories or CRUD Repository, while trying for Mongo Template ?

For further explanation, you can even use both at the same time.
MongoRepository is just an abstraction layer, like MongoTemplate, but with simpler interface.
If you found doing some kind of operation is too complicated with Spring query-creation, and somehow doesn't want to use #Query (for example, you want IDE type hint when constructing queries), you can extend the MongoRepository and use MongoTemplate as the query mechanism.
First we extend our repository with our custom interface.
#Repository
public interface ArticleRepository extends MongoRepository<Article, String>, CustomArticleRepository {
}
Then declare the interface.
public interface CustomArticleRepository {
List<Article> getArticleFilteredByPage(int page, int num);
}
And then implement our custom repository. We can autowire the MongoTemplate here and use it to query the database.
public class CustomArticleRepositoryImpl implements CustomArticleRepository {
#Autowired
MongoTemplate mongoTemplate;
#Override
public List<Article> getArticleFilteredByPage(int page, int num) {
return mongoTemplate.findAll(Article.class)
.skip(page * num)
.take(num);
}
}
Last, we use the ArticleRepository.
#Service
public class ArticleServiceImpl {
#Autowired
private ArticleRepository articleRepository;
public List<Article> getArticleByPage() {
return articleRepository.getArticleFilteredByPage(1, 10);
}
}

I have found some examples using Mongo Template
http://docs.spring.io/spring-data/data-document/docs/current/reference/html/#mongo-template
http://www.mkyong.com/mongodb/spring-data-mongodb-hello-world-example/
If you are interested in using JPA, please see below
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>{mongo.driver.version}</version>
</dependency>
application.properties
#Mongo DB
spring.data.mongodb.database=
spring.data.mongodb.host=
spring.data.mongodb.password=
spring.data.mongodb.port=
spring.data.mongodb.repositories.enabled=
spring.data.mongodb.uri=
spring.data.mongodb.username=
SpringBoot class
#SpringBootApplication
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
public class UserApp {
Mongo Repository
#Repository
public interface UserRepository extends MongoRepository<User, Long> {}

Related

Why get warning in repository "Unnecessary `#Repository`"

I am working on Spring Boot project. I have repository file in my project but it will show me a warning message in repository class Unnecessary #Repository. I am extending a JpaRepository<> with my repository. My Spring version is 4 and JDK version is 17.
Here is my dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Here is my repository
#Repository // Here I get a warning to remove this annotation becasue its unnecessary
public interface CollegeRepo extends JpaRepository<College, Integer>{
}
You are extending JpaRepository<T, ID> interface, it means that spring boot must autoconfigure this repository bean for you, namely, it will be configured a proxy bean of SimpleJpaRepository<T, ID>.
In simple words, we do not just create a bean using #Repository or #Component annotation, we extend the spring-data interface and then our repository bean will be autoconfigured.
When to use #Repository
You want to provide your own implementation of how to access the data layer and what should be done. In this case marking your implementation class with #Repository will allow you to have this class managed by spring so that you can autowire necessary fields to access data layer like EntityManager , JdbcTemplate ...etc. Although Component, and #Repository in the most fundamental level just register spring beans there are some slight enhancements using #Repository which might make it neccessary to use and also best practice in the current case.
As per doc1
A class thus annotated with #Repository is eligible for Spring
DataAccessException translation when used in conjunction with a
PersistenceExceptionTranslationPostProcessor.,
and doc2
PersistenceExceptionTranslationPostProcessor
Bean post-processor that
automatically applies persistence exception translation to any bean
marked with Spring's #Repository annotation, adding a corresponding
PersistenceExceptionTranslationAdvisor to the exposed proxy
Example of above case use with #Repository.
#Repository
public class CustomCarRepositoryImpl implements CustomCarRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<CarEntity> findCarsWithSpeed(Integer speed) {
return entityManager.createQuery("Query to execute")
.setMaxResults(50).getResultList();
}
}
public interface CustomCarRepository {
List<CarEntity> findCarsWithSpeed(Integer speed);
}
Then you can autowire in your other components the CustomCarRepository and access the data layer as you have implemented.
When Not to use #Repository
When you just declare your interface and you extend from any Spring child interface of Repository from org.springframework.data.repository.
Example
public interface CarRepository extends JpaRepository<CarEntity, Long> {
List<CarEntity> findCarsWithSpeed(Integer speed);
}
In that case Spring Boot will be able to create the repository bean for you automatically from auto configuration.
The only further action needed is if your own interfaces extending from Repository do not exist in the same package or subpackage of where your #Configuration or #SpringBootApplication exists then you would need
either #EnableJpaRepositories(basePackages = {"base-package-where-repositories-exist"})
or #AutoConfigurationPackage(basePackages = {"base-package-where-repositories-exist"})
as to help spring boot identify the package it should look for the auto configuration of that repository. ( The later #AutoConfigurationPackage will affect both repositories and other things required for auto configuration like entities scan and more. So it should be used with care in a project and not just for repositories.)
The Solution depends upon what type of database you intend to use. "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured." Error you're getting is because you have not set the url for the database in the application.properties. To solve this issue I would recommend you to Open the application.properties and add the following according to your system:
spring.datasource.url=jdbc:mysql://localhost:3306/restapi
spring.datasource.username=root
spring.datasource.password=

Spring #Repository #Document is not necessary?

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.

How to disable spring-data-mongodb in spring-boot

I am new to SpringBoot. I have built a simple application which should use fake data in the development environment, and connect to MongoDb in the test environment. Dev environment does not have mongodb setup.
I have tried using Spring Boot qualifiers/profiles to achieve it.
I have a main class which looks like the following:
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
I have a DAO interface StudentDao.java
public interface StudentDao {
Student getStudentById(String id);
}
I then created a couple of implementations for the DAO, one for fake data, and one for data from Mongo
FakeStudentDaoImpl.java
#Repository
#Qualifier("fakeData")
public class FakeStudentDaoImpl implements StudentDao {
private static Map<String, Student> students;
static {
students = new HashMap<String, Student>(){
{
put("1", new Student("Ram", "Computer Science"));
}
};
}
#Override
public Student getStudentById(String id){
return this.students.get(id);
}
}
MongoStudentDaoImpl.java
#Repository
#Qualifier("mongoData")
public class MongoStudentDaoImpl implements StudentDao {
#Autowired
private MongoStudentRepo repo;
#Override
public Student getStudentById(String id) {
return repo.findById(id).get();
}
}
The MongoStudentRepo is a simple interface extending MongoRepository:
public interface MongoStudentRepo extends MongoRepository<Student, String> {
}
And my POM file has the following dependencies called out:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
Of course, I have other controller classes.
This works fine in the Test environment, where there is a MongoDb, and it is able to connect to it. However, when I am trying to start it in my local environment, it fails to start because it is not finding MongoDb on startup.
How do I disable the MongoDb part in my local environment (and just use fake data)? I want to make the same code work in both environments.
Thanks in advance.
I had the same problem, and found the solution you ask for in this other question:
Spring Boot. How to disable Initialization of JPA Conditionaliy
For example, if you want to disable spring-data-mongodb in the development environment, then, assuming that you run under a "dev" profile:
application-dev.yml:
spring:
autoconfigure:
exclude: org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
You can use an embedded MongoDB database. Here an example.
Several possible options:
1) You can use spring profiles. Map one bean with #Profile("test) and second one with #Profile("prod"). To specify which profile to use --spring.profiles.active=test
2) You can have different configurations.
application-prod.yml
--------------
mongo-url:produrl
application-test.yml
--------------
mongo-url:localhost
Use spring active profiles to select config. To use local profile you need to setup local mongo instance. And you can have several options again: just download instance, docker image, embeded mongo.

How to create a JPA implementation with Spring that is not added to the Spring context?

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?

Spring Data REST repository 404 from time to time

I have the Spring Boot application with Spring Data REST.
I have the following classes in it:
Data JPA repository for authentication purposes:
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
Secured Data REST repository for API usages:
#RepositoryRestResource
#Secured(Role.ROLE_USER_READ)
public interface UserDataRestRepository extends PagingAndSortingRepository<User, Long> {
#Override
#Secured(Role.ROLE_USER_WRITE)
<S extends User>S save(S entity);
#Override
#Secured(Role.ROLE_USER_DELETE)
void delete(Long id);
}
Repository REST configurer adapter:
#Configuration
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setRepositoryDetectionStrategy(RepositoryDetectionStrategies.ANNOTATED);
config.setReturnBodyOnCreate(true);
config.setReturnBodyOnUpdate(true);
config.setReturnBodyForPutAndPost(true);
}
}
The problem is when I start up my application, Data REST repository for API is unavailable from time to time. I guess it's because Spring rewrites repository bean for type User with the first JPA repository.
In Actuator beans endpoint I can see both beans even if REST API says 404 for /users page.
Again, this behavior is unpredictable for me - sometimes it works, sometimes doesn't.
Do you know the way how to tell to Spring to use the exact bean for Data REST?
Thanks in advance.
After some investigation, I've found exactly the same issue posted here.
Also, see this one.
Finally, I've merged 2 repositories into one and it solved my problem. But this is definitely not the way I prefer.

Categories

Resources