Spring Boot Data console application - java

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

Related

Field <repo_interface> in com.<package_name>.<service_class required a bean of type com.<package>.<repository>that could not be found

I'm new in spring-boot, and have spring-boot desktop test project with weird names, based on another example. The error given is:
`***************************
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in com.test_4.test4.NonWebService required a bean of type 'com.test_4.test4.RandomTableREPO' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.test_4.test4.RandomTableREPO' in your configuration.`
And here are my classes:
**Test4Application**
package com.test_4.test4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Test4Application implements ExecuteI {
#Autowired
private NonWebService service;
public static void main(String[] args) {
SpringApplication.run(Test4Application.class, new String[]{"one_arg", "sec_arg"});
}
#Override
public void run(String[] args) throws Exception {
service.save(new String[] {"dummy_str", "dummy_str"});
}
}
**ExecuteI**
package com.test_4.test4;
import org.springframework.boot.CommandLineRunner;
public interface ExecuteI extends CommandLineRunner {
void run(String... args) throws Exception;
}
**NonWebService**
package com.test_4.test4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class NonWebService {
private final RandomTableREPO randomTableREPO;
#Autowired
public NonWebService(RandomTableREPO randomTableREPO) {
this.randomTableREPO = randomTableREPO;
}
public void save(String... dummyArgs) {
this.randomTableREPO.save(new RandomTableEntity());
System.out.println("\n\n+++++++++++++++++++++++++++++++++++++");
}
}
**RandomTableREPO**
package com.test_4.test4;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface RandomTableREPO extends JpaRepository<RandomTableEntity, Integer> {
}
**application.properties**
spring.main.web-application-type=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/test3
spring.datasource.username=postgres
spring.datasource.password=
spring.jpa.show-sql=true
have also RandomTableEntity.class, but i think for the described problem is not neccesary.
I accessed several links, but I still didn't find an answer to my error.
Until i've created and tried to use RandomTableREPO, i reached the save() method in NonWebService.class, but couldn't insert an object in database... any help from the enlightened minds in spring boot?
I want to have an object saved in database, using RandomTableREPO interface, which extends JpaRepository

CommandLineRunner Never Executes (Spring Boot 2.6.7)

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;
}

Spring Boot: Redis CRUD Repository findById or findAll always returns Optional.empty (null)

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

Unit testing a AWS Spring Boot Configuration file - possible?

We are currently using Spring Boot to connect to a mocked local instance of Amazon SQS. The application itself is working when run, but we would like to try and test the SQS Config class, if possible and if it makes sense.
Here is the configuration class. All properties are pulled from the typical application.properties file when the Spring application itself is run.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AWSSQSConfig {
#Value("${aws.sqs.endpoint}")
private String AWSSqsEndpoint;
// Producer QueueMessageTemplate
#Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resourceIdResolver) {
if (!AWSSqsEndpoint.isEmpty())
amazonSqs.setEndpoint(AWSSqsEndpoint);
return new QueueMessagingTemplate(amazonSqs, resourceIdResolver);
}
}
Here is the test class. We are attempting to pass the configuration in via TestPropertySource, but they don't actually seem to get to the AWSSQSConfig class. AWSSqsEndpoint inside the instance of the class is always NULL.
import com.amazonaws.services.sqs.AmazonSQSAsync;
import com.lonewolf.formsbuilder.config.AWSSQSConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.assertNotNull;
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#TestPropertySource(properties = {
"cloud.aws.region.static=us-east-1",
"cloud.aws.credentials.accessKey=zzzzz",
"cloud.aws.credentials.secretKey=zzzzzz",
"aws.sqs.endpoint = http://localhost:9324",
"aws.sqs.requestQueue = CreateSchemaRequest",
"aws.sqs.responseQueue = CreateSchemaResponse"
})
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#Test
public void contextLoads() {
AWSSQSConfig config = new AWSSQSConfig();
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Is this a chicken and the egg situation, where the spring framework actually needs to run first to inject those config values? Do we need an integration test here instead?
EDIT with working solution...
Using the accepted answer, here is my working test! I was able to remove my dependency of the Spring framework.
#RunWith(MockitoJUnitRunner.class)
public class AWSSQSConfigTests {
#Mock
private AmazonSQSAsync amazonSqs;
#Mock
private ResourceIdResolver resourceIdResolver;
#InjectMocks
private AWSSQSConfig config;
#Before
public void setup() {
ReflectionTestUtils.setField(config, "AWSSqsEndpoint", "http://fake");
}
#Test
public void contextLoads() {
QueueMessagingTemplate queueMessagingTemplate = config.queueMessagingTemplate(amazonSqs, resourceIdResolver);
assertNotNull("The response body must not be null", queueMessagingTemplate);
}
}
Have you tried injecting mock to your class (or autowire it), and then setting that field it using ReflectionTestUtils? This is a nice test utils class that Spring provides that allows you to do something like what you want without doing code modifications.
I mean something like this:
#InjectMocks
private AWSSQSConfig awssqsConfig;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(awssqsConfig, "AWSSqsEndpoint", "putYourEndpointHere");
}

writing an API that wraps Spring MongoDB repositories

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
}

Categories