I am planning to play around with some trading data, and made a request to retrieve some markets data that I wish to store in mysql.
Since yesterday I have been stuck on a NullPointerException in my code. On StackOverflow I often see the error to be of trying to instantiate the Service for example, or forgotten annotations.
For me it seems to be going wrong whenever I try to use the Autowired Service. I invoke the method (that makes the data request in the Controller) from the main method for now. This does mean I have to instantiate in order to get there. This is probably where it goes wrong. I might be missing the concept of how to deal with this properly or how to save my data to the DAO. Hope someone can steer me in the right direction.
See the code below:
#SpringBootApplication
public class DesktopAutoTradingApplication {
public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(DesktopAutoTradingApplication.class, args);
MarketsBinanceController marketsBinanceController = new MarketsBinanceController();
marketsBinanceController.saveListOfMarketsBinance();
}
#Controller
public class MarketsBinanceController{
#Autowired
RequestServices requestService;
public void saveListOfMarketsBinance() throws JsonProcessingException {
String resourceURL;
RestTemplate restTemplate = new RestTemplate();
ObjectMapper mapper = new ObjectMapper();
ResponseEntity<String> response;
MarketsBinance marketsBinance;
resourceURL = "https://api.cryptowat.ch/markets/binance";
response = restTemplate.getForEntity(resourceURL, String.class);
JsonNode root = mapper.readTree(response.getBody());
JsonNode result = root.get("result");
System.out.println(result);
List<MarketsBinance> markets = new ArrayList<>();
for(JsonNode item : result){
marketsBinance = new MarketsBinance();
MarketsBinanceDto marketsBinanceDto;
marketsBinanceDto = mapper.treeToValue(item,MarketsBinanceDto.class);
marketsBinance.setId(marketsBinanceDto.getId());
marketsBinance.setExchange(marketsBinanceDto.getExchange());
marketsBinance.setPair(marketsBinanceDto.getPair());
marketsBinance.setActive(marketsBinanceDto.getActive());
marketsBinance.setRoute(marketsBinanceDto.getRoute());
markets.add(marketsBinance);
requestService.saveMarketsBinance(markets);
}
}
}
#Service
#Transactional
public class RequestServices {
#Autowired
private MarketsBinanceDAO marketsBinanceDAO;
public void saveMarketsBinance(List<MarketsBinance> markets){
marketsBinanceDAO.saveAll(markets);
}
}
#Component
public interface MarketsBinanceDAO extends CrudRepository<MarketsBinance, Long> {
}
#Entity
public class MarketsBinance {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long tableId;
private Long id;
private String exchange;
private String pair;
private Boolean active;
private String route;
//getters and setters
public class MarketsBinanceDto {
private Long id;
private String exchange;
private String pair;
private Boolean active;
private String route;
//getters and setters
Exception in thread "main" java.lang.NullPointerException
at Controller.MarketsBinanceController.saveListOfMarketsBinance(MarketsBinanceController.java:46)
at com.DAT.DesktopAutoTrading.DesktopAutoTradingApplication.main(DesktopAutoTradingApplication.java:16)
Below you find the error message received upon running the program adding the proposed solution in the first answer.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'Controller.MarketsBinanceController' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:352)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:343)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1127)
at com.DAT.DesktopAutoTrading.DesktopAutoTradingApplication.main(DesktopAutoTradingApplication.java:15)
2020-09-07 12:24:03.545 INFO 22756 --- [ task-1] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-09-07 12:24:03.553 INFO 22756 --- [ task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
You should use bean of type MarketsBinanceController that Spring created and configured instead of MarketsBinanceController marketsBinanceController = new MarketsBinanceController();.
To get this bean in main method you should get ApplicationContext. ApplicationContext you can get as returned object from SpringApplication.run(DesktopAutoTradingApplication.class, args);.
Complete code example:
ConfigurableApplicationContext context = SpringApplication.run(DesktopAutoTradingApplication.class, args);
MarketsBinanceController marketsBinanceController = context.getBean(MarketsBinanceController.class);
marketsBinanceController.saveListOfMarketsBinance();
Related
Getting an error while running , a solr spring boot application, it says
"Parameter 0 of constructor in Controller required a bean of type Repository that could not be found."
"Consider defining a bean of type Repository in your configuration."
Here is my enitiy
#SolrDocument(collection = "content_core")
public class ContentDoc {
#Id
#Indexed
private String id;
#Indexed(name = "display_title", type = "string")
private String displayTitle;
}
Here is my solr config file
#EnableSolrRepositories(basePackages = "com.baeldung.repository.ContentDocRepository.spring.data.solr.repository", namedQueriesLocation = "classpath:solr-named-queries.properties")
#ComponentScan
public class SolrConfig {
#Bean
public SolrClient solrClient() {
return new HttpSolrClient.Builder("http://localhost:8983/solr").build();
}
#Bean
public SolrTemplate solrTemplate(SolrClient client) throws Exception {
return new SolrTemplate(client);
}
}
Here is my controller
#RequestMapping("/api/v1/content")
#AllArgsConstructor
#Slf4j
public class ContentDocController {
private final ContentDocRepository contentDocRepository;
#GetMapping("/searchfaq")
public Map<String, Object> searchFaq(#RequestParam("sectionId") Long sectionId,
#RequestParam("seachTerm") String searchTerm) {
// ContentDoc contentDoc = new ContentDoc();
// contentDoc.setContentId((long)1);
// contentDoc.setDisplayTitle("virat");
// contentDocRepository.save(contentDoc);
return null;
}
}
Here is my repository
#Qualifier("contentDocRepository")
public interface ContentDocRepository extends SolrCrudRepository<ContentDoc, String> {
public List<ContentDoc> findByName(String displayTitle);
}
this is the error that I am getting ->
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-10-20 18:22:47.836 ERROR 2760 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
*********
APPLICATION FAILED TO START
*********
Description:
Parameter 0 of constructor in com.aa.aa.controller.ContentController required a bean of type 'com.aa.aa.repository.ContentDocRepository' that could not be found.
Action:
Consider defining a bean of type 'com.aa.aa.repository.ContentDocRepository' in your configuration.
I am trying to create an an api which searches data from DB , and using solr to index the db data.
I am following this page -> https://www.baeldung.com/spring-data-solr
I have a Spring Boot Controller with POST. Method works fine. I tested it by POSTMAN and from postgresql I recieved JSON. But I need test it.
#RestController
#RequestMapping("/api")
public class FamilyController {
private final FamilyService familyService;
public FamilyController(FamilyService familyService) {
this.familyService = familyService;
}
#GetMapping("/getFamily/{familyId}")
public List<FamilyMember> getFamily(#PathVariable Integer familyId) {
return familyService.searchFamilyMember(familyId);
}
}
I created test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(FamilyController.class)
class FamilyControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private FamilyService service;
#Captor
private ArgumentCaptor<Family> argumentCaptor;
#Test
void createFamily() throws Exception {
Family family = new Family();
family.setId(1);
family.setFamilyName("Kowal");
family.setNrOfInfants(2);
family.setNrOfChildren(2);
family.setNrOfAdults(1);
Mockito.when(service.saveAndFlush(argumentCaptor.capture())).thenReturn(1);
mockMvc.perform(post("/api/createFamily")
.contentType(MediaType.APPLICATION_JSON)
.contentType(objectMapper.writeValueAsString(family)))
.andExpect(status().isCreated());
// .andExpect(header().exists("Location"))
// .andExpect(header().string("Location", "http://localhost/api/getFamily/1"));
assertThat(argumentCaptor.getValue().getFamilyName(), is("Kowal"));
}
when I run a test, I received error like below:
WARN 15404 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver :
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException:
Invalid mime type "{"id":1,"familyName":"Kowal","nrOfInfants":2,"nrOfChildren":2,"nrOfAdults":1,"familyMembers":[]};charset=UTF-8":
does not contain '/']
What am I doing wrong?
The problem is this line:
.contentType(objectMapper.writeValueAsString(family)))
Right now you are sending the object in the content-type header, that's why you get the error message.
Change it to:
.content(objectMapper.writeValueAsString(family)))
Below is the method I'm trying to write unit test using junit 5
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Below is the unit test I'm trying to run
#InjectMocks
private AWSAuthHandler testAWSAuthHandler;
#Test
public void testSetAwsRequestGETParameter() throws Exception {
URI mockedURI = Mockito.mock(URI.class);
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
Below is the stack trace:
java.lang.NullPointerException
at java.base/java.net.URI$Parser.parse(URI.java:3106)
at java.base/java.net.URI.<init>(URI.java:600)
at java.base/java.net.URI.create(URI.java:881)
Can someone please help me with the missing part? Thank you
For setting properties of class that you can't mock you can use Spring Reflection Utils, like that:
ReflectionUtils.setField(field, target, value);
where the field is the name of the field which you want to set ("endpoint" for your case),
target is the mocked class (testAWSAuthHandler for your case)
value is the wanted value
As Sweta Sharma said, you need to initialise AWSAuthHandler with some value for endpoint field. That's why it is better to use constructor injection rather than field one.
Assuming your AWSAuthHandler class look like this (as you didn't provide the code for the whole class):
public class AWSAuthHandler {
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
You can refactor it in the following way:
public class AWSAuthHandler {
private String endpoint;
public AWSAuthHandler(#Value("${proxy.host}") String endpoint) {
this.endpoint = endpoint;
}
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Then you can create tests for this class:
private AWSAuthHandler testAWSAuthHandler;
#BeforeEach
void setUpTests() {
this.testAWSAuthHandler = new AWSAuthHandler("some-endpoint-here");
}
#Test
public void testSetAwsRequestGETParameter() throws Exception {
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
You can read more about Spring #Value annotation here, for example: https://www.baeldung.com/spring-value-annotation
I'm using Spring Data with a MongoDB to save some documents. When saving documents, I would like that Mongo does not contain empty objects. (How) can this be achieved?
Say I have the following main class:
#Document(collection = "main_doc")
public class MainDoc {
#Id
private String id;
private String title;
private SubDoc subDoc;
}
that contains an object of the following class:
public class SubDoc {
private String title;
private String info;
}
Now if I would try to save the following object:
MainDoc main = new MainDoc();
main.setTitle("someTitle");
main.setSubDoc(new SubDoc());
Note: in reality I do not control the fact that the SubDoc is set like this. It can either be empty or filled in. What I want is that if an element's properties/fields are all NULL, it will not be stored in mongo at all.
This results in something like this in mongo:
{
"_id" : "5a328f9a-6118-403b-a3a0-a55ce52099f3",
"title": "someTitle",
"subDoc": {}
}
What I would like is that if an element contains only null properties, they aren't saved at all, so for the above example I would want the following result:
{
"_id" : "5a328f9a-6118-403b-a3a0-a55ce52099f3",
"title": "someTitle"
}
Saving of documents is done with the help of a repository as following:
#NoRepositoryBean
public interface MainRepo extends CrudRepository<MainDoc, String> {
// save inherited
}
Thanks in advance.
One thing you can do here is to write your custom converter for MainDoc:
public class MainDocConverter implements Converter<MainDoc, DBObject> {
#Override
public DBObject convert(final MainDoc source) {
final BasicDbObject dbObject = new BasicDBObject();
...
if(/* validate is subdoc is not null and not empty */) {
dbOject.put("subDoc", source.getSubDoc());
}
}
}
You can register it in #Configuration file for example:
#Configuration
#EnableMongoRepositories(basePackages = {"package"})
public class MongoConfig {
private final MongoDbFactory mongoDbFactory;
public MongoConfig(final MongoDbFactory mongoDbFactory) {
this.mongoDbFactory = mongoDbFactory;
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
final MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, getDefaultMongoConverter());
return mongoTemplate;
}
#Bean
public MappingMongoConverter getDefaultMongoConverter() throws Exception {
final MappingMongoConverter converter = new MappingMongoConverter(
new DefaultDbRefResolver(mongoDbFactory), new MongoMappingContext());
converter.setCustomConversions(new CustomConversions(Arrays.asList(new MainDocConverter())));
return converter;
}
}
If you don't want to write a custom converter for your object toy can use default one and and modify it a bit.
final Document document = (Document) getDefaultMongoConverter().convertToMongoType(mainDoc);
if(/* validate is null or is empty */) {
document .remove("subDoc");
}
mongoTemplate().save(document);
Actually it's not the best way. As guys wrote empty object should be stored as {}, but converter can help you with your case.
In a Spring Boot/Spring Data Rest project i have issues to use a custom JsonSerializer<Set<Object>> on a #OneToMany property. When i do an HTTP GET /collection request i have the following error:
Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: Can not override serializer (through
reference chain:
org.springframework.hateoas.Resources["_embedded"]->java.util.UnmodifiableMap["analogParameters"]->java.util.ArrayList[0]);
nested exception is
com.fasterxml.jackson.databind.JsonMappingException: Can not override
serializer (through reference chain:
org.springframework.hateoas.Resources["_embedded"]->java.util.UnmodifiableMap["analogParameters"]->java.util.ArrayList[0])
Below is an extract of my entity class:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
#JsonSerialize(using=InputParametersSerializer.class)
//#Transcient
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
And the JsonSerializer<Set<InputParameter>>
public class InputParametersSerializer
extends JsonSerializer<Set<InputParameter>> {
static final long serialVersionUID = 123L;
public void serialize (Set<InputParameter> ips, JsonGenerator jg,
SerializerProvider sp)
throws IOException {
jg.writeString("Yeah");
}
}
If i remove #OneToMany and define the property as #transient it works as expected.
InputParameter entity has no Repository associated (it is not exported as a rest resource).
How can a make use of a JsonSerializer on a #OneToMany property?
I ran into a very similar issue while using Spring Boot 2.1.0. Adding a custom serializer, both with using and keyUsing, works fine, but a custom deserializer with a #OneToMany annotated field throws out the same JsonMappingException: Can not override serializer message you got, while with an #ElementCollection it just plain gets ignored. I suspect Spring Data Rest does some undocumented magic in order to take care of the (de)serialization of these kinds of fields that does not play nice with the addition of a custom deserializer. My workaround to this was adding an extra JSON field through an annotated getter and setter. With your example, it would look like:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
#JsonSerialize(using=InputParametersSerializer.class)
public Set<InputParameter> getInputParametersSet() {
return getInputParameters();
}
#JsonDeserialize(using=InputParametersDeserializer.class)
public void setInputParametersSet(Set<InputParameter> inputParameters) {
setInputParameters(inputParameters);
}
Which will output something like
{
...
"inputParameters" : ...,
"inputParametersSet" : ...,
...
}
While not ideal, serialization and deserialization of this field works as expected.
alternatively, in order to keep the field name, a similar workaround worked with #ElementCollection but not with #OneToMany:
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="output_parameter_id")
#JsonIgnore
private Set<InputParameter> inputParameters = new HashSet<InputParameter>();
public Set<InputParameter> getInputParameters() {
return inputParameters;
}
public void setInputParameters(Set<InputParameter> inputParameters) {
this.inputParameters = inputParameters;
}
#JsonProperty("inputParameters")
#JsonSerialize(using=InputParametersSerializer.class)
public Set<InputParameter> getInputParametersSet() {
return getInputParameters();
}
#JsonProperty("inputParameters")
#JsonDeserialize(using=InputParametersDeserializer.class)
public void setInputParametersSet(Set<InputParameter> inputParameters) {
setInputParameters(inputParameters);
}
In the end I had to go with the first approach.