Dependency Injection of datasource and jdbctemplate? - java

I am very new to webapp development in general and Spring framework in particular. I am following this Spring JDBC Transactions tutorial But instead of accessing the db service from only one class, I wish to access it from multiple classes.
In the tutorial the service is defined like this
public class BookingService {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional
public void book(String... persons) {
for (String person : persons) {
System.out.println("Booking " + person + " in a seat...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
}
};
public List<String> findAllBookings() {
return jdbcTemplate.query("select FIRST_NAME from BOOKINGS", new RowMapper<String>()
#Override
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs.getString("FIRST_NAME");
}
});
}
}
These are the Beans
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration
public class Application {
#Bean
BookingService bookingService() {
return new BookingService();
}
#Bean
DataSource dataSource() {
return new SimpleDriverDataSource() {{
setDriverClass(org.h2.Driver.class);
setUsername("sa");
setUrl("jdbc:h2:mem");
setPassword("");
}};
}
#Bean
JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("Creating tables");
jdbcTemplate.execute("drop table BOOKINGS if exists");
jdbcTemplate.execute("create table BOOKINGS(" +
"ID serial, FIRST_NAME varchar(5) NOT NULL)");
return jdbcTemplate;
}
They instantiated the service in only the Application class and did all the transactions there
ApplicationContext ctx = SpringApplication.run(Application.class, args);
BookingService bookingService = ctx.getBean(BookingService.class);
//bookingService.doStuff()
In my test project, I copied the same Bean definitions but I performed the transactions in multiple classes.
public class foo {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.book(...);
// some other stuff
}
public class bar {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.findAllBookings(...);
// some other stuff
}
It seems that when I perform all transactions in one class only (for example, book and find in foo class) it performs as expected. But when I try to separate them into multiple classes it doesn't behave as expected. If I perform the book in foo, I cannot find in bar. What concept am I missing? Am I instantiating multiple instances of the datasource and jdbctemplate because I instantiated the service multiple times. But I thought Spring handles the injection? And since there is only one physical database then there would only be one instance of datasource and jdbctemplate. What concept did I misunderstood? Please help and point me to the right direction. Thanks.

You need to inject your dependencies, for example:
public class Foo {
#Autowired
private BookingService bookingService;
public doStuff() {
bookingService.book(...);
// some other stuff
}
}

Related

Test Dao layer with queries stored in properties file

