Spring Data JPA - Unit Testing with H2 - spring

My app is calling Oracle DB using Spring data JPA, and one of the operations is calling a Stored Procedure.
When it come to integration testing, I'm using H2 DB, but I found difficulties to call the SP.
Below is the way I'm calling the SP
final StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("MY_SP_HERE")
.registerStoredProcedureParameter("PARAM_1", String.class, ParameterMode.IN)
.setParameter("PARAM_1", "VALUE_PARAM_1")
.registerStoredProcedureParameter("V_CURSOR_OUT", CustomClass.class,
ParameterMode.REF_CURSOR);
query.execute();
final List<Object[]> obj = query.getResultList();
Is it possible to call the SP from H2 ?

Related

Spring boot manually commit transaction

In my Spring boot app I'm deleting and inserting a large amount of data into my MySQL db in a single transaction. Ideally, I want to only commit the results to my database at the end, so all or nothing. I'm running into issues where my deletions will be committed before my insertions, so during that period any calls to the db will return no data (not good). Is there a way to manually commit transaction?
My main logic is:
#Transactional
public void saveParents(List<Parent> parents) {
parentRepo.deleteAllInBatch();
parentRepo.resetAutoIncrement();
//I'm setting the id manually before hand
String sql = "INSERT INTO parent " +
"(id, name, address, number) " +
"VALUES ( ?, ?, ?, ?)";
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Parent parent = parents.get(i);
ps.setInt(1, parent.getId());
ps.setString(2, parent.getName());
ps.setString(3, parent.getAddress());
ps.setString(4, parent.getNumber());
}
#Override
public int getBatchSize() {
return parents.size();
}
});
}
ParentRepo
#Repository
#Transactional
public interface ParentRepo extends JpaRepository<Parent, Integer> {
#Modifying
#Query(
value = "alter table parent auto_increment = 1",
nativeQuery = true
)
void resetAutoIncrement();
}
EDIT:
So I changed
parentRepo.deleteAllInBatch();
parentRepo.resetAutoIncrement();
to
jdbcTemplate.update("DELETE FROM output_stream");
jdbcTemplate.update("alter table output_stream auto_increment = 1");
in order to try avoiding jpa's transaction but each operation seems to be committing separately no matter what I try. I have tried TransactionTemplate and implementing PlatformTransactionManager (seen here) but I can't seem to get these operations to commit together.
EDIT: It seems the issue I was having was with the alter table as it will always commit.
I'm running into issues where my deletions will be committed before my insertions, so during that period any calls to the db will return no data
Did you configure JPA and JDBC to share transactions?
If not, then you're basically using two different mechanisms to access the data (EntityManager and JdbcTempate), each of them maintaining a separate connection to the database. What likely happens is that only EntityManager joins the transaction created by #Transactional; the JdbcTemplate operation executes either without a transaction context (read: in AUTOCOMMIT mode) or in a separate transaction altogether.
See this question. It is a little old, but then again, using JPA and Jdbc together is not exactly a common use case. Also, have a look at the Javadoc for JpaTransactionManager.

Spring Boot + JPA - CrudRepository update and then read

I have a scenario where the need is to update some db records first based on a criteria and then read those records from db. I am using CrudReposirtory and in my controller I have a service which calls a repository method using a #Query to update the records and on the next line am trying to read the same records but the records are not updated unless the I am out of that controller method.
You should use #Modifying in conjunction with #Query if you perform an UPDATE statement.
From documentation:
#Modifying
#Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);
This will trigger the query annotated to the method as updating query instead of a selecting one. As the EntityManager might contain outdated entities after the execution of the modifying query, we automatically clear it (see JavaDoc of EntityManager.clear() for details). This will effectively drop all non-flushed changes still pending in the EntityManager. If you don't wish the EntityManager to be cleared automatically you can set #Modifying annotation's clearAutomatically attribute to false;

Spring JdbcTemplate and NamedParameterJdbcTemplate

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.

How do I migrate my JPA DAO to Spring Data with second level cache?

I have bunch of JPA DAOs im looking to migrate to Spring Data JPA. Some of my DAOS have second-level / query caching set up.
I have a process where I only retrieve the ID in my queries, and then look up the entity using findByID(). This way, only the id's are multiplied in the different query caches, and the entire entities are in the second level cache.
Example:
#NamedQuery(name = "SystemUser.findByEmail",
query = "SELECT u.id FROM SystemUser u WHERE email=:email"),
…
public SystemUser findByEmail(String email) {
TypedQuery<Long> q = getEntityManager().createNamedQuery("SystemUser.findByEmail", Long.class);
q.setParameter("email", email);
q.setHint("org.hibernate.cacheable", true);
q.setHint("org.hibernate.cacheRegion", "query.systemUser");
List<Long> res = q.getResultList();
if (res != null && res.size() > 0) {
return findById(res.get(0));
}
return null;
}
I have several more findBy…-methods, all doing it like this. It feels like a good way to keep cache memory consumption down.
I'm kind of new to the Spring Data JPA business, but I can't see how I would go about realizing this here? The #Cacheable annotations seems only to deal with query caches, which to me would duplicate the entities in each query cache?
Is there any way to do this with Spring Data? Pointers would be much appreciated.
In Spring Data JPA just create a findByEmail method and either Spring Data JPA will found your named query or create one itself.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
SystemUser findByEmail(String email);
}
Should be all you need to get the query executed and the desired result. Now with the #QueryHints you can add the hints you are setting now.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
#QueryHints(
#QueryHint(name="org.hibernate.cacheable", value="true"),
#QueryHint(name="org.hibernate.cacheRegion", value="query.systemUser") )
SystemUser findByEmail(String email);
}
The result will be cached and still the user will come from the 2nd level cache (if available, else created). Assuming of course your entity is #Cacheable.
A nice read on how the 2 different caches work (together) can be found here. A small snippet on how the query cache works.
The query cache looks conceptually like an hash map where the key is composed by the query text and the parameter values, and the value is a list of entity Id's that match the query:
If you want more complex logic (and really implement the optimization you did) you can always implement your own repository.

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