Problem while connecting Spring boot with MongoDB - java

I want to connect mongodb with spring project. But i faced the NullPointerException. How can I use save() in Interface MongoRepository? Should I make concrete class and override it?
about Interface MongoRepository: https://docs.spring.io/spring-data/mongodb/docs/current/api/org/springframework/data/mongodb/repository/MongoRepository.html
I dont know much about Java, Spring, DI(Dependency Injection).😭
[Test.java]
#SpringBootTest
class PatientsServiceTest {
private PatientsService patientsService = new PatientsService();
#Test
void save() {
Patients patient = new Patients();
~~~ object patient initialize ~~~
patientsService.save(patient);
}
}
[PatientsService.java]
#Service
public class PatientsService {
#Autowired
public PatientsRepository patientsRepository; // <- Null exists
public void save(Patients patients){
~~~ additional codes ~~
patientsRepository.save(patients);
}
}
[PatientsRepository.java]
#Repository
public interface PatientsRepository extends MongoRepository<Patients, String> {
public Patients findBySSN(String SSN);
}
error Messages

Related

How to use the data stored in cache for another function in springboot

I have created a sample spring boot application which have a class "CacheManagement "where I have a function "getAllValue" which return the a customer object whose values I need to cache.
This class also have a function checkCache which uses the data from cache. How Can I implement this. Somebody please help me on this. I am providing the class I have written.PFB the class
#Service
public class CacheManagement {
#Cacheable(value = "service",key = "list")
public Customer getAllValue(){
Customer customer = new Customer();
System.out.println("Inside cache");
customer.setId("1");
customer.setName("Shilpa");
return customer;
}
public String checkCache() {
Customer cus = getAllValue();
System.out.println(cus.toString());
return "1";
}
}
PFB the main class:
#SpringBootApplication
public class DemoApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) {
CacheManagement cacheService = new CacheManagement();
cacheService.getAllValue();
cacheService.checkCache();
}
}
According to my need the sysout statement should only print once right? But my issue is it is printing twice .Means the getAllValue() call from checkCache() method is calling the function itself not fetching data from cache.
Please help me to resolve this issue.
There are multiple issues with your code.
CacheManagement cacheService = new CacheManagement(); creates an instance which is not a Spring container managed bean. The Spring magic will not work here . You should get a Spring container managed bean of cacheManagement for all your Spring framework support to kickin.
#SpringBootApplication
#EnableCaching
public class DemoApplication implements CommandLineRunner {
#Autowired
CacheManagement cacheService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) {
cacheService.getAllValue();
cacheService.checkCache();
}
}
2.Assuming you have enabled caching using #EnableCaching ( Refer here )
#SpringBootApplication
#EnableCaching
public class DemoApplication implements CommandLineRunner{
...
}
The Spring Caching works with the help of Spring AOP and Spring AOP works on proxies. Calling getAllValue() from within a method of the same class ( here checkCache()) is called a self-invocation. Spring AOP will not be able to advice the method call in this case , as it will not go through the proxy and hence the caching logic fails to work.
To understand this concept in detail , do refer the official spring reference documentation : Understanding AOP Proxies . Read through the section starting with The key thing to understand here is that the client code inside the main(..)
The resolution to your issue is to make sure the checkValue() call goes through the proxy so that the cached result is returned. Since you need the method (checkValue()) as part of the same bean , a self reference , will do it.
You must read through the official documentation on the same topic though to understand the implications. Section starting with As of 4.3, #Autowired also considers self references for injection ..
Following code should fix the issue for you
#Service
public class CacheManagement {
#Autowired
CacheManagement self;
#Cacheable(value = "service",key = "list")
public Customer getAllValue(){
Customer customer = new Customer();
System.out.println("Inside cache");
customer.setId("1");
customer.setName("Shilpa");
return customer;
}
public String checkCache() {
Customer cus = self.getAllValue();
System.out.println(cus.toString());
return "1";
}
}
Read more on caching here

Android Hilt Testing: Bind different database per test

