This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 2 years ago.
I am trying to use the jdbcTemplate to query a MySQL database to get the SYSDATE. So the query is as simple as SELECT SYSDATE()
However, I get the following error:
Exception in thread "main" java.lang.NullPointerException
at com.trade.xml.modifier.Test.<init>(Test.java:18)
at com.trade.xml.modifier.TradeModifierApplication.main(TradeModifierApplication.java:17)
Here is a bit of what I do:
I created a DatabaseConfiguration.class which should take all the connection details from the application.properties
The application.properties looks like this:
spring.datasource.url=jdbc:mysql://localhost:3306/xmlconverter
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
The DatabaseConfiguration.class looks like this
#Configuration
public class DatabaseConfiguration {
#Value("${spring.datasource.driver-class-name}")
private String driverName;
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.username}")
private String userName;
#Value("${spring.datasource.password}")
private String password;
#Bean(name = "dataSource")
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverName);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public JdbcTemplate dbjdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
2.I also created a Test.class where I wrote my method for querying
#Service
public class Test {
//Since a bean is created in DatabaseConfiguration,
//"template = new JdbcTemplate()" is omitted (should be recognized automatically, right?)
#Autowired
private JdbcTemplate template;
public String getDate() {
String sql = "SELECT SYSDATE()";
return template.queryForObject(sql, String.class);
}
}
The main class looks as follows
#SpringBootApplication
public class TradeModifierApplication {
public static void main(String[] args) {
SpringApplication.run(TradeModifierApplication.class, args);
Test test = new Test();
System.out.println(test.getDate());
At this point I have read the whole Internet and I am not sure what is the root cause of this NPE. Any help/hints will be appreciated.
The problem should be this line in TradeModifierApplication:
Test test = new Test();
You should let the spring container to instance Test class.
One way would be to inject by:
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(TradeModifierApplication.class, args);
Test service = applicationContext.getBean(Test.class);
service.getDate());
}
}
Related
So, I need to make an initialization of DataSource by condition. I take all the db-config data from the .env file, where there is a special variable spring_profiles_active. When this variable is equal to "p2" I need to initialize secondDataSource, otherwise not. How do I do this?
My configuration:
#Configuration
public class JpaConfig {
#Value("${jdbc.url}")
private String jdbcUrl;
#Value("${jdbc.username}")
private String jdbcUsername;
#Value("${jdbc.password}")
private String jdbcPassword;
#Value("${jdbc.driverClassName}")
private String jdbcDriverClassName;
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Primary
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(jdbcDriverClassName);
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(jdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(jdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); }
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
update:
I try to do this with #Condition annotation, but get an exception java.lang.NullPointerException: null in return activeProfile.equals("p2"). code:
#Configuration
#Conditional(SecondJpaConfigCondition.class)
public class SecondJpaConfig {
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
#Component
public class SecondJpaConfigCondition implements Condition {
#Value("${spring.profiles.active}")
private String activeProfile;
#Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return activeProfile.equals("p2");
}
}
I recommend you to use the #ConditionalOnProperty annotation. It can be used on the class or a method with the #Bean annotation. I would separate the configuration of the two databases in two different classes (it's not mandatory, you could us the annotion over the secondDataSource and secondJdbcTemplate methods) and do something like this:
JpaConfig (First Datasource):
#Configuration
public class JpaConfig {
#Value("${jdbc.url}")
private String jdbcUrl;
#Value("${jdbc.username}")
private String jdbcUsername;
#Value("${jdbc.password}")
private String jdbcPassword;
#Value("${jdbc.driverClassName}")
private String jdbcDriverClassName;
#Bean
#Primary
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(jdbcDriverClassName);
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(jdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(jdbcPassword));
return dataSource;
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
JpaConditionalConfig (Second Datasource):
#Configuration
#ConditionalOnProperty(prefix = "cond", name = "spring_profiles_active", havingValue = "p2")
public class JpaOptionalConfig {
#Value("${second.jdbc.url}")
private String secondJdbcUrl;
#Value("${second.jdbc.username}")
private String secondJdbcUsername;
#Value("${second.jdbc.password}")
private String secondJdbcPassword;
#Value("${second.jdbc.driverClassName}")
private String secondJdbcDriverClassName;
#Bean
#Qualifier("secondDataSource")
public DataSource secondDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(secondJdbcDriverClassName);
dataSource.setUrl(secondJdbcUrl);
dataSource.setUsername(secondJdbcUsername);
dataSource.setPassword(EncryptionUtil.decryptProperty(secondJdbcPassword));
return dataSource;
}
#Bean
#Qualifier("secondJdbcTemplate")
public JdbcTemplate secondJdbcTemplate(#Qualifier("secondDataSource") DataSource secondDataSource) {
return new JdbcTemplate(secondDataSource);
}
}
Properties file where is defined the spring_profiles_active property, which will be read by Spring to know if the conditional beans should be created:
jdbc.url=jdbc:h2:mem:mydb
jdbc.username=sa
jdbc.password=password
jdbc.driverClassName=org.h2.Driver
second.jdbc.url=jdbc:h2:mem:mydb
second.jdbc.username=sa
second.jdbc.password=password
second.jdbc.driverClassName=org.h2.Driver
#THIS IS THE PROPERTY USED IN THE CONDITIONAL BEANS
conditional.spring_profiles_active=p2
I'm running this code to set my config and get a DataSource to access a database but when i call ds.getDataSource() i get a null value. What am i missing there?
private static HikariConfig config = new HikariConfig();
private static HikariDataSource ds;
static {
config.setJdbcUrl("jdbc:mysql://localhost:5656/...");
config.setUsername("");
config.setPassword("");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.setMaximumPoolSize(10);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
ds = new HikariDataSource(config);
}
public static DataSource getDataSource() {
return ds.getDataSource();
}
Ok so i wasn't familiar with this aspect of SpringBoot, for anyone having the same problem here is what i did to make the #Autowired work:
I need to use the DataSource in my a QuickSearchService class which i defined as a #Service. In that service i can call the HikariCP DataSource with #Autowired:
#Service
public class QuickSearchService{
#Autowired
private DataSource ds;
...
}
Then when i needed to use the service i #Autowired it aswell on call:
public class UsingService {
#Autowired
private QuickSearchService qss;
...
}
Finaly i had to use #ComponentScan on the application main class to tell the server to find the components that need to be autowired:
#ComponentScan(basePackages = {"example.services", ...})
public class DemoApplication {
public static void main(String[] args){
...
}
}
Then i can configure the DataSource in the application.properties.
A bean definition of type org.springframework.jdbc.core.JdbcTemplate was not found, therefore #Autowired private JdbcTemplate jdbcTemplate; doesn't actually have a value inside it.
My Application.java looks as following:
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Value("${spring.name}")
private String name;
#Autowired
private JdbcTemplate jdbcTemplate;
private static final Logger log = LoggerFactory.getLogger(Application.class);
// #Bean
// public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
// return args -> {
// System.out.printf("The application is running %s!", name);
// };
// }
public void run(String... strings) throws Exception {
log.info("Creating tables");
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
.map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));
// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
(rs, rowNum) -> new CustomerModel(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(customer -> log.info(customer.toString()));
}
I understand Dependency Injection and IoC which should technically instantiate a JdbcTemplate instance by itself, but if I do it manually it I have the following code which gives the error that JdbcTemplate bean requires dataSource property (which I'm giving as below):
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUsername;
#Value("${spring.datasource.password}")
private String dbPassword;
private DataSource dataSource = new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
private JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
These lines are not producing Spring beans, so they are not candidates for autowiring:
private DataSource dataSource = new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
private JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
If you're using Spring Boot, you can follow these instructions to configure your datasource, though make sure you use the spring-boot-starter-jdbc dependency in your pom.
If you're configuring these manually, you need to create a #Configuration class, that exposes both a DataSource and JdbcTemplate bean. For example, something like:
#Configuration
public class DatabaseConfiguration {
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUsername;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
public DataSource dataSource() {
return new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
}
Spring boot application properties needs to follow convention from https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html when we use any DB like cassandra/mongo. In case if we want to declare our own properties for DB setup instead of spring-boot convention, what are all the steps we need to do for setting up DB?
You can do this: Spring boot - custom variables in Application.properties
or you can just create your own property in your application.properties file like:
my.property.someDb.hostname=http://wherever.comand then reference to it in your code like:
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
Update 1:
If you want to create the MongoDb with your own properties you have to define the right Java Beans in an #Configuration file. For MongoDB it could look like the following:
#Configuration
public class MyMongoConfig extends AbstractMongoConfiguration{
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
#Value("${my.property.someDb.myOwnPortDefinition}")
private int myOwnPortDefinition;
#Value("${my.property.someDb.myDatabasename}")
private String myDatabasename;
#Override
protected String getDatabaseName() {
return myDatabasename;
}
#Override
#Bean
public Mongo mongo() throws Exception{
return new MongoClient(someDbHostname, myOwnPortDefinition );
}
#Bean
public MongoTemplate mongoTemplate() throws Exception{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
These are the essential steps you need in order to get a data source like Jdbc, mongodb set up in Spring Boot
Need a #Configuration class that has transaction management enabled
on it
Read the environment properties for the datasource i.e. dataSource
url, username, password etc.
Create beans for datasource, session factory, transaction manager
etc.
Once all of the above setup, use this #Configuration in your
consumer to initialize the spring application context
Here are some snippets of wiring mongodb datasource in spring boot
DataSourceConfiguration.java
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.example.xyz"})
public class DatabaseEntityConfiguration {
public static final String DATABASE_ENTITY_DATA_SOURCE = "databaseDataSource";
public static final String DATABASE_HIBERNATE_PROPERTIES = "databaseHibernateProperties";
public static final String DATABASE_ENTITY_SESSION_FACTORY = "databaseSessionFactory";
public static final String DATABASE_ENTITY_TRANSACTION_MANAGER = "databaseTransactionManager";
public static final String DATABASE_ENTITY_DB_CONFIG_DAO = "dmdatabaseDbConfigDao";
public static final String DATABASE_ENTITY_DB_CONFIG_SERVICE = "dmdatabaseDbConfigService";
private static final String ENTITY_PACKAGE = "com.example.xyz.database.entity";
#Autowired
private org.springframework.core.env.Environment environment;
#Bean(name = DATABASE_ENTITY_DATA_SOURCE)
public DataSource databaseEntitydataSource() throws PropertyVetoException {
// mongodb properties
String driverClass = environment.getProperty("databaseEntity.mongodb.driverClassName");
String mongodbUrl = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.url");
String user = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.username");
String password = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.password");
Preconditions.checkArgument(StringUtils.isNotBlank(driverClass), "The property mongodb driverClass must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(mongodbUrl), "The property mongodb mongodbUrl must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(user), "The property mongodb user must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(password), "The property mongodb password must not be null or blank");
dataSource.setDriverClass(driverClass);
dataSource.setmongodbUrl(mongodbUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = DATABASE_ENTITY_SESSION_FACTORY)
public AnnotationSessionFactoryBean databaseEntitySessionFactory() throws PropertyVetoException {
AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean();
annotationSessionFactoryBean.setDataSource(databaseEntitydataSource());
annotationSessionFactoryBean.setPackagesToScan(ENTITY_PACKAGE);
annotationSessionFactoryBean.setAnnotatedClasses(DBConfig.class);
annotationSessionFactoryBean.setHibernateProperties(databaseEntityHibernateProperties());
return annotationSessionFactoryBean;
}
#Bean(name = DATABASE_ENTITY_TRANSACTION_MANAGER)
public HibernateTransactionManager databaseEntityTransactionManager() throws PropertyVetoException {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(databaseEntitySessionFactory().getObject());
return transactionManager;
}
}
I have been trying to implement a web service using spring. This webservice will provide data access to a mySQL database using JDBC. I am trying to not use any xml configuration files, so I have come across a problem trying to connect to the database.
I am following the tutorial: http://spring.io/guides/tutorials/rest/ but I changed a few things along the way.
Now that I am trying to implement the connection with the database I get an error when trying to execute the tomcat instance, and I guess the problem is within the configurations.
Here follows some of my code:
Datasource configuration:
#Configuration
#Profile("mySQL")
#PropertySource("classpath:/services.properties")
public class MySQLDataSourceConfiguration implements DataSourceConfiguration{
#Inject
private Environment environment;
#Bean
public DataSource dataSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setPassword(environment.getProperty("dataSource.password"));
dataSource.setUrl(environment.getProperty("dataSource.url"));
dataSource.setUsername(environment.getProperty("dataSource.user"));
dataSource.setDriverClassName(environment.getPropertyAsClass("dataSource.driverClass", Driver.class).getName());
return dataSource;
}
}
the file service.properties is where I keep my configurations for the database, so when I desire to change the database I will just have to change 4 fields.
The JDBCConfiguration class for the setup of the JDBCtemplate
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSourceConfiguration.dataSource());
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
Then there is the Repository, that recieves the template.
#Transactional
#Repository
#Qualifier("jdbcRepository")
public class JdbcIndividualRepository implements IndividualsRepository{
private static final Logger LOG = LoggerFactory.getLogger(JdbcIndividualRepository.class);
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
public JdbcIndividualRepository(DataSource jdbcDataSource) {
LOG.info("JDBCRepo arg constructor");
this.jdbcTemplate = new JdbcTemplate(jdbcDataSource);
}
#Override
public Individual save(Individual save) {
String sql = "INSERT INTO Individual(idIndividual, Name) VALUES(?,?)";
this.jdbcTemplate.update(sql, save.getId(), save.getName());
return save;
}
#Override
public void delete(String key) {
String sql = "DELETE FROM Individual WHERE idIndividual=?";
jdbcTemplate.update(sql, key);
}
#Override
public Individual findById(String key) {
String sql = "SELECT i.* FROM Individual i WHERE i.idIndividual=?";
return this.jdbcTemplate.queryForObject(sql, new IndividualRowMapper(), key);
}
#Override
public List<Individual> findAll() {
String sql = "SELECT * FROM Individual";
return new LinkedList<Individual>(this.jdbcTemplate.query(sql, new IndividualRowMapper()));
}
}
Then I register the jdbc configuration in the initializer class when creating the root context of the application as follows:
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(CoreConfig.class, SecurityConfig.class, JdbcConfiguration.class);
rootContext.refresh();
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
However, the Tomcat server wont run because it can't autowire the class MySQLDataSourceConfiguration.
Anyone knows what the problem might be? I can give more details on the code, but the question is already really large.
Appreciate any kind of help!
Cheers
EDIT
Solved changing the JdbcConfiguration class to:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public IndividualsRepository createRepo(){
return new JdbcIndividualRepository(dataSource);
}
}
Remove
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
Because that's not how it's supposed to be used. Instead add to the same class the following:
#Autowired DataSource dataSource;
and use it like this: new JdbcTemplate(dataSource);
Also, try adding #ComponentScan to JdbcConfiguration class. From what I see in your code the class JdbcIndividualRepository is not picked up by anything.
In your class JdbcConfiguration, you are trying to autowire DataSourceConfiguration. I'm not really sure if that's possible - typically you should try to autwire the DataSource, not the DataSourceConfiguration.
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
Also if you have several DataSources and you're using Spring profiles to separate them, it's easier to provide all the DataSource beans in one file and annotate each bean with a different profile:
#Configuration
public class DataSourceConfig {
#Bean
#Profile("Test")
public DataSource devDataSource() {
.... configure data source
}
#Bean
#Profile("Prod")
public DataSource prodDataSource() {
... configure data source
}