Requirement :
Having a query stored in DB with in a query there is a where condition in that its calling a database function.
Using spring MVC I need to get the query, pass the parameter and get the return value.
This is the query:
SELECT COUNT(*)
FROM IncidentHdr ih, IncidentUser iu
WHERE ih.incidentId = iu.incidentHdr.incidentId
AND get_response_team_access (ih.incidentId, :perscode)
Here get_response_team_access is a DB function which returns an integer. Query works fine as we tested in DB using dummy data.
What I tried So far :
#PersistenceContext
private EntityManager em;
#Override
public Long getAlertCount(String queryString, long persCode) throws DataAccessException {
Query q = em.createQuery(queryString);
q.setParameter("perscode", persCode);
return (long) q.getSingleResult();
}
Throws Exception:
ERROR org.hibernate.hql.internal.ast.ErrorCounter - <AST>:1:293: unexpected AST node: (
antlr.NoViableAltException: unexpected AST node: (
To call DB function from JPQL you have to use FUNCTION keyword.
SELECT COUNT(*) FROM IncidentHdr ih,IncidentUser iu
WHERE ih.incidentId = iu.incidentHdr.incidentId
AND FUNCTION('get_response_team_access',ih.incidentId, :perscode)
Use FUNCTION (formerly FUNC) to call database specific functions from
JPQL
Usage:
You can use FUNCTION to call database functions that are not supported
directly in JPQL and to call user or library specific functions.
Source: http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/j_func.htm
Related
I want to map my query result to RequestProjection interface values.
The following code works and return request id and submission date.
I need to return worker name too. I have tried r.worker_name AS workerName and r.worker_name AS worker_name and r.worker_name AS worker.name but none of them works.
How can I select and map worker name?
Query:
SELECT r.id AS id, r.submission_date AS submissionDate
From Request r
WHERE r.id = 1
Projection:
public interface RequestProjection {
Long getId();
Long getSubmissionDate();
Worker getWorker();
interface Worker {
String getName();
}
}
You can do this without native query using the constructor expression, read more here Spring JPA native query with Projection gives "ConverterNotFoundException"
I'm using Spring MVC 4, Hibernate and PostgreSQL 9.3 and have defined function (stored procedure) inside Postgres like this:
CREATE OR REPLACE FUNCTION spa.create_tenant(t_name character varying)
RETURNS void AS
$BODY$
BEGIN
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I AUTHORIZATION postgres', t_name);
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION spa.create_tenant(character varying)
OWNER TO postgres;
If I run this function inside pgAdmin like this it's working fine:
select spa.create_tenant('somename');
Now I'm trying to run this function from my service like this:
#Override
#Transactional
public void createSchema(String name) {
StoredProcedureQuery sp = em.createStoredProcedureQuery("spa.create_tenant");
sp.registerStoredProcedureParameter("t_name", String.class, ParameterMode.IN);
sp.setParameter("t_name", name);
sp.execute();
}
If I run my method I'm getting following error:
javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
I'm guessing this is because of return type void that is defined in function so I changed return type to look like this:
RETURNS character varying AS
If I run my method again I'm getting this exception instead:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Error calling CallableStatement.getMoreResults
Does anyone know what is going on here and how to properly call stored procedures in PostgreSQL even with void as return type?
In case you are using also spring data, you could just define a procedure inside your #Repository interface like this,
#Procedure(value = "spa.create_tenant")
public void createTenantOrSomething(#Param("t_name") String tNameOrSomething);
More in the docs.
In your entity class, define a NamedNativeQuery like you would call postgresql function with select.
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.Entity;
#NamedNativeQueries(
value={
// cast is used for Hibernate, to prevent No Dialect mapping for JDBC type: 1111
#NamedNativeQuery(
name = "Tenant.createTenant",
query = "select cast(create_tenant(?) as text)"
)
}
)
#Entity
public class Tenant
hibernate is not able to map void, so a workaround is to cast result as text
public void createSchema(String name) {
Query query = em.createNamedQuery("Tenant.createTenant")
.setParameter(1, name);
query.getSingleResult();
}
Since you're using PostgreSQL, you can, as you've already written, call any stored procedure of type function in SELECT (Oracle, otherwise, would let you only execute functions declared to be read only in selects).
You can use EntityManager.createNativeQuery(SQL).
Since you're using Spring, you can use SimpleJdbcTemplate.query(SQL) to execute any SQL statement, as well.
I think it's the RETURN VOID that's causing the issue. So, changed the FUNCTION definition like this:
CREATE OR REPLACE FUNCTION spa.create_tenant(t_name character varying)
RETURNS bigint AS
$BODY$
BEGIN
EXECUTE format('CREATE SCHEMA IF NOT EXISTS %I AUTHORIZATION postgres', t_name);
RETURN 1;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION spa.create_tenant(character varying)
OWNER TO postgres;
After you changed your function to return some dummy value, change the stored procedure query to this:
StoredProcedureQuery query = entityManager
.createStoredProcedureQuery("spa.create_tenant")
.registerStoredProcedureParameter(1,
Long.class, ParameterMode.OUT)
.registerStoredProcedureParameter(2,
String.class, ParameterMode.IN)
.setParameter(2, name);
query.getResultList();
If you want to keep it simple, just do this:
em.createSQLQuery("SELECT * FROM spa.create_tenant(:t_name) ")
.setParameter("t_name", name)").list();
Notice I used list() intentionally.. for some reason .update() didn't work for me.
PostgreSQL
Hibernate
Kotlin
CREATE OR REPLACE FUNCTION your_procedure() RETURNS text AS $$
BEGIN
RETURN 'Some text';
END;
$$ LANGUAGE plpgsql;
val query = session.createNativeQuery("SELECT your_procedure()")
query.list().map {
println("NativeQuery: $it")
}
For a procedure, try this:
#Procedure("spa.create_tenant")
String createTenant(String tenant);
Here is my code which uses jdbcTemplate
String SQL = "select branch from branchTable where branch_name = '" + branch + "'";
ArrayList<String> branchList = (ArrayList<String>) jdbcTemplateObject.query(SQL, new RowMapper() {
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("city_desc");
}
});
return branchList;
Now i want to be able to use preparedstatement with a query like "select branch from branchTable where branch_name = ?"
How can i do that with jdbcTemplate ?
Examples i searched show demonstration on how to use it with update or insert query, but not with select query..
Please help.
JdbcTemplate has another query() method which takes arguments of the prepared statement as parameter:
jdbcTemplateObject.query(SQL, new Object[] {branchName}, new RowMapper() {...});
Note that:
SQL should be named sql
You should use List and not ArrayList. Nothing in the javadoc guarantees that an ArrayList is returned. And you shouldn't care about the concrete type of list returned.
You should use a RowMapper<String> and not a raw RowMapper.
I am trying to query a Azure Table Storage. For that I use the following two methods:
TableServiceContext:
public IQueryable<T> QueryEntities<T>(string tableName) where T : TableServiceEntity
{
this.ResolveType = (unused) => typeof(T);
return this.CreateQuery<T>(tableName);
}
Code that uses the method above:
CloudStorageAccount account = AzureConnector.GetCloudStorageAccount(AppSettingsVariables.TableStorageConnection);
AzureTableStorageContext context = new AzureTableStorageContext(account.TableEndpoint.ToString(), account.Credentials);
// Checks if the setting already exists in the Azure table storage. Returns null if not exists.
var existsQuery = from e in context.QueryEntities<ServiceSettingEntity>(TableName)
where e.ServiceName.Equals(this.ServiceName) && e.SettingName.Equals(settingName)
select e;
ServiceSettingEntity existingSettginEntity = existsQuery.FirstOrDefault();
The LINQ query above generates the following request url:
http://127.0.0.1:10002/devstoreaccount1/PublicSpaceNotificationSettingsTable()?$filter=(ServiceName eq 'PublicSpaceNotification') and (SettingName eq 'expectiss')
The code in the class generates the following MissingMethodException:
I have looked at the supported LINQ Queries for the Table API;
Looked at several working stackoverflow solutions;
Tried IgnoreResourceNotFoundException on the TableServiceContext (usercomments of QueryOperators);
Tried to convert the linq query with ToList() before calling first or default (usercomments of QueryOperators).
but I can't get this to work.
Make sure you have parameterless constructor for the class "ServerSettingEntity". The ‘DTO’ that inherits TableServiceEntity needs a constructor with no parameters.
Is there any way I can get resultset object from one of jdbctemplate query methods?
I have a code like
List<ResultSet> rsList = template.query(finalQuery, new RowMapper<ResultSet>() {
public ResultSet mapRow(ResultSet rs, int rowNum) throws SQLException {
return rs;
}
}
);
I wanted to execute my sql statement stored in finalQuery String and get the resultset. The query is a complex join on 6 to 7 tables and I am select 4-5 columns from each table and wanted to get the metadata of those columns to transform data types and data to downstream systems.
If it is a simple query and I am fetching form only one table I can use RowMapper#mapRow and inside that maprow method i can call ResultsetExtractor.extractData to get list of results; but in this case I have complex joins in my query and I am trying to get resultset Object and from that resultset metadata...
The above code is not good because for each result it will return same resultset object and I dont want to store them in list ...
Once more thing is if maprow is called for each result from my query will JDBCTemplate close the rs and connection even though my list has reference to RS object?
Is there any simple method like jdbcTemplate.queryForResultSet(sql) ?
Now I have implemented my own ResultSet Extractor to process and insert data into downstream systems
sourceJdbcTemplate.query(finalQuery, new CustomResultSetProcessor(targetTable, targetJdbcTemplate));
This CustomResultSetProcessor implements ResultSetExtractor and in extractData method I am calling 3 different methods 1 is get ColumnTypes form rs.getMetaData() and second is getColumnTypes of target metadata by running
SELECT NAME, COLTYPE, TBNAME FROM SYSIBM.SYSCOLUMNS WHERE TBNAME ='TABLENAME' AND TABCREATOR='TABLE CREATOR'
and in 3rd method I am building the insert statement (prepared) form target columntypes and finally calling that using
new BatchPreparedStatementSetter()
{
#Override
public void setValues(PreparedStatement insertStmt, int i) throws SQLException{} }
Hope this helps to others...
Note that the whole point of Spring JDBC Template is that it automatically closes all resources, including ResultSet, after execution of callback method. Therefore it would be better to extract necessary data inside a callback method and allow Spring to close the ResultSet after it.
If result of data extraction is not a List, you can use ResultSetExtractor instead of RowMapper:
SomeComplexResult r = template.query(finalQuery,
new ResultSetExtractor<SomeComplexResult>() {
public SomeResult extractData(ResultSet) {
// do complex processing of ResultSet and return its result as SomeComplexResult
}
});
Something like this would also work:
Connection con = DataSourceUtils.getConnection(dataSource); // your datasource
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(query); // your query
ResultSetMetaData rsmd = rs.getMetaData();
Although I agree with #axtavt that ResultSetExtractor is preferred in Spring environment, it does force you to execute the query.
The code below does not require you to do so, so that the client code is not required to provide the actual arguments for the query parameters:
public SomeResult getMetadata(String querySql) throws SQLException {
Assert.hasText(querySql);
DataSource ds = jdbcTemplate.getDataSource();
Connection con = null;
PreparedStatement ps = null;
try {
con = DataSourceUtils.getConnection(ds);
ps = con.prepareStatement(querySql);
ResultSetMetaData md = ps.getMetaData(); //<-- the query is compiled, but not executed
return processMetadata(md);
} finally {
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, ds);
}
}