I am a beginner to Spring Boot/MVC. I have been trying to build a very basic example of querying a table containing master data for doctors.However I am repeatedly getting "java.lang.NullPointerException".Most probably because jdbcTemplate object is not getting initialized.
Some of the other users also faced this issue however in their case the problem was resolved after either including starter-jdbc jar or after removing usage of new operator to create jdbctemplate object.I have already factored these suggestions in my code. Any help on the matter would be appreciated.My code snippets are as following:
1. application.properties :
server.port=8181
spring.datasource.url = jdbc:mysql://localhost:3306/my_sample_schema
spring.datasource.username = qwerty
spring.datasource.password = qwerty
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.driverClassName=com.mysql.jdbc.Driver
debug=true
2. Test123Application.java
package com.example;
....all imports...
#SpringBootApplication
public class Test123Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Test123Application.class, args);
}
}
3.Testcontroller.java
package com.example.controller;
....all imports...
#Controller
public class TestController {
#RequestMapping(value = "/")
public String demofunction(){
return "dummytemplate";
}
#RequestMapping("/default")
public String demofunction2(Model model){
Docrepo docrepo = new Docrepoimpl();
List<Docmaster> listContact = docrepo.list();
model.addAttribute("listContact", listContact);
return "dummytemplate2";
}
}
4. Docrepoimpl.java
package com.example.repository;
----all imports---
#Configuration
#Repository
public class Docrepoimpl implements Docrepo{
#Autowired
private JdbcTemplate jdbcTemplate;
public void adddoctor(Docmaster doc){
String sql = "INSERT INTO docmastertable (docid,name,yoe,speciality,degree,college,hospital,regno)"
+ " VALUES (?,?,?,?,?,?,?,?)";
jdbcTemplate.update(sql, doc.getdocid(), doc.getname(),doc.getyoe(), doc.getspeciality(),doc.getdegree(),doc.getcollege(),doc.gethospital(),doc.getregno());
}
public List <Docmaster> list(){
String sql = "SELECT * FROM docmastertable";
if(jdbcTemplate != null)
System.out.println("jdbc seems ok...");
else
System.out.println("jdbc is null...");
List<Docmaster> listContact = jdbcTemplate.query(sql, new RowMapper<Docmaster>() {
#Override
public Docmaster mapRow(ResultSet rs, int rowNum) throws SQLException {
Docmaster doc = new Docmaster();
doc.setdocid(rs.getString("docid"));
doc.setname(rs.getString("name"));
doc.setyoe(rs.getInt("yoe"));
doc.setspeciality(rs.getString("speciality"));
doc.setdegree(rs.getString("degree"));
doc.setcollege(rs.getString("college"));
doc.sethospital(rs.getString("hospital"));
doc.setregno(rs.getString("regno"));
return doc;
}
});
return listContact;
}
}
Error Dump:
java.lang.NullPointerException: null
at com.example.repository.Docrepoimpl.list(Docrepoimpl.java:52) ~[classes/:na]
at com.example.controller.TestController.demofunction2(TestController.java:43) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_40]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_40]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_40]
...
Actually you are creating the repo directly in your controller class with a new statement. I'd recommend to also inject your Repo into your controller class by creating a local member and annotate it with #Autowired. If you don't want to follow this way you can also ask the Context to return a ready to use bean.
It always happens when you try to access Spring class that are not managed by the Spring Boot framework AKA the Spring boot container.
With the following line you can get any spring boot class for which the #Autowired does not work.
Here is a class that i used to access JdbcTemplate without #Autowired.
public class MysqlServiceJava {
// You don't need #Autowired
JdbcTemplate jdbcTemplate;
public MysqlServiceJava() {
// Get JdbcTemplate through Context container AKA !! THE MAGIC !!
jdbcTemplate = ApplicationContextHolder.getContext().getBean(JdbcTemplate.class);
}
public List<Integer> findAllEasyId(){
List<Integer> Ids = new ArrayList<>();
String sql = "SELECT id FROM master_table";
jdbcTemplate.query(
sql, new Object[] {},
(rs, rowNum) -> new MasterTable(rs.getInt("id"))
).forEach(
masterTable ->
Ids.add(masterTable.getId()) // add Id to Ids List array
);
return Ids;
}
}
class MasterTable {
private Integer id;
public MasterTable(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
#Override
public String toString() {
return "MasterTable{" +
"id=" + id +
'}';
}
public void setId(Integer id) {
this.id = id;
}
}
Thumbs up if you like =)
Related
Trying to configure spring boot application with spring-session and redis but having below issue. Not able to resolve it.
Constructor threw exception; nested exception is java.lang.IllegalStateException: BeanFactory has not been injected into #Configuration class
This code works fine for me
#Configuration
#EnableRedisHttpSession
public class HttpSessionConfig {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(SLAVE_PREFERRED)
.build();
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("192.168.56.50", 26379)
.sentinel("192.168.56.50", 26380)
.sentinel("192.168.56.50", 26381);
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
}
but not this code using PropertySource.
Spring document says:-
**RedisSentinelConfiguration can also be defined with a PropertySource, which lets you set the following properties:
Configuration Properties
spring.redis.sentinel.master: name of the master node.
spring.redis.sentinel.nodes: Comma delimited list of host:port pairs.**
#Configuration
#EnableRedisHttpSession
#PropertySource(name="application", value="classpath:application.properties")
public class HttpSessionConfig {
#Resource
ConfigurableEnvironment environment;
#Bean
public PropertiesPropertySource propertySource() {
return (PropertiesPropertySource) environment.getPropertySources().get("defaultProperties");
}
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(SLAVE_PREFERRED)
.build();
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration(propertySource());
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
}
application.properties
server.port=8090
spring.security.user.name=admin
spring.security.user.password=admin
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.56.50:26379,192.168.56.50:26380,192.168.56.50:26381
spring.application.name=spring-session-demo
The format of sentinel nodes property is comma separated key:value pairs. So you can extract host and port by java split() function.
#Autowired
private Environment env;
#Bean
public LettuceConnectionFactory connectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
String master = env.getProperty("spring.redis.sentinel.master");
String nodes = env.getProperty("spring.redis.sentinel.nodes");
sentinelConfig.master(master);
for (String node : nodes.split(",")) {
String split[] = node.split(":");
sentinelConfig.sentinel(split[0], Integer.parseInt(split[1]));
}
...
}
I have a Asp.Core project with dependency injection. The problem is that when I get an instance of a CustomerService my session is null. But I pass it through with dependency injection.
My Controller looks like this:
private ISessionService _sessionService;
private IContainer _container;
public AuthController(ISessionService sessionService, IContainer container) {
_container = container;
_sessionService = sessionService;
// here my session is NOT NULL
string userName = _sessionService.Username;
}
public IActionResult Index() {
// here I have some code so the line below is not always needeed and therefore not injected in the constructor
IUserService userService = _container.GetInstance<IUserService>();
// here my session is NULL
string name = userService.GetUserName();
}
public class UserService : IUserService {
private ISessionService _sessionService;
private IHttpContextAccessor _httpContextAccessor;
public UserService(ISessionService sessionSerivce, IHttpContextAccessor httpContextAccessor) {
_sessionService = sessionSerivce;
_httpContextAccessor = httpContextAccessor;
}
public string GetUserName() {
return _sessionService.User.Name;
}
}
my StartUp.cs where I'm using StructureMap
Container container = new Container(expr => {
expr.For<IHttpContextAccessor>().Use<HttpContextAccessor>();
expr.For<ISessionService>().Use<SessionService>();
expr.For<IUserService>().Use<UserService>();
});
So why is my session null when I instantiate a UserService object when using the container class ?
UPDATE
I know now that the Session object is null because the life cycle of a MVC page. In my BaseController class I created a method named InitContainer like this:
public void InitContainer(Type typeOfInterface, Type typeOfClass) {
Configure(expr =>
For(typeOfInterface).Use(typeOfClass)
.Ctor<ISessionService>().Is(_sessionService)
.Ctor<IHttpContextAccessor>().Is(_httpContextAccessor)
);
}
}
When I want to use an instance of a service than I do this in my Controller method:
public IActionResult Test() {
InitContainer(typeof(ICustomerService), typeof(CustomerService));
ICustomerService customerService = _container.GetInstance<ICustomerService>();
}
I don't know if it's ugly or the right way to do this, but it works for me now.
Has someone a better way to do this?
I have a spring mvc 3.0 web application and am trying to implement a generic solution for my initBinder methods. Rather than speciyfying the same initBinder code in all my contorllers for my date picker I am trying to do this (I do not have #Controller advice to help me in this version of spring)
#Component
public class MyPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
WebBindingInitializer wbi = ((RequestMappingHandlerAdapter) bean).getWebBindingInitializer();
if (wbi == null) {
wbi = new ConfigurableWebBindingInitializer();
((RequestMappingHandlerAdapter) bean).setWebBindingInitializer(wbi);
}
if (wbi instanceof ConfigurableWebBindingInitializer) {
((ConfigurableWebBindingInitializer) wbi).setPropertyEditorRegistrar(new MyPropertyEditorRegistrar());
}
}
return bean;
}
public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {
#Value("#{appProperties['date.format']}")
public String dateFormat = null;
/* (non-Javadoc)
* #see org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)
*/
public void registerCustomEditors(PropertyEditorRegistry registry) {
SimpleDateFormat df = new SimpleDateFormat(dateFormat);
df.setLenient(false);
registry.registerCustomEditor(Date.class, new CustomDateEditor(df, true));
}
}
But my BeanPostProcessor doesn't seem to be getting called. Can someone help me understand how and when the BeanPostProcessor gets called?
p.s. I am using mvc:annotationdriven
thanks
I have the following class:
public static class ARestRequestParam
{
String name;
LocalDate date; // joda type
}
And I want it to be deserialized from the following JSON which is processed by jackson.
{ name:"abc", date:"20131217" }
Actually, I want to deserialize any LocalDate field in any class with "yyyyMMdd" format, without duplicating the format string, without adding any setter method, without any XML configuration. (That is, annotation and Java code is preferable)
How can it be done?
Also, I also want to know the serialization part. that is, LocalDate -> "yyyyMMdd".
I've seen followings:
jackson-datatype-joda (https://github.com/FasterXML/jackson-datatype-joda)
custom serializer (public class JodaDateTimeJsonSerializer extends JsonSerializer { ... } - Spring #ResponseBody Jackson JsonSerializer with JodaTime)
#JsonCreator
#DateTimeFormat
But I don't know which is applicable, and which is most up-to-date.
BTW, I use Spring Boot.
UPDATE
Ok, I have managed to write working code for the deserialization part.
It is as follows:
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters)
{
converters.add(jacksonConverter());
}
#Bean
public MappingJackson2HttpMessageConverter jacksonConverter()
{
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ApiJodaModule());
converter.setObjectMapper(mapper);
return converter;
}
#SuppressWarnings("serial")
private class ApiJodaModule extends SimpleModule
{
public ApiJodaModule()
{
addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
}
}
#SuppressWarnings("serial")
private static class ApiLocalDateDeserializer
extends StdScalarDeserializer<LocalDate>
{
private static DateTimeFormatter formatter =
DateTimeFormat.forPattern("yyyyMMdd");
public ApiLocalDateDeserializer() { super(LocalDate.class); }
#Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
{
String s = jp.getText().trim();
if (s.length() == 0)
return null;
return LocalDate.parse(s, formatter);
}
throw ctxt.wrongTokenException(jp, JsonToken.NOT_AVAILABLE,
"expected JSON Array, String or Number");
}
}
}
I had to implement the deserializer myself, since the datetime format for the deserializer in jackson-datatype-joda cannot be altered. So, since I've implemented the deserializer myself, jackson-datatype-joda is not needed. (although I've copied pieces of its code)
Is this code Ok?
Is this up-to-date solution?
Is there any other easier way?
Any suggestion would be greatly appreciated.
UPDATE
Following Dave Syer's suggestion, I modified the source above as follows:
Removed 2 methods: configureMessageConverters(), jacksonConverter()
Added following method into WebMvcConfiguration class:
#Bean
public Module apiJodaModule()
{
return new ApiJodaModule();
}
But now it does not work. It seems apiJodaModule() is ignored.
How can I make it work?
(It seems that I should not have a class that has #EnableWebMvc to use that feature.)
The version I use is org.springframework.boot:spring-boot-starter-web:0.5.0.M6.
UPDATE
Final working version is as follows: (with other configurations I've done previously in the class that had #EnableWebMvc)
As Dave Syer mentioned, this will only work on BUILD-SNAPSHOT version, at least for now.
#Configuration
public class WebMvcConfiguration
{
#Bean
public WebMvcConfigurerAdapter apiWebMvcConfiguration()
{
return new ApiWebMvcConfiguration();
}
#Bean
public UserInterceptor userInterceptor()
{
return new UserInterceptor();
}
public class ApiWebMvcConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(userInterceptor())
.addPathPatterns("/api/user/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/**")
.addResourceLocations("/")
.setCachePeriod(0);
}
}
#Bean
public Module apiJodaModule()
{
return new ApiJodaModule();
}
#SuppressWarnings("serial")
private static class ApiJodaModule extends SimpleModule
{
public ApiJodaModule()
{
addDeserializer(LocalDate.class, new ApiLocalDateDeserializer());
}
private static final class ApiLocalDateDeserializer
extends StdScalarDeserializer<LocalDate>
{
public ApiLocalDateDeserializer() { super(LocalDate.class); }
#Override
public LocalDate deserialize(JsonParser jp,
DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
if (jp.getCurrentToken() == JsonToken.VALUE_STRING)
{
String s = jp.getText().trim();
if (s.length() == 0)
return null;
return LocalDate.parse(s, localDateFormatter);
}
throw ctxt.mappingException(LocalDate.class);
}
}
private static DateTimeFormatter localDateFormatter =
DateTimeFormat.forPattern("yyyyMMdd");
}
}
Your code is OK, but if you use #EnableWebMvc in a Spring Boot app you switch off the default settings in the framework, so maybe you should avoid that. Also, you now have only one HttpMessageConverter in your MVC handler adapter. If you use a snapshot of Spring Boot you ought to be able to simply define a #Bean of type Module and everything else would be automatic, so I would recommend doing it that way.
I have these classes
public class Application {
public String name;
public String ico;
public List<MenuStruct> menu =new ArrayList<MenuStruct>();
//Constructor
public Application() { }
}
public class MenuStruct {
public String id;
public String type;
public String parent;
public String name;
public String secId;
//Constructor
public MenuStruct() {}
}
If I try to deserialize a collection directly in this way:
ApplicationManager apm= new ApplicationManager();
s="[ {\"name\":\"reg_salida\" , \"ico\":\"document-open-2-32x32.ico\" }]";
apm.apps=(new Gson()).fromJson(s,apm.apps.getClass() );
for (Application ap:apm.apps){
System.out.println(ap.name); //gets error here
}
I get a java.lang.ClassCastException.
But if I try to deserialize its containig class ApplicationManager it does not fail.
s="{ \"apps\": [ {\"name\":\"reg_salida\" , \"ico\":\"document-open-2-32x32.ico\" }]}";
ApplicationManager apm=(new Gson()).fromJson(s,ApplicationManager.class);
for (Application ap:apm.apps){
System.out.println(ap.name); // now no errors here! and shows reg_salida
}
Is this a bug of gson 2.2.4? or maybe I am doing something not correct?
Eduard.
You have to provide full definition of property class. Your example should looks like that:
manager.apps = gson.fromJson(json, new TypeToken<List<Application>>() {}.getType());