Issue
We are using ObjectMapper. When using ObjectMapper with RowMapper, should it be declared inside each mapRow (seen below), or outside of mapRow as a class public member? I assume it should be outside as a public class member per this article. href="https://stackoverflow.com/questions/3907929/should-i-declare-jacksons-objectmapper-as-a-static-field">Should I declare Jackson's ObjectMapper as a static field?
Currently using Spring boot with SQL Server database. Researching thread safety with thousands/millions of sql rows its getting.
List<Product> productList = namedParameterJdbcTemplate.query(sqlQuery,
parameters,
new ProductMapper(productRequest));
public class ProductMapper implements RowMapper<Product> {
@Override
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
ObjectMapper objectMapper = new ObjectMapper()
Product product = new Product();
product.setProductId(rs.getLong("ProductId"));
product.setProductType(rs.getString("ProductType"));
product.setLocations(objectMapper.readValue(rs.getString("Locations"), new TypeReference<List<ServiceLocation>>(){}));
} catch (Exception e) {
throw new ServiceException(e);
}
}
Note: Please don't ask why we are writing this manual mapper with ObjectMapper, we are doing legacy coding, and architects requested to do this.
Solution
An ObjectMapper
instance is not immutable but, as stated in the documentation:
Mapper instances are fully thread-safe provided that ALL configuration of the instance occurs before ANY read or write calls.
Which means that this code is perfectly thread-safe:
public class ProductMapper implements RowMapper<Product> {
private ObjectMapper mapper;
public ProductMapper()
{
objectMapper = new ObjectMapper();
}
@Override
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
Product product = new Product();
product.setLocations(objectMapper.readValue(rs.getString("Locations"), new TypeReference<List<ServiceLocation>>(){}));
return product;
}
}
However, the TypeReference
object is still created for each row, which is not very efficient. A better way is to create
an ObjectReader
instance via the readerFor()
method:
public class ProductMapper implements RowMapper<Product> {
private ObjectReader objectReader;
public ProductMapper()
{
ObjectMapper objectMapper = new ObjectMapper();
objectReader = objectMapper.readerFor(new TypeReference<List<ServiceLocation>>(){});
}
@Override
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
Product product = new Product();
product.setLocations(objectReader.readValue(rs.getString("Locations")));
return product;
}
}
An ObjectReader
instance is immutable and thread-safe.
Answered By - Olivier
Answer Checked By - Robin (JavaFixing Admin)