In one of my team's Spring Boot application we are using Spring JPA with Hibernate as JPA provider. We have this SQLExecutor class as a repository which is autowired in the request handler. The following is the skeleton structure of the class:
@Repository
public class SQLExecutor {
@PersistenceContext
private EntityManager em;
@Value("${query.fetch.size}")
private Integer queryFetchSize;
@SuppressWarnings("unchecked")
public Stream<Map<String, Object>> executeSqlStreamed(String dbQuerySql, List<Object> params) {
Query query = constructQueryForStream(dbQuerySql, params);
return query.getResultStream();
}
@SuppressWarnings("unchecked")
public List<Map<String, Object>> executeSql(String dbQuerySql, List<Object> params) {
Query query = constructQueryForStream(dbQuerySql, params);
return query.getResultList();
}
@SuppressWarnings("unchecked")
public Map<String, Object> executeSqlSingleResult(String dbQuerySql, List<Object> params) {
Query query = constructQuery(dbQuerySql, params);
return (Map<String, Object>) query.getSingleResult();
}
private Query constructQueryForStream(String dbQuerySql, List<Object> params) {
Query query = constructQuery(dbQuerySql, params);
query.setHint(QueryHints.HINT_FETCH_SIZE, queryFetchSize);
return query;
}
@SuppressWarnings("deprecation")
private Query constructQuery(String dbQuerySql, List<Object> params) {
Query query = em.createNativeQuery(dbQuerySql);
query.unwrap(org.hibernate.query.Query.class).setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
int paramIndex = 0;
if (params != null && !params.isEmpty()) {
for (Object paramValue : params) {
query.setParameter(++paramIndex, paramValue);
}
}
return query;
}
}
All the DB query strings are SELECT statements, because the application only provides read access to only a specific category of tables. The datasource is Tomcat JDBC pooled data source and the underlying database is a SQL based database that supports connection specific session variables. Both datasource and entitymanagerfactorbean are configured in a different @Configuration file.
Question
I have to pass a token from authentication context of the request to the db layer as a session variable, which would authenticate whether the application user trying to access a particular resource has access to that resource or not, using something similar to stored procedures. So basically, every time a user requests to read a resource, a session variable needs to be set using a value from the context of that request, before making the actual SELECT query.
Now, I know how to extract the token, but what I want to understand is how to set it to the session variable whenever a db connection is initialized for that read request. From other applications of my team which are using JDBC directly instead of Hibernate JPA, it is simple enough like so:
try (Statement stmt = conn.createStatement()) {
stmt.executeUpdate("SET '" + variable + "' = '" + value + "'");
} catch (SQLException e) {
throw e;
}
But since JPA does not expose the connection object directly, how do I ensure the session variable is set to the same connection as the one being used to execute the select statement.
Note: I understand the connection object can be accessed by unwrapping EntityManager instance to underlying Session object and then using the doReturningWork method, but I think that is an anti-pattern and would make supporting streaming result set a manual effort which is detrimental to the application.