I'm developing REST App for the IT courses. One of the requirements was to use Spring-JDBC with all queries to be stored in separate properties file. Here is a part of my UserDaoImpl:
#Repository
#RequiredArgsConstructor
#PropertySource(value = "classpath:/user_queries.properties")
#Log
public class UserDaoImpl implements UserDao {
private final NamedParameterJdbcTemplate template;
private final RowMapper<User> rowMapper;
#Value("${find.by_id}")
private String findById;
#Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findById, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
String.format("User with id - %d, not found.", id);
}
return Optional.ofNullable(user);
}
}
All method work fine, but troubles started when I wrote my Test to this class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {TestDBConfiguration.class})
public class UserDaoTest {
#Autowired
DataSource dataSource;
#Test
public void findByIdTest() {
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource);
UserRowMapper userMapper = new UserRowMapper();
UserDao userDao = new UserDaoImpl(template, userMapper);
User user = new User(1, "John", "Doe",
"$2a$04$MzVXtd4o0y4DOlyHMMLMDeE4/eezrsT5Xad.2lmGr/NkCpwBgvn3e",
"jdoe#gmail.com", true);
assertEquals(Optional.of(user), userDao.findById(1));
}
}
When I run this Test my findById is always null. Is there any way to run this test and keep all queries inside the separate file?
Since your UserDaoImpl uses Spring annotations (#Value, #PropertySource, etc), you have to initialize it via Spring to enable their injection. Or else you'd have to supply all these values yourself manually.
So in your test you should inject the DAO itself:
#Autowired
private UserDao userDao;
Or if you want to go manual way (not recommended), allow setting field findById via constructor (recommended) same way you're passing template, userMapper.

Property 'dataSource' is required in Spring, Java

I just started learning Spring, and now I try to crate Spring JDBC based DAO application.
I created config class in this way
#Configuration
#ComponentScan("com.foxminded.university")
public class SpringJdbcConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://127.0.0.1:5432/university");
dataSource.setUsername("maintainer");
dataSource.setPassword("12345678");
return dataSource;
}
}
And dao-class uses this bean
#Component
public class BuildingDao implements Dao<Building> {
#Autowired
private DataSource dataSource;
private final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
private static final String SAVE_BUILDING = "Insert into buildings (name, floors) values (?,?)";
#Override
public void save(Building building) {
jdbcTemplate.update(SAVE_BUILDING, building.getName(), building.getFloors());
}
}
But when I try to run this query i get
Exception in thread "main" java.lang.IllegalArgumentException: Property 'dataSource' is required
How I can fix it? As I can see, I use #Autowired incorrectly, because everything works fine when I use
private DataSource dataSource = new SpringJdbcConfig().dataSource();
But it is extra relation and mistake in terms of IoC.
By the way in main I also have to use this in this way
public class Main {
public static void main(String[] args) {
Building building = new SpringJdbcConfig().building();
building.setName("hghgf");
building.setFloors(2);
BuildingDao buildingDao = new SpringJdbcConfig().buildingDao();
buildingDao.save(building);
}
}
I would be very grateful if you could explain how to use #autowired correctly and inject beans into the main class.
I would suggest you to use spring boot to configure your application as below. This will initialize and auto-configure most of your needs.
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan("com.foxminded.university")
public class SpringBootWebApp {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootWebApp.class, args);
context.registerShutdownHook();
}
}
After this, you can use #Autowire for all those spring managed beans you configure.
Write the ComponentScan attribute as follows and make sure SpringJdbcConfig class is under the package com.foxminded.university*
#ComponentScan(basePackages = "com.foxminded.university")

Spring database transaction not committing data in Db

I have created a project with spring boot. I have hikariConfig to create the data source for connection pooling with the autocommmit property set as false. Doing batch insert with jdbcTemplate running inside method annotated with #Transaction for DataSourceTransactionManager. I am unable to see the data getting inserted in Db after the program execution. If I make the autocommit true in hikariconfig it works fine.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Component
#EnableTransactionManagement
public class DataSourceConfig {
#Bean (name = "dateSourceForSqlServer")
public DataSource dataSourceForSqlServer () {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setConnectionTimeout(10000L);
hikariConfig.setIdleTimeout(10000L);
hikariConfig.setMinimumIdle(1);
hikariConfig.setMaximumPoolSize(1);
hikariConfig.setMaxLifetime(600000L);
hikariConfig.setConnectionTestQuery("select 1");
hikariConfig.setValidationTimeout(4000L);
hikariConfig.setJdbcUrl("jdbc:sqlserver://localhost:1433;database=invt_mgmt");
hikariConfig.setUsername("sa");
hikariConfig.setPassword("sql_server_pass_123");
hikariConfig.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
hikariConfig.setAutoCommit(false);
return new HikariDataSource(hikariConfig);
}
#Bean (name = "jdbcTemplateForSqlServer")
public JdbcTemplate jdbcTemplateForSqlServer () {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSourceForSqlServer());
return jdbcTemplate;
}
#Primary
#Bean(name = "invtMgmtTxMangerForSqlServer")
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSourceForSqlServer());
return manager;
}
}
#Component
public class startBean {
#Autowired
private Business Business;
#PostConstruct
public void startApp() throws SQLException {
Business.insertContainerHierarchy();
Business.insertContainerHierarchy();
}
}
#Component
class public Business {
#Autowired
#Qualifier("jdbcTemplateForSqlServer")
private JdbcTemplate jdbcTemplateForSqlServer;
String insertIntStudent = "INSERT INTO student (id, name) Values(?, ?)";
#Transactional(value = "invtMgmtTxMangerForSqlServer")
public void insertContainerHierarchy () throws SQLException {
System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
Date start = new Date();
for (int i = 0; i < 500; i++) {
System.out.println(i);
jdbcTemplateForSqlServer.batchUpdate(insertIntStudent, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, i);
ps.setString(2, String.valueOf(i));
}
#Override
public int getBatchSize() {
return 1000;
}
});
}
System.out.println(new Date().getTime() - start.getTime());
}
}
I have used TransactionSynchronizationManager.isActualTransactionActive() which is returing true when the method is executed.
Q1. Why the data is not getting inserted, Transaction is supposed to autocommit once the method is executed?
Q2. In case spring transaction is getting used will database connection autocommit value make any difference?
Q3. How currently I am able to insert with autocommit set to true?
You are trying to invoke a transaction-wrapped proxy from within the #PostConstruct method. For that bean, all the initialization may be complete but not necessarily for the rest of the context. Not all proxies may be set at that point.
I would suggest implementing the ApplicationListener<ContextRefreshedEvent> interface in order to trigger any data creation inside that class. This will ensure it will be called after the entire context has been set-up:
#Component
public class OnContextInitialized implements
ApplicationListener<ContextRefreshedEvent> {
#Autowired
private Business Business;
#Override public void onApplicationEvent(ContextRefreshedEvent event) {
Business.insertContainerHierarchy();
Business.insertContainerHierarchy();
}
}

