I am trying to create a generic class that will interact with multiple Spring MongoDB repositories. The current code I have compile but blows up with a NULL Exception because both of the repositories are NULL.
Here is my setup, I can't find any documentation that would ensure the autowiring in the wrapper class. Is there something that I am missing that will execute the autowiring. I am using gradle and I added the dependency based off the basic spring mongo project.
Thanks
InstructorDBRepository.java
package courseSystem;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface InstructorDBRepository extends MongoRepository<InstructorDB, String> {
InstructorDB save(InstructorDB saved);
void delete(InstructorDB deleted);
List<InstructorDB> findAll();
}
DBinterface.java
package courseSystem;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
public class DBinterface {
#Autowired
private AcademicRecordDBRepository AcademicRecordRepository;
#Autowired
private InstructorDBRepository InstructorRepository;
public DBinterface() {}
public void deleteAllTables()
{
AcademicRecordRepository.deleteAll();
InstructorRepository.deleteAll();;
}
//Some additional functions
}
Related
I am trying to learn Spring Boot and restful application from an online tutorial. But the code I wrote for it somehow gives me an error. I have written a CommandLineRunner class like so:
package com.trial.cas.preload;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import com.trial.cas.employee.repository.EmployeeRepository;
import com.trial.cas.employee.pojo.Employee;
#Configuration
class LoadDatabase implements CommandLineRunner {
private static final Logger log = java.util.logging.LogManager.getLogManager().getLogger("LoadDatabase");
#Autowired
EmployeeRepository employeeRepository;
#Override
public void run(String... args) throws Exception {
log.info("Preloading " + employeeRepository.save(new Employee("Bilbo Baggins", "burglar")));
log.info("Preloading " + employeeRepository.save(new Employee("Frodo Baggins", "thief")));
}
}
My EmployeeRepository class is like this:
package com.trial.cas.preload;
import org.springframework.data.jpa.repository.JpaRepository;
import com.trial.employee.pojo.Employee;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
My main class is written like:
package com.trial.cas.logging.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CASLoggingToolApplication {
public static void main(String[] args) {
SpringApplication.run(CASLoggingToolApplication.class, args);
}
}
But when I run the Spring Boot application, the lines in the run method never execute. I have a debug point point in the first line of the run method. But that never gets triggered.
Please help! Thanks in advance.
By default Spring Boot will only scan packages below the package of the main application class (in your example it would be the package com.trial.cas.logging.example and all subpackages.
You can either move the application class to a common super package for your application, or specify all other packages, that should be scanned for beans etc. on the annotation:
#SpringBootApplication(scanBasePackages = {"com.trial.cas.logging.example", "com.trial.cas.preload"})
Try using #Component instead of #Configuration when annotating your LoadDatabase class.
Also, if I recall correctly, for Spring Boot to run properly it is necessary to annotate your EmployeeRepository with #Repository.
Another last tip, try to avoid field injections like:
#Autowired
EmployeeRepository employeeRepository;
It has several drawbacks compared to constructor injection, which are described in detail in this answer: What exactly is Field Injection and how to avoid it?
Instead use constructor injection, like this:
private EmployeeRepository employeeRepository;
#Autowired
public LoadDatabase(EmployeeRepository employeeRepository){
this.employeeRepository = employeeRepository;
}
Hi Team,
I am using Spring Boot 2.3.12.RELEASE which internally uses Spring Data Redis 2.3.9.RELEASE as a managed dependency.
When I am trying to save an object to the Redis cache using Spring Boot CRUD repository, it is getting stored without any error and I can see the object stored via Redis Manager.
However, when I try to fetch the same object using the same id i.e. using findById() method of CRUD repository, I am unable to find it.
Moreover, when I try findAll() on the same CRUDRepository object I get Optional.empty result which is strange as findAll() should return all records present in the repository.
I have added the configuration, repository and model class codes and some screenshots below for your perusal.
Please Note: I know there are many similar questions asked on this platform related to this issue and also I tried the solutions mentioned on such questions, but that didn't work for me.
Any solutions for this issue will be really helpful.
Model Class:
package com.test.cache.entity;
import java.util.concurrent.TimeUnit;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.TimeToLive;
import org.springframework.data.redis.core.index.Indexed;
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
#RedisHash("OTPValidationLogCache")
public class OTPValidationLogCache {
#Id
#Indexed
private String id;
#Indexed
private int validationFailureCount;
#TimeToLive(unit = TimeUnit.MILLISECONDS)
private long expiry;
}
Repository:
package com.test.cache.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.test.cache.entity.OTPValidationLogCache;
#Repository
public interface OTPValidationLogCacheRepository extends CrudRepository<OTPValidationLogCache, String> {
}
Redis Configuration Class:
package com.test.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import java.time.Duration;
#Configuration
#EnableRedisRepositories(basePackages = "com.test")
public class RedisConfig {
public static final long REDIS_CONNECT_TIMEOUT_SECS = 10L;
#Bean
public RedisStandaloneConfiguration redisStandaloneConfiguration() {
final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName("*******");
redisStandaloneConfiguration.setPort(6379);
redisStandaloneConfiguration.setPassword(RedisPassword.of("**********"));
//Credentials hidden for code sharing purpose.
return redisStandaloneConfiguration;
}
#Bean
public JedisConnectionFactory redisConnectionFactory() {
final JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofSeconds(REDIS_CONNECT_TIMEOUT_SECS))
.useSsl()
.build();
return new JedisConnectionFactory(redisStandaloneConfiguration(), jedisClientConfiguration);
}
#Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
return template;
}
}
Redis Manager Screenshot:
Eclipse IDE - Screenshot of Debugging Screen:
Well, I also raised a defect to spring-data-redis repository on GitHub for the same but the defect got closed by one of the maintainers of this repository without even posting any proper solution. He just gave a reference to an existing issue that was even closed without posting any solution. Here is the link to that issue.
https://github.com/spring-projects/spring-data-redis/issues/2130
Hence, while doing some research, I came across a solution that I am sharing here which worked in my case.
The solution is not to use the default CRUD repository methods implemented by Spring Boot, instead, write your own repository class having methods with your criteria to store and fetch the data from the Redis cache. That's it, now you should be able to store/fetch the data using the repository methods across your project.
I am posting an example below for reference.
Custom Repository Interface
package com.test.cache.repository;
import java.io.IOException;
import java.util.Map;
import com.test.cache.entity.OTPValidationLogCache;
public interface OTPValidationLogCacheRepository {
void save(OTPValidationLogCache customer);
OTPValidationLogCache find(Long id);
Map<?,?> findAll() throws IOException;
void update(OTPValidationLogCache customer);
void delete(Long id);
}
Custom Repository Interface Implementation
package com.test.cache.repository;
import java.io.IOException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ScanOptions.ScanOptionsBuilder;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.test.cache.entity.OTPValidationLogCache;
import com.test.configuration.AppConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
#Repository
public class OTPValidationLogCacheRepositoryImpl implements OTPValidationLogCacheRepository {
private String key;
private RedisTemplate redisTemplate;
private HashOperations hashOperations;
private ObjectMapper objMapper;
#Autowired
public OTPValidationLogCacheRepositoryImpl(RedisTemplate redisTemplate, ObjectMapper objmapper) {
this.redisTemplate = redisTemplate;
this.objMapper = objmapper;
}
#PostConstruct
private void init() {
hashOperations = redisTemplate.opsForHash();
}
#Override
public void save(OTPValidationLogCache otpvalCache) {
hashOperations.put(key.concat(otpvalCache.getId().toString()), otpvalCache.getId(), otpvalCache);
setExpiryTime(key.concat(String.valueOf(otpvalCache.getId())), AppConfig.getUserBanDurationInSeconds());
}
#Override
public OTPValidationLogCache find(Long id) {
return (OTPValidationLogCache) hashOperations.get(key.concat(String.valueOf(id)), id);
}
#Override
public Map findAll() throws IOException {
Map<Integer, OTPValidationLogCache> values = Maps.newHashMap();
Cursor c = hashOperations.scan(OTPValidationLogCache.class, new ScanOptionsBuilder().match(key.concat("*")).build());
AtomicInteger count = new AtomicInteger(1);
c.forEachRemaining(element ->
{
values.put(count.getAndIncrement(), objMapper.convertValue(element, OTPValidationLogCache.class));
}
);
c.close();
return values;
}
#Override
public void update(OTPValidationLogCache customer) {
hashOperations.put(key, customer.getId(), customer);
}
#Override
public void delete(Long id) {
hashOperations.delete(key, id);
}
private void setExpiryTime(String key, Long timeout)
{
redisTemplate.expire(key, Duration.ofSeconds(timeout));
}
public synchronized void setKey(String key)
{
this.key = key;
}
}
Hope this helps others who may encounter this issue in the future.
Also, there is one more alternative available for this issue, that is switching to a different library provider such as Redisson, however, I have not tried it yet, so if you want, you may try and check.
You need to have the same package for your entities ,
I resolved the problem by extracting a lib and putting my entities there
You would find an explication here :
https://github.com/spring-projects/spring-data-redis/issues/2114
I'm gonna create a Java console application for accessesing a database (MySQL). I'm gonna use Spring Boot/Spring Data JPA. What is the correct way to create a console application using Spring Boot?
I found a few ways to do this:
spring.main.web-application-type=NONE (in application.properties)
spring.main.web-environment = false (in application.properties)
using the Spring Shell project
implementing the CommandLineRunner interface
I suppose that some of them may be obsolete, have pros and cons. Could you please explain how to create a plain console application using Spring Boot/Spring Data?
Recently, I have done a console application, as you require now. I did that by implementing CommandLineRunner interface. When spring boot starts the application, it will invoke the run(String... args) method of CommandLineRunner interface.
So, you can autowire(or using constructor injection) spring data repositories in this implemention class(e.g. AppRunner) & invoke database operations.
Instead of MySql database, I have you used MongoDB along with some caching operations.
Example:
AppRunner.java
package com.cache.caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class AppRunner implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(AppRunner.class);
BookRepository bookRepository;
public AppRunner(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#Override
public void run(String... args) throws Exception {
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
}
}
BookRepository.java
package com.cache.caching;
import java.net.UnknownHostException;
import java.util.List;
public interface BookRepository {
List<Article> getArticles() throws UnknownHostException;
}
BookRepositoryImpl.java
package com.cache.caching;
import com.mongodb.*;
import org.bson.codecs.pojo.annotations.BsonId;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.MongoCollectionUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
#Component
public class BookRepositoryImpl implements BookRepository{
#Override
#Cacheable("articles")
public List<Article> getArticles() throws UnknownHostException {
MongoClient mongoClient
= new MongoClient(new MongoClientURI("mongodb://localhost:27017"));
DB db = mongoClient.getDB("Mart");
DBCollection collection = db.getCollection("articles");
DBCursor cursor = collection.find();
List<Article> list= new ArrayList<>();
while (cursor.hasNext()) {
Article article = new Article();
DBObject dbObject = cursor.next();
article.setId((Double) dbObject.get("_id"));
article.setSubject((String) dbObject.get("subject"));
list.add(article);
}
return list;
}
}
In your case, you can provide MySQL database connection details here in application.yml / application.properties file.
CachingApplication.java - application starts here, this SpringApplication.run(CachingApplication.class, args); invokes the run(String... args) method of CommandLineRunner interface.
package com.cache.caching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CachingApplication {
public static void main(String[] args) {
SpringApplication.run(CachingApplication.class, args);
}
}
Sample: Sample Full Example Here
This is the userService class that requires a bean of type com.example.repository.userRepository that could not be found
package com.example.services;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.modal.User;
import com.example.repository.userRepository;
#Service
#Transactional
public class UserService {
#Autowired
private userRepository userRepository;
public UserService() {
super();
}
public UserService(userRepository userRepository)
{
this.userRepository = userRepository;
}
public void saveMyuser(User user) {
userRepository.save(user);
}
}
The error message reads :
Consider defining a bean of type 'com.example.repository.userRepository' in your configuration.
This is the repository:
package com.example.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.modal.User;
public interface userRepository extends CrudRepository<User,Integer> {
}
this is the application class
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
public class TutorialProjectApplication {
public static void main(String[] args) {
SpringApplication.run(TutorialProjectApplication.class, args);
}
}
Seems like userRepository interface is outside of spring-boot default scanning i.e. package of that repository interface is not same or sub-package of the class annotated with #SpringBootApplication. If so, you need to add #EnableJpaRepositories("com.example.repository") on your main class.
Update:
After looking at your updated post, you need to add #EnableJpaRepositories("com.example.repository") to TutorialProjectApplication class
Always keep the #SpringBootApplication main class outer package so that it will automatically scan all the subpackages.
In your case you have main class in package com.example.demo; but the repository in package com.example.repository; which are different packages.so spring boot is not able to find the repositories.
So you have to make spring boot aware of the repositories location.
So now you have 2 solutions.
1.Either put repository class in subpackges of Main class package.
2.Or use #EnableJpaRepositories("com.example.repository") in main class.
In your repository you need to annotate the class
#Repository
public interface userRepository extends CrudRepository<User,Integer> {
}
I am still a beginner in Spring Framework so I tried to code a program about "introduction" in Spring AOP but I am facing an error while compiling. Please find below the classes in the package concert:
PerformanceImp.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class PerformanceImp implements Performance {
public void perform() {
System.out.println("This is the performance function");
}
}
Performance.java
package concert;
public interface Performance {
public void perform();
}
Encoreable.java
package concert;
public interface Encoreable {
void performEncore();
}
DefaultEncoreable.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class DefaultEncoreable implements Encoreable {
public void performEncore() {
System.out.println("This is the performEncore function");
}
}
EncoreableIntroducer.java
package concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class EncoreableIntroducer {
#DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}
ConcertConfig.java
package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("concert")
public class ConcertConfig {
}
And the main class:
Main.java
package concert;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);
((Encoreable) pi).performEncore();
pi.perform();
}
}
I am getting the error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'concert.PerformanceImp' available
Any help please ?
You cannot access the implementation (PerformanceImp) by default, because you enabled AOP, which sets to target interfaces instead of implementation. If you would remove EnableAspectJAutoProxy, you would see the code would work fine.
To understand a bit more about how AOP targeting works, take a look at this Spring Documentation
Spring AOP can also use CGLIB proxies. This is necessary to proxy
classes rather than interfaces. CGLIB is used by default if a business
object does not implement an interface. As it is good practice to
program to interfaces rather than classes; business classes normally
will implement one or more business interfaces. It is possible to
force the use of CGLIB, in those (hopefully rare) cases where you need
to advise a method that is not declared on an interface, or where you
need to pass a proxied object to a method as a concrete type.
So you have two options:
Take the interface when trying to get the bean from the ApplicationContext.
Enable AOP to target concrete classes instead.
To do this point #2, modify your annotation as follows:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Try:
Performance pi = context.getBean("performanceImp", Performance.class);
instead of:
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);