简体   繁体   中英

Getting a java.lang.NullPointerException with Spring Boot JdbcTemplate

General Information: New to Spring Boot and wanted to test out my JDBC connections via unit tests. I made a simple class to connect to my database and a simple test class to follow up with the proper test case.

Problem: Continuously receiving a java.lang.NullPointerException when performing jdbcTemplate.getDataSource().getConnection(); I am having a hard time understanding why. I tried using different databases, and ensured that I could make a connection with the regular JDBC. I have referred to numerous other questions on Stack Overflow but I have still been stuck on this for the past two days without any progress. I even tried to use different types of DataSource libraries but all of them yield the same result.

Question: How do I solve this? If anyone can also explain why the issue is happening, and why do we need to use the Spring JDBC on an enterprise level, that would be great.


My code:

DatabaseTableService.java

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.vertica.jdbc.DataSource;

@RestController
@RequestMapping("/databaseServices")
public class DatabaseTableService {

    private JdbcTemplate jdbcTemplate;
    private DataSource dataSource;


    public void setDataSource(DataSource dataSource) {
        this.dataSource= dataSource;
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }


    @RequestMapping("/testConnection")
    @ResponseBody
    public boolean canConnectToDB() {
        boolean result;
        try {
            jdbcTemplate.getDataSource().getConnection();
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
            result = false;
        }
        return result;
    }

}


beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.company.project"></context:component-scan>

    <mvc:annotation-driven/>

    <bean id="dataSource"
          class="com.vertica.jdbc.DataSource">
        <property name="URL" value="DBURLHERE"/>
        <property name="userID" value="USERIDHERE"/>
        <property name="password" value="PASSWORDHERE"/>
    </bean>






    <bean id="databaseTableService"
          class="com.company.project.services.DatabaseTableService">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

</beans>

DatabaseTableServiceTest.java

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.vertica.jdbc.DataSource;


@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ApplicationController.class)
@WebAppConfiguration
public class DatabaseTableServiceTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private JdbcTemplate jdbcTemplate;
    private MockMvc mockMvc;
    DatabaseTableService databaseTableServiceObject;
    DataSource testDataSource = new DataSource();

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .build();

        databaseTableServiceObject = new DatabaseTableService();
    }




    @Test
    public void setDataSource() throws Exception {
        databaseTableServiceObject.setDataSource(testDataSource);
    }

    @Test
    public void validateCanConnectToDB() throws Exception {
        Assert.assertTrue(databaseTableServiceObject.canConnectToDB());
    }


    @After
    public void tearDown() throws Exception {
        mockMvc = null;
        databaseTableServiceObject = null;
        testDataSource = null;
    }

}

ApplicationController.java

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;


@SpringBootApplication
@ImportResource({"beans.xml"})
@ComponentScan(basePackages = "com.company.project")
public class ApplicationController {

    public static void main(String[] args) throws Exception {

        SpringApplication.run(ApplicationController.class, args);
    }
}

Folder Structure

文件夹结构


Spring Application Context Information Spring 应用上下文信息

You are using Spring Boot and next try very hard not to. Remove your beans.xml and the @ImportResource from your ApplicationController (which isn't a controller but your actual application btw, as your service is your actual controller). Also assuming that your ApplicationController is in the same package you can remove the @ComponentScan as well.

Then in your application.properties add the following

spring.datasource.url=<your-url>
spring.datasource.username=<your-username>
spring.datasource.password=<your-password>
spring.datasource.type=com.vertica.jdbc.DataSource

spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

Spring Boot will create a DataSource and a JdbcTemplate for you, so no need to create that yourself. Just @Autowire the JdbcTemplate in your class.

@RestController
@RequestMapping("/databaseServices")
public class DatabaseTableService {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public DatabaseTableService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate=jdbcTeplate;
    }

    @RequestMapping("/testConnection")
    @ResponseBody
    public boolean canConnectToDB() {
        boolean result;
        try {
            jdbcTemplate.getDataSource().getConnection();
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
            result = false;
        }
        return result;
    }
}

Now your test, it is quite a mess. You are using Spring but are doing things yourself, so not sure what you are trying to achieve there.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ApplicationController.class)
@WebAppConfiguration
public class DatabaseTableServiceTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;
    private DatabaseTableService databaseTableServiceObject;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .build();
    }

    @Test
    public void setDataSource() throws Exception {
        databaseTableServiceObject.setDataSource(testDataSource);
    }

    @Test
    public void validateCanConnectToDB() throws Exception {
        Assert.assertTrue(databaseTableServiceObject.canConnectToDB());
    }

}

You can just @Autowired the service which is now fully constructed.

Final thing is that your canConnectToDB method is flawed. You are obtaining a connection but are never returning/closing it. So after calling this method for a couple of times your application will stop work as the connection pool will be depleted or your database stops accepting connections.

In short work with the framework instead of against/around the framework. Read the documentation instead of trying to hack your way around it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM