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.
Related
I want to design an update function for my DynamoDB table that only updates the record when the hash key and range key pair is in the DynamoDB table. If the item doesn't exist, I would like to throw a badRequestError. How can I use a DynamoDB expression to achieve this? Can I set both the expectedAttribute to true and set the conditional operator to AND?
If you are using DynamoDBMapper you could use DynamoDBSaveExpression
Something like:
Map<String, ExpectedAttributeValue> expected =
Map.of("hashKeyAttrName", new ExpectedAttributeValue().withExists(true),
"rangeKeyAttrName", new ExpectedAttributeValue().withExists(true));
DynamoDBSaveExpression expression = new DynamoDBSaveExpression();
expression.setExpected(expected);
dynamoDBMapper.save(item, expression);
If condition is not met save will trow a ConditionalCheckFailedException
My rep is too low to comment ccamcmxc's answer, so I'll just give a new answer.
The additional thing you need is to provide AttributeValue for the ExpectedAttributeValue call to make the whole thing work. The complete code sample looks like below.
Map<String, ExpectedAttributeValue> expected = Map.of(
"hashKeyAttrName", new ExpectedAttributeValue(new AttributeValue(hashKey))
.withExists(true),
"rangeKeyAttrName", new ExpectedAttributeValue(new AttributeValue(rangeKey))
.withExists(true));
DynamoDBSaveExpression expression = new DynamoDBSaveExpression();
expression.setExpected(expected);
dynamoDBMapper.save(item, expression);
To add a little flavor to the recipe, the save method has an overload that supports you to also provide SaveBehavior as the third parameter which gives you even more control.
An example:
dynamoDBMapper.save(item, expression, UPDATE_SKIP_NULL_ATTRIBUTES.config());
Using the JOOQ parser API, I'm able to parse the following query and get the parameters map from the resulting Query object. From this, I can tell that there is one parameter, and it's name is "something".
However, I haven't been able to figure out how to determine that the parameter "something" is assigned to a column named "BAZ" and that column is part of the table "BAR".
Does the parser API have a way to get the table/column metadata associated to each parameter?
String sql = "SELECT A.FOO FROM BAR A WHERE A.BAZ = :something";
DSLContext context = DSL.using...
Parser parser = context.parser();
Query query = parser.parseQuery(sql);
Map<String, Param<?>> params = query.getParams();
Starting from jOOQ 3.16
jOOQ 3.16 introduced a new, experimental (as of 3.16) query object model API, which can be traversed, see:
The manual
A blog post about traversing jOOQ expression trees
Specifically, you can write:
List<QueryPart> parts = query.$traverse(
Traversers.findingAll(q -> q instanceof Param)
);
Or, to conveniently produce exactly the type you wanted:
Map<String, Param<?>> params = query.$traverse(Traversers.collecting(
Collectors.filtering(q -> q instanceof Param,
Collectors.toMap(
q -> ((Param<?>) q).getParamName(),
q -> (Param<?>) q
)
)
));
The Collectors.toMap() call could include a mergeFunction, in case you have the same param name twice.
Pre jOOQ 3.16
As of jOOQ 3.11, the SPI that can be used to access the internal expression tree is the VisitListener SPI, which you have to attach to your context.configuration() prior to parsing. It will then be invoked whenever you traverse that expression tree, e.g. on your query.getParams() call.
However, there's quite a bit of manual plumbing that needs to be done. For example, the VisitListener will only see A.BAZ as a column reference without knowing directly that A is the renamed table BAR. You will have to keep track of such renaming yourself when you visit the BAR A expression.
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!
I am using the AWS Java SDK for communicating with DynamoDB. I am trying to do a table update of some properties stored in a map.
Before the update, I have an object that looks like this:
{
"myMap": {
"innerMap": {}
},
"hashKeyName": "hashKeyValue"
}
My code looks like this:
Table myTable = ...;
myTable.updateItem("hashKeyName", "hashKeyValue",
new AttributeUpdate("myMap.innerMap.myKey").addNumeric(100));
After this update, my Dynamo object looks like this (notice that the map is still empty):
{
"myMap": {
"innerMap": {}
},
"myMap.innerMap.myKey": 100,
"hashKeyName": "hashKeyValue"
}
Why was myMap.innerMap.myKey added as a separate field instead of being correctly set in the map?
The reason for this is because using AttributeUpdate is considered a legacy operation and the usage of map keys as attribute names is not supported. The correct usage to use an update expression:
myTable.updateItem("hashKeyName", "hashKeyValue",
"ADD myMap.innerMap.myKey :val", null /* NameMap, see comment */,
new ValueMap().withNumeric(":val", 100));
Notice that there is no name map in the above expression. One might be tempted to do use ADD #name :val as an update expression and then provide a name map for #name => myMap.innerMap.myKey. In this case, these expressions are not equivalent. When a . appears in the raw expression, it is treated as a path separator. When . appears in a NameMap value, it is not considered a path separator.
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).