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
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.