Showing posts with label JDBCTemplate. Show all posts
Showing posts with label JDBCTemplate. Show all posts

Monday, April 28, 2014

Resolved : Spring Initialize a bean which depends on another bean: Use JdbcTemplate when initializing a bean.

I was working on a web project using spring. I needed in the project to initialize a cache by doing a call to the database. So in order to do so, I was putting the database call in the default class constructor.
So in my Configuration class I do

@Bean("usersCache")
@DependsOn("myJdbcTemplate")
public UsersCache userCache{
    return new UsersCache(); 
}

And in the UserCache constructor I do :

@ManagedResource
public class UsersCache {
@Autowired
JdbcTemplate jdbcTemplate;

@PostConstruct
public UsersCache(){

@SuppressWarnings({ "unchecked", "rawtypes" })
List<LatestRecordPerUser> latestRecordByDevice = jdbcTemplate.query(populateCache, new BeanPropertyRowMapper(LatestRecordPerUser.class));
for(LatestRecordPerUser record:latestRecordByDevice ){
// And Here I insert into my cache
}
}


But even using this code I get a NullPointerException as the JdbcTemplate bean is not injected:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [main.java.org.beans.UsersCache ]: Constructor threw exception; nested exception is java.lang.NullPointerException

I have later understand that as the UsersCache is not yet initialized, nothing will be injected to it. So only after initialization, spring will inject the jdbcTemplate and thus we can execute the request.

So the fix the problem, there is no need to use @dependsOn . We need just to populate the cache after the bean (or repository) initialisation, So this can be done thanks to @PostConstruct. So the code is now :

@Repository
@Scope("singleton")
public class UsersCache {
@Autowired
JdbcTemplate jdbcTemplate;

private final Map userCache = new ConcurrentHashMap();

@PostConstruct
public void init(){

@SuppressWarnings({ "unchecked", "rawtypes" })
List<LatestRecordPerUser> latestRecordByDevice = jdbcTemplate.query(populateCache, new BeanPropertyRowMapper(LatestRecordPerUser.class));

for(LatestRecordPerUser record:latestRecordByDevice ){
// And Here I insert into my cache
}
}

Thant's it :D

Sunday, April 13, 2014

Spring JdbcTemplate/ NamedJdbcTemplate tutorial : Resolve Date insertion

I was working with Spring JdbcTemplate to insert rows in the data base. My query is:
insert into person (person_id, name, birth_date) values(?, ?, ?)

@Autowired
JdbcTemplate jdbcTemplate;
private String query ="insert into person (person_id, name, birth_date) values(?, ?, ?)"

public void store(Person person){

       jdbcTemplate.update(query , person.getId(), person.getName(), person.getBirthDate());

}

Using this code, I get this error

 org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [insert into
...]
; nested exception is org.postgresql.util.PSQLException: ERROR: column "birth_date" is of type timestamp with time zone but expression is of type character varying

So the database is not accepting the java.util.Date.
Next olution is to use the DATE '2004-02-02' of postgres, so I have modified my query:
insert into person (person_id, name, birth_date) values(?, ?, DATE ?)

So even with this modification I got :
nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "$3"

Ok, so maybe I need to add qotes. My query is now :
insert into person (person_id, name, birth_date) values(?, ?, DATE '?')
This didn't resolve the problem and I got this error:
PSQLException: The column index is out of range: 3, number of columns: 2

Ok, so my last chance is to use NamedParameterJdbcTemplate.
I have modified my code:
1- In the aplicationContext, I added:

<bean id="namedJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" >
        <constructor-arg ref="myDataSource" />
</bean>

2- In my code :

@Autowired
protected NamedParameterJdbcTemplate namedJdbcTemplate;

        private String query ="insert into person (person_id, name, birth_date) values(:id, :name, :birthDate)";

public void store(Person person){

       MapSqlParameterSource params = new MapSqlParameterSource();
   params.addValue("id", person.getId());
       params.addValue("name",  person.getName());
       params.addValue("birthDate", person.getBirthDate(), Types.DATE);
       jdbcTemplate.update(query , params);

}

Adding the Types.DATE ( don't forget to import java.sql.Types;), has resolved the problem, and now the insertion is working without any ProBleM ;)


Articles les plus consultés