NullPointerException while accessing an instance of Spring JdbcTemplate

I am trying to use Spring JDBCTemplate class to access database.
As a first tutorial, I have used xml spring configuration file and everything works as expected.
Now, I am trying to use #Configuration and created DataSource and JdbcTemplate instances through #Bean annotation within this file. But, I get a NullPointerException at int result = template.update(sql);
I am sure I have done a silly mistake. Was wondering what it could be.
The code is as follows.
#Configuration
public class SpringJDBCAnnotation {
#Autowired
static JdbcTemplate template;
#Autowired
static DataSource dataSource;
#Bean
DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/organization");
ds.setUsername("root");
ds.setPassword("test");
return ds;
}
#Bean
JdbcTemplate template() {
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
public static void main(String[] args) {
String sql = "insert into employee values(1, 'Tom', 'Cruise')";
int result = template.update(sql);
System.out.println("# of records inserted : " + result);
}
}
Application Context needs to be obtained whenever we need to create beans or autowire them.
Since this is Java based configuration, it is retrieved as follows.
ApplicationContext context = new AnnotationConfigApplicationContext(SpringJDBCAnnotation.class);
and the beans needs to be accessed from the context as usual.
JdbcTemplate template = context.getBean(JdbcTemplate.class);

Spring JUNIT datasource autowiring

I got Spring Java Config style configuration with 2 datasources:
#Configuration
#EnableTransactionManagement
public class DBConfig {
// main db
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.build();
}
//db for test
#Bean(name = "testDataSource")
public DataSource testDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
#Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
But when I autowire that datasources in my Test class and run him: i got the same result:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DBConfig.class)
#Transactional
public class JdbcTest {
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("testDataSource")
private DataSource testDataSource;
#Test
public void findRealDb() {
String rowCount = jdbcTemplate.queryForObject("select message from messages", String.class);
System.out.println("real db " + rowCount);
}
#Test
public void findTestDb() {
String rowCount = (new JdbcTemplate(testDataSource).queryForObject("select message from messages", String.class));
System.out.println("test db " + rowCount);
}
}
So as result method findTestDb() logs same rowCount string as findRealDb() , but as you see them use different datasources to build jdbcTemplate.
The test code will be autowiring by type. I'm surprised you don't get a non-unique bean exception.
The trouble is you have two beans, one of which is qualified and one of which isn't.
You are much better off using Spring profiles and assigning the test datasource to a test profile and the production datasource to a default profile, then setting the active profile for your test case to be test.
Here's an example:
http://fahdshariff.blogspot.co.uk/2012/09/spring-3-javaconfig-unit-testing-using.html
note that you can put the #Profile annotation on an individual bean or a configuration class.

Categories

Resources