Spring JdbcTemplate and NamedParameterJdbcTemplate - spring

Is it advisable to use JDBCTemplate and NamedParameterJdbcTemplate together with an idea that NamedParameterJdbcTemplate is used for inserting/updating while JdbcTemplate takes care of retrieving and deleting? Because I can insert objects by using NamedParameterJdbcTemplate as simple as shown below:
public long save(Domain obj) {
String sql = "insert into domain(name,password,salt,dnspod_domain_id,status)" +
" values(:name,:password,:salt,:dnspodDomainId,:status)";
KeyHolder keyHolder = new GeneratedKeyHolder();
namedJdbc.update(sql, new BeanPropertySqlParameterSource(obj), keyHolder);
return keyHolder.getKey().longValue();
}
If I want to insert objects/data into table by using JDBCTemplate, I will have to write lot of code manually assigning parameters with PreparedStatement...
When it comes to retrieving, I can do it by JDBCTemplate as shown below:
List<User> users = jdbcTemplate.query("SELECT * FROM user", BeanPropertyRowMapper.newInstance(User.class));
No need to use ResultSet along with RowMapper to retrieve rows.
My concern is that if there are any performance issues using JDBCTemplate and NamedParameterJdbcTemplate together.

You can use both JdbcTemplate and NamedParameterJdbcTemplate whenever it is needed. JdbcTemplate is slightly error-prone, since the order of "?" placeholders present in query and order of parameters you are passing through either array or direct setting is matter.
Where as NamedParameterJdbcTemplateallows you to assign names to parameters and map values to the parameters by name, does't matter which order you set the values.
As per NamedParameterJdbcTemplate api doc,
This class delegates to a wrapped JdbcTemplate once the substitution from named parameters to JDBC style '?' placeholders is done at execution time.
So internally api takes some additional time to convert Named params to `?' place holders, but this can be ignored.
My suggestion is if your query has too many parameters go with NamedParameterJdbcTemplate, since its safe and error free else go with JdbcTemplate.

Related

How to query more than one columns but not all columns with #Query but still use the Domain Data Model to map with Spring Data JDBC?

My Data model is
#Getter
#Setter
public class Customer {
#Id private ID id;
#CreatedDate protected Instant createdAt;
#LastModifiedDate protected Instant updatedAt;
#CreatedBy protected String createdBy;
#LastModifiedBy protected String updatedBy;
#Version protected Long version;
private UUID orderId;
private String offer;
}
My Repository is
public interface CustomerRepository extends CrudRepository<Customer, UUID> {
#Query(
"SELECT ID, Offer FROM Customer WHERE orderId = :orderId ")
List<Customer> findCustomerByOrderId(
#Param("orderId") UUID orderId);
}
This will result in an exception saying 'orderId column not found [42122-190]'. So Spring expects you to always query all the columns. I understand that with JPA we have a strong mapping between the Entities and the Data Schema. But the whole point of spring data JDBC is avoiding the tight coupling between POJO's data model and database schema. Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
Is there a way to tell the RowMapper used, to ignore properties which are not part of the query? Creating separate RowMapper for these simple queries seems a lot of unnecessary work.
I still can work around this by changing the query like
#Query(
"SELECT ID, Offer, OrderId, null as CreatedAt, null as CreatedBy, null as UpdatedAt, null as UpdatedBy, null as Version FROM Customer WHERE orderId = :orderId ")
But this will still serialize the entire object with null values. Am I missing something obvious here?
Note This is not Spring Data JPA. Its Spring Data JDBC.
Edit
Looking more into it, the exception is from h2 database lib.
Caused by: org.h2.jdbc.JdbcSQLException: Column "orderid" not found [42122-190]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.jdbc.JdbcResultSet.getColumnIndex(JdbcResultSet.java:3129)
at org.h2.jdbc.JdbcResultSet.get(JdbcResultSet.java:3217)
at org.h2.jdbc.JdbcResultSet.getObject(JdbcResultSet.java:522)
at com.zaxxer.hikari.pool.HikariProxyResultSet.getObject(HikariProxyResultSet.java)
at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:127)
You can't at least right now.
There are three solutions to this, two of which you already pointed out:
extend your select statement with , NULL as <column-name> for all the missing columns.
I'm not sure if
But this will still serialize the entire object with null values.
means that this isn't working for you in some way.
specify a RowMapper.
You could use a class containing exactly the fields returned by the query. It could even have getters for the other columns if you want an interface implemented by both your normal entity and the partial entity.
You write:
But the whole point of spring data JDBC is to avoid the tight coupling between pojo's data model and database schema.
This is not quite right.
An important goal of Spring Data JDBC is to not have a run time connection between entities and table rows.
This would require proxies or similar and brings a lot of complexity.
But the structural mapping between entities and table is probably going to be stronger (and certainly is right now) since all the variants of mappings available in JPA bring complexity.
And the main goal in Spring Data JDBC is to be conceptually simpler than JPA.
You also ask
Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
I'm not sure if I actively thought about it when I coded it but I don't like the idea of defaulting to NULL because this would make it easy to accidentally not load a column because you have a typo in an alias.
But I'm not against alternative solutions.
If you have an idea please create a feature request.

How return column from my DB using Java Spring JPQL

I have a very basic task. I want to return just one column from my table in my DB. Literally, I want the text from my category_name. This is my JPQL cod:|
#Transactional
#Modifying
#Query(value = "SELECT category_name FROM Category WHERE id=:id", nativeQuery = true)
String findName(#Param("id") long id);
And I have this error:
Modifying queries can only use void or int/Integer as return type!
As the error apparently states, you should use #Modifying annotation when you are actually updating/deleting the row. Since you are fetching data from already stored database, you can simply remove this annotation.
You should also remove #Transactional annotation.
https://dzone.com/articles/how-does-spring-transactional is an interesting article to know about how transactional annotation works and when it should be used.

How to give dynamic value to #Table(name=p+"name") in spring JPA

name of the table should be fixed but in my scenario the last part of the table name is profile based so in local it is X but in dev it is Y and so on till Prod. Is there way to add dynamically the value to the table name.
The question tries to implement a bad practice. Don't do that.
Currently, Spring, Hibernate, and JPA does not support your configuration type.
You can use Hibernate interceptors to change the table in the generated SQL statements.
For your case you can define your table class like this:
#Entity
#org.hibernate.annotations.Proxy(lazy=false)
#Table(name=TableNameReplacer.PLACEHOLDER, schema="MySchema")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class ProfileData implements Serializable {
and define your Hibernate interceptor in a following way:
public class TableNameReplacer extends EmptyInterceptor {
public static final String TABLE_PLACEHOLDER = "{table_placeholder}";
#Override
public String onPrepareStatement(String sql) {
if (sql.contains(TABLE_PLACEHOLDER )) {
String replacement = "{your logic to fill proper table name}";
sql = sql.replace(TABLE_SUFFIX_PLACEHOLDER, replacement);
}
return super.onPrepareStatement(sql);
}
Using this approach you're free to modify generated SQL and replace the table name there as you wish.
I recommend to use good placeholder value which you're sure will not be a part of actual values being saved to the table (or you can only limit this to select statements if you only read the data).

Spring Boot - Change connection dynamically

I have a Spring Boot project with multiple databases of different years and these databases have same tables so the only difference is the year (..., DB2016, DB2017). In the controller of the application i need to return data that belong to "different" years. Moreover in future years other databases will be created (eg. in 2018 there's going to be a db named "DB2018"). So my problem is how to switch the connection among databases without creating a new datasource and a new repository every new year.
In an other question posted by me (Spring Boot - Same repository and same entity for different databases) the answer was to create different datasources and different repositories for every existing database, but in this case i want to return data from existing databases on the basis of the current year. More specifically:
SomeEntity.java
#Entity(name = "SOMETABLE")
public class SomeEntity implements Serializable {
#Id
#Column(name="ID", nullable=false)
private Integer id;
#Column(name="NAME")
private String name;
}
SomeRepository.java
public interface SomeRepository extends PagingAndSortingRepository<SomeEntity, Integer> {
#Query(nativeQuery= true, value = "SELECT * FROM SOMETABLE WHERE NAME = ?1")
List<SomeEntity> findByName(String name);
}
SomeController.java
#RequestMapping(value="/foo/{name}", method=RequestMethod.GET)
public ResponseEntity<List<SomeEntity>> findByName(#PathVariable("name") String name) {
List<SomeEntity> list = autowiredRepo.findByName(name);
return new ResponseEntity<List<SomeEntity>>(list,HttpStatus.OK);
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/DB
spring.datasource.username=xxx
spring.datasource.password=xxx
So if the current year is 2017 i want something like this:
int currentyear = Calendar.getInstance().get(Calendar.YEAR);
int oldestDbYear = 2014;
List<SomeEntity> listToReturn = new LinkedList<SomeEntity>();
//the method getProperties is a custom method to get properties from a file
String url = getProperties("application.properties", "spring.datasource.url");
props.setProperty("user", getProperties("application.properties","spring.datasource.username"));
props.setProperty("password", getProperties("application.properties","spring.datasource.password"));
for (int i = currentYear, i>oldestDbYear, i--) {
//this is the connection that must be used by autowiredRepo Repository, but i don't know how to do this.
//So the repository uses different connection for every year.
Connection conn = getConnection(url+year,props);
List<SomeEntity> list_of_specific_year = autowiredRepo.findByName(name);
conn.close;
listToReturn.addAll(list_of_specific_year);
}
return listToReturn;
Hope everithing is clear
The thing that is probably most suitable to your needs here is Spring's AbstractRoutingDataSource. You do need to define multiple DataSources but you will only need a single repository. Multiple data sources is not an issue here as there is always a way to create the DataSource beans programatically at run time and register them with the application context.
How it works is you basically register a Map<Object, DataSource> inside your #Configuration class when creating your AbstractRoutingDataSource #Bean and in this case the lookup key would be the year.
Then you need create a class that implements AbstractRoutingDataSource and implement the determineCurrentLookupKey() method. Anytime a database call is made, this method is called in the current context to lookup which DataSource should be returned. In your case it sounds like you simply want to have the year as a #PathVariable in the URL and then as the implementation of determineCurrentLookupKey() grab that #PathVariable out of the URL (e.g in your controller you have mappings like #GetMapping("/{year}/foo/bar/baz")).
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
.getRequestAttributes()).getRequest();
HashMap templateVariables =
(HashMap)request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return templateVariables.get("year");
I used this approach when writing a testing tool for a product where there were many instances running on multiple different servers and I wanted a unified programming model from my #Controllers but still wanted it to be hitting the right database for the server/deployment combination in the url. Worked like a charm.
The drawback if you are using Hibernate is that all connections will go through a single SessionFactory which will mean you can't take advantage of Hibernate's 2nd level caching as I understand it, but I guess that depends on your needs.

Spring (MVC) SQL injection avoidance?

I am wondering how Spring MVC handles SQL injections (and other security issues: XSS, code [javascript] injection, etc). I'm talking mostly about escaping the values that are added to DBs and such. I can't seem to find any answer because every time I search for spring sql injection results that involve dependency injection arise.
My flow is as follows: from the client browser I make a request consisting of an JSON with some query parameters (not the SQL statement, that would be too stupid - to form the SQL query in JS). When the request reaches the properly annotated method in the Controller, the request is mapped via #RequestBody using Jackson to an "request object". Now this object is sent to the DAO, where using JDBC Template I query the db (and using RowMapper I map the results).
In the DAO I have something like:
public int countAll(RequestObject request) {
String sql = "SELECT count(*) FROM employees WHERE name = '" + request.getName() + "'";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
int count = jdbcTemplate.queryForInt(sql);
return count;
}
Now is this approach safe from SQL injection?
Are non-JDBCTemplate -based queries safe given that are flowing through Spring MVC?
Could we have a little discussion on this?
Anytime you build a query by concatenation you are vunerlable to injection attacks
pass your parameters correctly:
jdbcTemplate.queryForInt(sql, args, argTypes)
for example:
JdbcTemplate insert = new JdbcTemplate(dataSource);
insert.update("INSERT INTO PERSON (FIRSTNAME, LASTNAME) VALUES(?,?)",
new Object[] { firstName, lastName });

Resources