I am trying to execute a parameter query for a Postgre database using Springs SimpleJdbcTemplate. My class that calls the query looks like this:
public class GeoCodeServiceImpl extends SimpleJdbcDaoSupport implements GeoCodeServiceInterface {
public static final String SELECT_STATEMENT = "SELECT ste_code, ste_code_type, name, fips_code " +
"FROM \"steGeo\" " +
"WHERE st_contains( the_geom, ST_GeomFromText('POINT(:lon :lat)',4269))";
public List<GeoCode> getGeoResults(Double lon, Double lat) throws DataAccessException {
MapSqlParameterSource mappedParms = new MapSqlParameterSource("lon", lon.toString());
mappedParms.addValue("lat", lat.toString());
SqlParameterSource namedParms = mappedParms;
List<GeoCode> resultList = getSimpleJdbcTemplate().query(SELECT_STATEMENT, new GeoCodeRowMapper(), namedParms);
if (resultList == null || resultList.size() == 0) {
logger.warn("No record found in GeoCode lookup.");
}
return resultList;
}
protected static final class GeoCodeRowMapper implements RowMapper<GeoCode> {
public GeoCode mapRow(ResultSet rs, int i) throws SQLException {
GeoCode gc = new GeoCode();
gc.setCode(rs.getString(1));
gc.setType(rs.getString(2));
gc.setFips(rs.getString(3));
gc.setName(rs.getString(4));
return gc;
}
}
}
I am testing the query with this class:
public class GeoCodeServiceTest {
public static void main(String[] args) {
Double lat = 40.77599;
Double lon = -83.82322;
String[] cntxs = {"project-datasource-test.xml","locationService-context.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(cntxs);
GeoCodeServiceImpl impl = ctx.getBean("geoCodeService", GeoCodeServiceImpl.class);
List<GeoCode> geoCodes = impl.getGeoResults(lon, lat);
System.out.println(geoCodes);
}
}
I keep getting the following error:
2011-03-07 08:16:29,227 [main] DEBUG org.springframework.jdbc.support.SQLErrorCodesFactory - SQL error codes for 'PostgreSQL' found
2011-03-07 08:16:29,227 [main] DEBUG org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator - Unable to translate SQLException with SQL state 'XX000', error code '0, will now try the fallback translator
2011-03-07 08:16:29,227 [main] DEBUG org.springframework.jdbc.support.SQLStateSQLExceptionTranslator - Extracted SQL state class 'XX' from value 'XX000'
Exception in thread "main" org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [SELECT ste_code, ste_code_type, name, fips_code FROM "steGeo" WHERE st_contains( the_geom, ST_GeomFromText('POINT(:lon :lat)',4269))]; SQL state [XX000]; error code [0]; ERROR: parse error - invalid geometry
Hint: "POINT(" <-- parse error at position 6 within geometry; nested exception is org.postgresql.util.PSQLException: ERROR: parse error - invalid geometry
Hint: "POINT(" <-- parse error at position 6 within geometry
It looks like my parameters are not populated.
I haven't used Postgre before so any help would be much appreciated.
Thanks
Parameters are not handled inside quoted strings, so I guess you need to pass the whole string as a single parameter:
public static final String SELECT_STATEMENT =
"SELECT ste_code, ste_code_type, name, fips_code " +
"FROM \"steGeo\" " +
"WHERE st_contains( the_geom, ST_GeomFromText(:pt, 4269))";
...
MapSqlParameterSource mappedParms = new MapSqlParameterSource("pt",
"POINT(" + lon.toString() + " " + lat.toString() + ")");
Related
I am new to spring boot and spring data jpa. I am trying to use native queries for executing search based on search attributes received from UI.
The records that are obtained based on the searchParam should search if the searchParam is contained in any of the specified columns (as mentioned in the native query)
I have written the following code but I end up receiving the error as mentioned in the title. I have tried looking up for response in stackoverflow. But i believe i have followed the suggestions as mentioned in many of the threads.
Any help in this regard would be highly appreciated.
Code snippet below
EpicController.java
#CrossOrigin
#RequestMapping(value="/search", method = RequestMethod.GET)
public Page<Epic> searchEpicsByProjectIdAndSearchParam(#RequestParam String searchParam, #RequestParam String projectId, Pageable pageable) throws Exception {
logger.info("Inside searchEpicsByAttributes() based on searchQuery API");
Page<Epic> results = null;
try {
results = epicService.searchEpicsByProjectIdAndSearchParam(searchParam, projectId, pageable);
}
catch(Exception ex) {
ex.printStackTrace();
throw new Exception("Exception occurred :: " + ex.getStackTrace());
}
return results;
}
EpicService.java (Interface)
public interface EpicService {
Page<Epic> searchEpicsByProjectIdAndSearchParam(String searchParam, String projectId, Pageable pageable);
}
EpicServiceImpl.java
#Override
public Page<Epic> searchEpicsByProjectIdAndSearchParam(String searchParam, String projectId, Pageable pageable) {
logger.info(" Inside searchEpicsByProjectIdAndSearchParam() API in EpicServiceImpl");
return epicRepository.findBySearchParamsAndProjectId(searchParam,projectId, pageable);
}
EpicRepository.java
#Repository
public interface EpicRepository extends JpaRepository<Issue, String> {
#Query(value =
"select i.* from issue i where ("
+ "upper(i.name) like upper('%?1%'))"
+ "and upper(i.project_id) = upper('%?2%')"
+ "ORDER BY i.name DESC \n-- #pageable\n",
countQuery =
"select count(i.*) from issue i where ("
+ "upper(i.name) like upper('%?1%'))"
+ "and upper(i.project_id) = upper('%?2%')",
nativeQuery = true)
Page<Epic> findBySearchParamsAndProjectId(String name, String projectId, Pageable pageable);
}
Exception:
2019-02-08 23:25:21.199 INFO 12556 --- [nio-8080-exec-1] c.a.m.A.controller.ProjectController : Inside searchEpicsByProjectIdAndSearchParam() API in EpicServiceImpl
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that position [1] did not exist; nested exception is java.lang.IllegalArgumentException: Parameter with that position [1] did not exist
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:525)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:209)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
Caused by: java.lang.IllegalArgumentException: Parameter with that position [1] did not exist
at org.hibernate.jpa.spi.BaseQueryImpl.findParameterRegistration(BaseQueryImpl.java:502)
at org.hibernate.jpa.spi.BaseQueryImpl.setParameter(BaseQueryImpl.java:692)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:181)
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:32)
at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:141)
at org.springframework.data.jpa.repository.query.StringQueryParameterBinder.bind(StringQueryParameterBinder.java:61)
at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:101)
at org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.bind(SpelExpressionStringQueryParameterBinder.java:76)
at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:161)
at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:152)
at org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.doCreateQuery(AbstractStringBasedJpaQuery.java:81)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:202)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$PagedExecution.doExecute(JpaQueryExecution.java:188)
Skip Single Quotations "'" around params i.e. ?1 and ?2. Working query will be like:
"select i.* from issue i where ("
+ "upper(i.name) like upper(%?1%))"
+ "and upper(i.project_id) = upper(%?2%)"
+ "ORDER BY i.name DESC \n-- #pageable\n",
countQuery =
"select count(i.*) from issue i where ("
+ "upper(i.name) like upper(%?1%))"
+ "and upper(i.project_id) = upper(%?2%)"
I'm working on an spring-boot/jpa/ mysql project. Now so far everything worked with DateTime objects when fetching/storing objects with the repository.
The problem has now occured when I use the Jdbc Template to execute a custom sql query.
org.springframework.beans.ConversionNotSupportedException: Failed to convert property
value of type 'java.sql.Timestamp' to required type java.time.LocalDateTime' for
property 'from_time': no matching editors or conversion strategy found
The idea is to fetch Time slots (has a start time and duration in minutes) that are overlapping with a new incoming entry.
To get back my objects I was first using a BeanPropertyMapper and then switched to a custom NestedRowMapper.
The resulting conflicting time slots I want to get look like this:
{
id: 1
comment: "i worked 60minutes"
from_time: "2018-06-16 13:00"
duration_minutes: 60
task: {
name: "My task"
...
}
}
This is the method where I run into the issue:
public List<TimeSlot> getOverlappingEntries(TimeSlot timeslot) throws SQLException {
String sql = "SELECT time_slot.comment, time_slot.from_time,"
+ "DATE_ADD(from_time, INTERVAL duration_minutes MINUTE) AS end_time, "
+ " task.name as `task.name`, task.category as `task.category` "
+ " FROM `time_slot` " + " INNER JOIN task on task.id = time_slot.task_id "
+ " WHERE person_id = ? "
+ " HAVING ? < end_time AND DATE_ADD(? ,INTERVAL ? MINUTE) > from_time;";
PreparedStatementCreator prepared = (con) -> {
PreparedStatement prep = con.prepareStatement(sql);
prep.setObject(1, timeslot.person.id);
prep.setObject(2, timeslot.from_time);
prep.setObject(3, timeslot.from_time);
prep.setObject(4, timeslot.durationMinutes);
logger.info(prep.toString());
return prep;
};
return this.connector.query(prepared, NestedRowMapper.get(TimeSlot.class));
}
Now I would imagine spring is capable of converting those objects easily. And anyway there is the simple way of timestamp.toLocalDateTime() to do so. The problem seems more how to register this as a converter service or how to fix spring-boot configuration to do so.
I tried already a custom converter service but that didn't help:
#javax.persistence.Converter
public class SqlTimestampToLocalDateTimeConverter implements Converter<Timestamp,
LocalDateTime>, AttributeConverter<Timestamp, LocalDateTime> {
#Convert
#Override
public LocalDateTime convert(Timestamp source) {
return source.toLocalDateTime();
}
#Override
public LocalDateTime convertToDatabaseColumn(Timestamp attribute) {
return attribute.toLocalDateTime();
}
#Override
public Timestamp convertToEntityAttribute(LocalDateTime dbData) {
return Timestamp.valueOf(dbData);
}
}
Also many other answers on the internet mentioned that this was already implemented with spring framework 4.x.
The dependencies in the project look like this (build.gradle):
dependencies {
compile "org.springframework.boot:spring-boot-starter-thymeleaf:2.0.2.RELEASE"
compile "org.springframework.boot:spring-boot-starter-web:2.0.2.RELEASE"
compile "org.springframework.boot:spring-boot-starter-security:2.0.2.RELEASE"
compile "org.springframework.boot:spring-boot-starter-data-jpa:2.0.2.RELEASE"
compile "mysql:mysql-connector-java:5.1.46"
compileOnly "org.springframework.boot:spring-boot-devtools:2.0.2.RELEASE"
compile 'org.springframework.data:spring-data-rest-webmvc:3.0.7.RELEASE'
compile 'com.querydsl:querydsl-jpa:4.1.4'
compile 'com.querydsl:querydsl-apt:4.1.4:jpa'
testCompile("junit:junit")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("org.springframework.security:spring-security-test")
}
Thank you for any hints, how to solve this!
/edit:
I think I see a possible workaround now. What I could do is just to fetch the id's of all time slots and then use the repository to fetch the actual objects with their data (also their task objects).
But that feels definitely not like the optimal solution...
This is the NestedRowMapper I use:
import org.springframework.beans.*;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.JdbcUtils;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
public class NestedRowMapper<T> implements RowMapper<T> {
private Class<T> mappedClass;
public static <T> NestedRowMapper<T> get(Class<T> mappedClass) {
return new NestedRowMapper<>(mappedClass);
}
public NestedRowMapper(Class<T> mappedClass) {
this.mappedClass = mappedClass;
}
#Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
try {
T mappedObject = this.mappedClass.newInstance();;
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
bw.setAutoGrowNestedPaths(true);
ResultSetMetaData meta_data = rs.getMetaData();
int columnCount = meta_data.getColumnCount();
for (int index = 1; index <= columnCount; index++) {
try {
String column = JdbcUtils.lookupColumnName(meta_data, index);
Object value = JdbcUtils.getResultSetValue(rs, index, Class.forName(meta_data
.getColumnClassName(index)));
bw.setPropertyValue(column, value);
} catch (TypeMismatchException | NotWritablePropertyException
| ClassNotFoundException e) {
e.printStackTrace();
}
}
return mappedObject;
} catch (InstantiationException | IllegalAccessException e1) {
throw new RuntimeException(e1);
}
}
}
You're on the right lines that you can define a RowMapper that tells your app what type of object each column needs to be mapped to. I would recommend trying to use JdbcTemplate.query method: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html#query-java.lang.String-java.lang.Object:A-org.springframework.jdbc.core.RowMapper-
You will need to define a RowMapper (not necessarily a NestedRowMapper, you could try ParameterizedRowMapper), then pass that into query with your SQL and WHERE conditions mapped as args.
I think the bast way to use BeanPropertyRowMapper.newInstance(TimeSlot.class) in your getOverlappingEntries method
try this on NestedRowMapper.mapRow
if (value instanceof Timestamp) value = ((Timestamp) value).toLocalDateTime();
I'm using Spring and Oracle database in my solution and i need to execute script
select count(1) from ELEMENTS, table(cast(? as arrayofnumbers)) session_ids
where root_session_id in session_ids.VALUE
but i have a problem with passing input parameter.
i try to pass List or array of BigInteger into
JdbcTemplate.queryForObject("select count(1) from ELEMENTS, table(cast(? as arrayofnumbers)) session_ids
where root_session_id in session_ids.VALUE", Integer.class, INPUT_PARAMS)
but has an Exception:
java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8861)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8338)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9116)
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:9093)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:234)
at weblogic.jdbc.wrapper.PreparedStatement.setObject(PreparedStatement.java:357)
Does anyone have the same problem?
EDIT:
Forget to describe arrayofnumber. It's custom type:
TYPE arrayofnumbers as table of number(20)
Found the solution:
final BigInteger[] ids = new BigInteger[]{BigInteger.valueOf(9137797712513092132L)};
int count = jdbc.query("select count(1) from NC_DATAFLOW_ELEMENTS\n" +
" where root_session_id in (select /*+ cardinality(t 10) */ * from table(cast (? as arrayofnumbers)) t)"
, new PreparedStatementSetter() {
public void setValues(PreparedStatement preparedStatement) throws SQLException {
Connection conn = preparedStatement.getConnection();
OracleConnection oraConn = conn.unwrap(OracleConnection.class);
oracle.sql.ARRAY widgets = oraConn.createARRAY("ARRAYOFNUMBERS", ids);
preparedStatement.setArray(1, widgets);
}
}, new ResultSetExtractor<Integer>() {
public Integer extractData(ResultSet resultSet) throws SQLException, DataAccessException {
resultSet.next();
return resultSet.getInt(1);
}
});
out.println(count);
should note that type of array (ARRAYOFNUMBER) should be in upper case
I have java method
void someMethod(String str, Map map) {
...
}
From JS call this method
var map = new Object()
map.key1 = "val1"
...someMethod(str, map)
Exception:
java.lang.NoSuchMethodException: None of the fixed arity signatures
[(java.lang.String, java.util.Map)] of method org.prjctor.shell.Bash.eval
match the argument types [java.lang.String, jdk.nashorn.internal.scripts.JO]
But in Nashorn docs "Mapping of Data Types Between Java and JavaScript" said "Every JavaScript object is also a java.util.Map so APIs receiving maps will receive them directly".
What am I doing wrong?
Agree with the previous answers that you cannot do this as the docs have implied.
However you could create and pass a map as follows
..
var HashMap = Java.type('java.util.HashMap');
var map = new HashMap();
map.put('1', 'val1');
...someMethod(str, map)
The doc says ""Every JavaScript object implements the java.util.Map interface". But this sample test program shows thats not the case.
public final class NashornTestMap {
public static void main(String args[]) throws Exception{
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine nashorn = factory.getEngineByName("nashorn");
nashorn.eval(""
+"load(\"nashorn:mozilla_compat.js\");"
+ "importClass(Packages.NashornTestMap);"
+ "var map={};"
+ "map[\"Key\"]=String(\"Val\"); "
+ "var test = new NashornTestMap();"
+ "test.test(map);"
+ "");
}
public void test(Map<String, String> obj){
System.out.println(obj);
}
}
The above code give exception "Exception in thread "main" java.lang.ClassCastException: Cannot cast jdk.nashorn.internal.scripts.JO4 to java.util.Map". This link confirms this.
However you can use Map inside your script and call the java objects directly, like this.
public final class NashornTestMap {
public static void main(String args[]) throws Exception{
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine nashorn = factory.getEngineByName("nashorn");
nashorn.eval(""
+"load(\"nashorn:mozilla_compat.js\");"
+ "importClass(Packages.NashornTestMap);"
+ "var HashMap = Java.type(\"java.util.HashMap\");"
+ "var map = new HashMap();"
+ "map.put(0, \"value1\");"
+ "var test = new NashornTestMap();"
+ "test.test(map);"
+ "");
}
public void test(Map<String, String> obj){
System.out.println(obj);
}
}
Returns "{0=value1}"
#Override
public List<String> getusers(String role) {
// TODO Auto-generated method stub
String namecount = "SELECT userName FROM users WHERE userName LIKE ?";
role="\"%" + role + "%\"";
List<String> names = jdbcTemplate.query("SELECT userName FROM users where userName like ?", new RowMapper() {
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString(1);
}
},role);
System.out.println(names);
return names;
}
I am not understanding why I am get this error , please can one say where it went wrong
Error message:
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT userName FROM users userName like ?]; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'like '%blabla%'' at line 1
You forget the WHERE keyword in the JdbcTemplate query.