Is it possible to pass dynamic param to #MapKey annotation ?? I have to dynamically choose the key of the table for constructing Table result in a Map.
I have tries passing the dynamic param same as we do for any sql query using ${} syntax.
Does not Work
#MapKey("${tablePk}")
#Select("${sql}")
Map doGenericOperation(#Param("sql") String sql, #Param("tablePk") String tablePk);
Works
#MapKey("id")
#Select("${sql}")
Map doGenericOperation(#Param("sql") String sql, #Param("tablePk") String tablePk);
Map Key does not get apply in this case. However if I pass it if like
#Map("id") works perfectly.
No, it is not possible.
As you pass the entire sql as a string, it should be possible to assign fixed alias to the primary key beforehand.
Also, it is fairly easy to achieve what you want without using #MapKey.
#Select("${sql}")
List<Map<String, ?>> doGenericOperation_Internal(#Param("sql") String sql);
default Map<?, Map<String, ?>> doGenericOperation(String sql, String tablePk) {
return doGenericOperation_Internal(sql).stream()
.collect(Collectors.toMap(e -> e.get(tablePk), e -> e));
}
Just be sure NOT to pass any user provided string as part of sql to avoid SQL injection!
Related
There is a table that has map type column, and the map column type would be like below
Map<String, CustomClass.class>
and CustomClass is like below
Class CustomClass {
String name;
String attr;
}
I would like to select record that match 'keyword' contain in map column's values (no matter what key is). I need something like below. Is there any way that I can use?
JooqQuery jooqQuery = (SelectJoinStep<?> step) -> {
step.where(MANAGERS.NAME_DESC_I18N_MAP.contains(
Map<"ANY KEY", keyword in CustomClass.name> // need help here
));
You can use LIKE as a quantified comparison predicate in jOOQ. If it's not supported natively by your RDBMS, jOOQ will emulate it for you. Try this:
MANAGERS.NAME_DESC_I18N_MAP.like(any(
map.values().stream().map(cc -> "%" + cc.name + "%").toArray(String[]::new)
))
You can't use contains() in this way yet, but I guess that's OK.
See also this blog post about using LIKE as a quantified comparison predicate.
I need to copy the records of one table to another table based on some condition.
String query = "insert into public.ticket_booking_archive select * from public.ticket_booking where ticketId in (:ticketIds)";
So here the :ticketIds are dynamic, where i need to pass ticketIds to make sure whether it satisfies the condition. So it may be the matching and non matching ticket id's here at runtime.
The values of ticketIds are something like this
('f1fa3a42-5837-11ec-bf63-0242ac130002','516fd14d-3c9d-4b4b-91a0-b684d8592dfe','c9652f86-734c-4df5-8ef9-d407cb3eaf7a','df7f2812-b445-45b4-b731-da23c36d7738','f1fa3a42-5837-11ec-bf63-0242ac130002'). And this is just an example. And the list might goes on.
Since it is of type UUID, I'm storing it into a Set<UUID>
Set<UUID> tktIds = new HashSet<UUID>();
for(int i=0 ; i<ticketIds.size(); i++) {
String ticketId = ticketIds[i];
tktIds.add(UUID.fromString(ticketId));
}
Map<String, Object> params = new HashMap<>();
params.put("ticketIds", tktIds);
SqlParameterSource namedParameters =
new MapSqlParameterSource().addValue("ticketIds",params.get("ticketIds"));
Since I'm using NamedParameterJdbcTemplate, so I'm using like below
int res = writeNamedJdbcTemplate.update(query, namedParameters);
res = 3 when executed programmatically.
Here the problem is, as soon as it finds the first matching value in the IN clause it executes. And it is not considering the other matching values (ticketIds here)
But if I execute the same query in pgadmin it works fine
insert into public.ticket_booking_archive select * from public.ticket_booking where ticketId in ('f1fa3a42-5837-11ec-bf63-0242ac130002','516fd14d-3c9d-4b4b-91a0-b684d8592dfe','c9652f86-734c-4df5-8ef9-d407cb3eaf7a','df7f2812-b445-45b4-b731-da23c36d7738','f1fa3a42-5837-11ec-bf63-0242ac130002');
result is 6. Working as expected.
writeNamedJdbcTemplate.queryForObject(query, namedParameters, Integer.class); //. throws an error
Can anyone please assist? I'm really not sure where I'm making a mistake
I am not quite sure whether you are using the appropriate JDBC template for the named parameters, but you can do the following:
you can consult this article to use the right template and employ proper SQL query composition,
for string passing you can wrap the parameter mapping as shown here
after all your named parameter should work
Spring Data Cassandra 1.5.0 comes with a streaming API in CassandraTemplate. I'm using spring-data-cassandra 1.5.1. I have a code like:
String tableName = cassandraTemplate.getTableName(MyEntity.class).toCql();
Select select = QueryBuilder.select()
.all()
.from(tableName);
// In real world, WHERE statement is much more complex
select.where(eq(ENTITY_FIELD_NAME, expectedField))
List<MyEntity> result = cassandraTemplate.select(select, MyEntity.class);
and want to replace this code with iterable or Java 8 Stream in order to avoid fetching a big list of results to memory at once.
What I'm looking for is a method signature like CassandraOperations.stream(Select query, Class<T> entityClass), but it is not available.
The only available method in CassandraOperations accepts query string: stream(String query, Class<T> entityClass). I tried to pass here a string generated by Select like
cassandraTemplate.stream(select.getQueryString(), MyEntity.class)
But that fails with InvalidQueryException: Invalid amount of bind variables, because getQueryString() returns query with question mark placeholders instead of variables.
I see 3 options to get what I want, but every option looks bad:
Use Spring Query creation mechanism with Stream/Iterator expected return type (good only for simple queries) http://docs.spring.io/spring-data/cassandra/docs/current/reference/html/#repositories.query-methods.query-creation
Use raw CQL query and not to use QueryBuilder
Call select.getQueryString() and then substitute parameters again via BoundStatement
Is there any better way to stream selection results?
Thanks.
So, as of now the answer on my question is to wait until stable version of spring-data-cassandra 2.0.0 comes out:
https://github.com/spring-projects/spring-data-cassandra/blob/2.0.x/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java#L208
I am using hibernate to save records (i.e. objects) to a database. Before saving my objects, I want to verify if the database already contains this object. (The primary key is just an incremental key and cannot be used for this.)
I am creating a HQL statement at runtime to check the existance of a record with these attributes (i.e. column1-3).
The resulting query should look like:
from myTable where column1 is null and column2 = :column2 and column3 = :column3'
Because sometimes the columns can contain null values, I check the value of the attributes, if it is a NULL value, then I use a is instead of a = in this query (e.g. the column1 is :column1 in the above statement).
Because I start to realize that I am doing a lot of work to achieve something reletively crucial, I am starting to wonder if I'm on the right track. Is there an easier way to check the existance of objects ?
EDIT: I slightly rephrased my question after I realized that also column1 is :column1 does not work when :column1 parameter is set to null. Apparently the only syntax that seems to work as expected is column1 is null. So, it seems like you just cannot use wildcards when searching for null values. But that does not change the main aspect of my question: should I really be checking all this stuff at runtime ?
This is the best way that I found so far.
I prefer to put my filters in a map. The key refers to the property (i.e. map.put("column1", Integer.valueOf(1))).
There is a Restritions.eqOrIsNull method that simplifies the conversion to a Criterion object. The following method converts an entire Map to a List<Criterion>.
public List<Criterion> mapToCriterion(Map<String, Object> params)
{
if (params == null) return null;
// convert the parameter map to a list of criterion
List<Criterion> criterionList = new ArrayList<>(params.size());
for (Map.Entry<String, Object> entry : params.entrySet())
criterionList.add(Restrictions.eqOrIsNull(entry.getKey(), entry.getValue()));
return criterionList;
}
Later on, I use the List<Criterion> to build a Criteria object.
Criteria criteria = session.createCriteria(clazz);
if (criterionList != null)
{
for(Criterion criterion : criterionList)
criteria.add(criterion);
}
// get the full list
#SuppressWarnings("unchecked")
List<T> objectList = criteria.list();
My general impression is still that there are missing several convenience methods here (e.g. Criteria#addAll(List<Criterion>) would have been nice).
In HttpServletRequest, getParameterMap returns a Map of all query string parameters and post data parameters.
Is there a way to get a Map of ONLY query string parameters? I'm trying to avoid using getQueryString and parsing out the values.
You can use request.getQueryString(),if the query string is like
username=james&password=pwd
To get name you can do this
request.getParameter("username");
Contrary to what cularis said there can be both in the parameter map.
The best way I see is to proxy the parameterMap and for each parameter retrieval check if queryString contains "&?<parameterName>=".
Note that parameterName needs to be URL encoded before this check can be made, as Qerub pointed out.
That saves you the parsing and still gives you only URL parameters.
The servlet API lacks this feature because it was created in a time when many believed that the query string and the message body was just two different ways of sending parameters, not realizing that the purposes of the parameters are fundamentally different.
The query string parameters ?foo=bar are a part of the URL because they are involved in identifying a resource (which could be a collection of many resources), like "all persons aged 42":
GET /persons?age=42
The message body parameters in POST or PUT are there to express a modification to the target resource(s). Fx setting a value to the attribute "hair":
PUT /persons?age=42
hair=grey
So it is definitely RESTful to use both query parameters and body parameters at the same time, separated so that you can use them for different purposes. The feature is definitely missing in the Java servlet API.
As the other answers state there is no way getting query string parameters using servlet api.
So, I think the best way to get query parameters is parsing the query string yourself. ( It is more complicated iterating over parameters and checking if query string contains the parameter)
I wrote below code to get query string parameters. Using apache StringUtils and ArrayUtils which supports CSV separated query param values as well.
Example: username=james&username=smith&password=pwd1,pwd2 will return
password : [pwd1, pwd2] (length = 2)
username : [james, smith] (length = 2)
public static Map<String, String[]> getQueryParameters(HttpServletRequest request) throws UnsupportedEncodingException {
Map<String, String[]> queryParameters = new HashMap<>();
String queryString = request.getQueryString();
if (StringUtils.isNotEmpty(queryString)) {
queryString = URLDecoder.decode(queryString, StandardCharsets.UTF_8.toString());
String[] parameters = queryString.split("&");
for (String parameter : parameters) {
String[] keyValuePair = parameter.split("=");
String[] values = queryParameters.get(keyValuePair[0]);
//length is one if no value is available.
values = keyValuePair.length == 1 ? ArrayUtils.add(values, "") :
ArrayUtils.addAll(values, keyValuePair[1].split(",")); //handles CSV separated query param values.
queryParameters.put(keyValuePair[0], values);
}
}
return queryParameters;
}
Java 8
return Collections.list(httpServletRequest.getParameterNames())
.stream()
.collect(Collectors.toMap(parameterName -> parameterName, httpServletRequest::getParameterValues));
I am afraid there is no way to get the query string parameters parsed separately from the post parameters. BTW the fact that such API absent may mean that probably you should check your design. Why are you using query string when sending POST? If you really want to send more data into URL use REST-like convention, e.g. instead of sending
http://mycompany.com/myapp/myservlet?first=11&second=22
say:
http://mycompany.com/myapp/myservlet/11/22