I am currently writing tests for my Android application using Hilt. The App got a Room database, which I want to use in my test classes. Furthermore, I want in some test classes an empty database and in others a database with test values. My question is if I can define in my Test, which provider (empty/full DB) is used for all injections that are happening in the test class?
I tried using the #Named annotation, Qualifiers, and #BindValue. With the first two I am able to use the correct provider for my database, but I do not know how this could be used when injecting my repository. With #BindValue I am not sure if this can be used for this at all.
Here is some simplified code of my problem:
TestDatabaseModule:
#Module
#TestInstallIn(components = SingletonComponent.class, replaces = DatabaseModule.class)
public class TestDatabaseModule {
// Provide empty DB
#Singleton
#Provides
AppDatabase provideInMemoryDbEmpty(#ApplicationContext Context context) {
return Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build();
}
// Provide populated DB
#Singleton
#Provides
AppDatabase provideInMemoryDbFull(#ApplicationContext Context context, Provider<AppDatabase> databaseProvider) {
// AppDatabasePopulateCallback adds some test data
return Room.inMemoryDatabaseBuilder(context, AppDatabase.class).addCallback(new AppDatabasePopulateCallback(databaseProvider)).build();
}
}
Module providing DAO and repository
#InstallIn(SingletonComponent.class)
#Module
public class AccessModule {
#Singleton
#Provides
MyDao provideMyDao(AppDatabase appDatabase) {
return appDatabase.myDao();
}
#Provides
MyRepository providesMyRepository(MyDao myDao) {
return new MyRepository(myDao);
}
}
Test Class one:
#HiltAndroidTest
#SmallTest
public class TestOne {
#Rule
public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
#Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
// Inject repository that uses empty DB
#Inject
MyRepository myRepository;
#Before
public void createDb() {
hiltRule.inject();
}
#Test
public void testOne() {
// Use my repository with the empty database
}
Test class two:
#HiltAndroidTest
#SmallTest
public class TestTwo {
#Rule
public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
#Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
// Inject repository that uses populated DB
#Inject
MyRepository myRepository;
#Before
public void createDb() {
hiltRule.inject();
}
#Test
public void testTwo() {
// Use my repository with the full database
}
So in the TestOne class, I want to use the repository that can access the empty database, and in the TestTwo class the repository that uses the database with test data.
I know I could populate the database in every test I want to use the data. That is what I will do if it is not possible with Hilt. I am glad for any advice to solve my problem or someone answering that I am on the wrong track if it is not possible.
Thank you!

How to instantiate object(Jdbc template) inside Hazelcast Map store

I'm trying to Autowire jdbc template inside mapStore.. but I'm getting null pointer exception.
I worked on so many examples but sill not able to resolve this issue..
Here is my main class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class TestCacheApplication {
public static void main(String[] args) {
SpringApplication.run(TestCacheApplication.class, args);
System.err.println("......running successfully......");
}
}
Here is my cache configured code
#Component
public class CacheConfig {
#Bean
public static Config config() {
System.err.println("config class");
Config config = new Config();
config.setInstanceName("hazelcast");
MapConfig mapCfg = new MapConfig();
mapCfg.setName("first-map");
mapCfg.setBackupCount(2);
mapCfg.setTimeToLiveSeconds(300);
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setClassName(DataMapStore .class.getName()).setEnabled(true);
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
}
and TblRepo implementation
#Service
public class DataTblRepoImpl implements DataTblRepo {
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public void save(String id, String name) {
Object[] params = new Object[] { id, name };
int[] types = new int[] { Types.VARCHAR, Types.VARCHAR };
String insertSql = "INSERT INTO public.person(id, name) VALUES(?, ?)";
jdbcTemplate.update(insertSql, params, types);
}
and TblRepo interface I have annotated with #Repository annotation..
And My map store class
#SpringAware
public class DataMapStore implements MapStore<String, ModelClass>{
#Autowired
DataTblRepo dataTblRepo;
#Override
public void store(String key, ModelClass value) {
dataTblRepo.save(value.getId(), value.getName());
}
//remaining methods will come here
}
and Controller
#RestController
#CrossOrigin(origins = "*")
#RequestMapping("/api/v1")
public class DataController {
#Autowired
DataService dataService;
HazelcastInstance hazelCast = Hazelcast.getHazelcastInstanceByName("hazelcast");
#PostMapping("/{test}")
public String saveDatafrom(#RequestBody ModelClass model) {
hazelCast.getMap("first-map").put(model.getId(), model);
return "stored";
}
}
Here is the program flow.. When I start the application, first Cacheconfig class will run.
In the controller when I perform the map.put() operation, data will go to the DataMapStore class and call the store method to save the data in database..since DataTblRepo is null so operation is failing at the store method itself..*
I tried adding #component on the DataMapStore class also
but in my case I'm getting this error
"message": "Cannot invoke "com.example.demo.repo.DataTblRepository.save(String, String)" because "this.dataTableRepo" is null",
I saw this same issue in many platforms also but still not able to resolve this issue.
Any suggestions would be very helpful
SpringAware is for Hazelcast distributed objects (cf. documentation).
The MapStore in your example is not a distributed object but a simple plain object. It should be managed by Spring itself. You should replace the #SpringAware annotation by a Spring #Component annotation.
The next issue is that your map store configuration makes Hazelcast responsible to instantiate the MapStore. If this happens, you won't benefit from Spring's Dependency Injection mechanism. You should directly set the instance created by Spring.
Replace SpringAware by Component
#Component
public class DataMapStore implements MapStore<String, ModelClass> {
// ...
}
Use the Spring-configured MapStore instance
#Bean
public Config config(DataMapStore mapStore) { // Ask Spring to inject the instance
// ...
MapStoreConfig mapStoreCfg = new MapStoreConfig();
mapStoreCfg.setImplementation(mapStore); // Use it
mapCfg.setMapStoreConfig(mapStoreCfg);
config.addMapConfig(mapCfg);
return config;
}
I also removed the static keyword on the config() method.
Note that this way of using MapStore couples it with the "client" code. This means you need to use Hazelcast embedded. For more information about embedded mode vs. client/server, please check the documentation related to topology.

SpringBoot direct MongoRepository to specific MongoTemplate

I have an app with multiple mongo configurations. This is achieved through some #Configuration classes like so
public abstract class AbstractMongoConfig {
private String database;
private String uri;
public void setUri(String uri) {
this.uri = uri;
}
public void setDatabase(String database) {
this.database = database;
}
public MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(new MongoClientURI(this.uri)), this.database);
}
abstract public MongoTemplate getMongoTemplate() throws Exception;
}
Config 1 -- app
#Configuration
#ConfigurationProperties(prefix="app.mongodb")
public class AppMongoConfig extends AbstractMongoConfig {
#Primary
#Override
#Bean(name="appMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
Config 2 -- test
#Configuration
#ConfigurationProperties(prefix="test.mongodb")
public class TestMongoConfig extends AbstractMongoConfig {
#Override
#Bean(name="testMongoTemplate")
public MongoTemplate getMongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
Then in my properties
test.mongodb.uri=mongodb://127.0.0.1/test
test.mongodb.database=test
app.mongodb.uri=mongodb://127.0.0.1/app
app.mongodb.database=app
So, two mongo configs wired up to an instance running locally but with different databases. I have tried it with different addresses also but it behaves the same.
Anyway, this then gets used via an Entity and MongoRepository
#Document(collection="collname")
public class TestObj {
#Id
private String id;
private String username;
private int age;
// getters & setters
}
Repo
#Repository
public interface TestObjRepository extends MongoRepository<TestObj, String> {
public TestObj findByUsername(String username);
}
However when I use this in some class somewhere
#Service
public class ThingDoer {
#Autowired
TestObjRepository toRepo;
public void doStuff() {
TestObj to = new TestObj("name", 123);
toRepo.save(to);
}
}
This object gets written into the TestMongoConfig one not the AppMongoConfig as I would expect since that's the one annotated with #Primary. Further, if I add the #EnableMongoRepositories annotation on the ThingDoer like:
#EnableMongoRepositories(basePackages={"com.whatever.package"}, mongoTemplateRef="appMongoTemplate")
It still doesn't work. It still writes to the db referenced by "test".
If I #Autowire in the MongoTemplate directly and use that, it works as I expect. Things go to the "app" repo. How can I tell it which database that the TestObjRepository should be writing to and reading from?
So, if anyone else still has this problem, the solution is this:
#EnableMongoRepositories(basePackages={"com.whatever.package"}, mongoTemplateRef="appMongoTemplate")
You have to put it on your custom mongo properties.
Where basePackages is the package path to your repo. You have to have one package for each mongo database, so it looks for the intended repository and model reference.
And you also have to disable mongo auto configuration by spring, when using multiple DBs:
spring.autoconfigure.exclude:org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
This is a great tutorial:
https://dzone.com/articles/multiple-mongodb-connectors-with-spring-boot

Testing JPA Entities

I want to know how to write Junit test for Spring Repository classes.
As example :
class-AccountMoveActionDet
Jpa interface-AccountMoveActionDetlJpaRepository
And I want to test this repository class work or not.Spring Jpa support some methods like
List findAll();
deleteAll();
I wrote a class just like below:
#RunWith(SpringJUnit4ClassRunner.class)
public class AccountTypeMovementJpaRepositoryTest extends AbstractJpaTest {
#Autowired
AccountTypeMovementJpaRepository accountTypeMovementJpaRepository;
#Override
public void test() {
executeSqlScript("/fixtures/accountTypeMovementJpa.sql");
assertEquals("accountTypeMovementJpaRepository Test", accountTypeMovementJpaRepository.findAll().size(),
JdbcTestUtils.countRowsInTable(getJdbcTemplate(), "COF5REP"));
}
}
Error creating bean with name
'com.gayan.cmp.jparepositories.test.AccountTypeMovementJpaRepositoryTest':
Please help me to resolve this.
If you use spring-boot 1.4 and above the best place to start, Testing the JPA slice :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void findByUsernameShouldReturnUser() {
this.entityManager.persist(new User("sboot", "123"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("123");
}
}

Categories

Resources