Conditional IN from DynamoDb not working on java - spring

I am working with DynamoDB with Spring Boot 2.1, and I'm facing an error when I need o user the clause IN during the conditional evaluation. Even with lines that fulfill the requirements, the query result is empty.
How can I return the lines from the table after explicit the result within the IN clause ?
public class DynamoRepository {
private final DynamoDBMapper dynamoDBMapper;
public Optional<List<USER>> query(String id) {
Map<String, String> ean = new HashMap<>();
ean.put("#status", "status");
Map<String, AttributeValue> eav = new HashMap<>();
eav.put(":id", new AttributeValue().withS(documento));
DynamoDBQueryExpression<USER> queryExpression = new DynamoDBQueryExpression<USER>()
.withKeyConditionExpression("id = :id")
.withFilterExpression("#status in (ACTIVE, PENDING)")
.withExpressionAttributeNames(ean)
.withExpressionAttributeValues(eav);
List<USER> query = dynamoDBMapper.query(USER.class, queryExpression);
return query.isEmpty() ? Optional.empty() : Optional.of(query);
}
}

After taking a while, my solution was to define the status' values as Expression Attribute Values like the code below
public class DynamoRepository {
private final DynamoDBMapper dynamoDBMapper;
public Optional<List<USER>> query(String id) {
Map<String, String> ean = new HashMap<>();
ean.put("#status", "status");
Map<String, AttributeValue> eav = new HashMap<>();
eav.put(":id", new AttributeValue().withS(documento));
eav.put(":active", new AttributeValue().withS("ACTIVE"));
eav.put(":pending", new AttributeValue().withS("PENDING"));
DynamoDBQueryExpression<USER> queryExpression = new DynamoDBQueryExpression<USER>()
.withKeyConditionExpression("id = :id")
.withFilterExpression("#status in (:active, :pending)")
.withExpressionAttributeNames(ean)
.withExpressionAttributeValues(eav);
List<USER> query = dynamoDBMapper.query(USER.class, queryExpression);
return query.isEmpty() ? Optional.empty() : Optional.of(query);
}
}

Related

Convert to simplejdbccall resultset to java objects

I am calling DB2 procedure which takes a input parameter and returns a resultset.
How can i map the O/P to my pojo class.
I have to map the result to nexted pojo classes.
simpleJdbcCall = new SimpleJdbcCall(jdbctemplate)
.withSchemaName("myschema")
.withProcedureName("DB2-PROC")
.declareParameters(
new SqlParameter("1", Types.VARCHAR)
);
Map<String, Object> map = simpleJdbcCall.execute("2020-01-01");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("Entry value is " + entry.getValue() );
}
//my o/p
Entry value is [{Col_1=abc, col_2=abc,col_2=xyz, col_2=abc},....];
you can use returningResultSet(parameterName, rowMapper) method to map values to object.
Here some reference code:
SimpleJdbcCall procedureActor = new SimpleJdbcCall(dataSource)
.withSchemaName("myschema")
.withProcedureName("DB2-PROC")
.declareParameters(
new SqlParameter("1", Types.VARCHAR))
.returningResultSet("mapObjRefrence", new RowMapper<Contact>() {
#Override
public Contact mapRow(ResultSet rs, int rowNum) throws SQLException {
YourPojo pojo = new YourPojo();
pojo.setId(rs.getInt("col_1"));
pojo.setName(rs.getString("col_2"));
pojo.setEmail(rs.getString("col_2"));
pojo.setAddress(rs.getString("col_3"));
pojo.setTelephone(rs.getString("col_4"));
return contact;
}
});
Map<String, Object> out = procedureActor.execute("2020-01-01");
List<YourPojo> listPojos = (List<YourPojo>) out.get("mapObjRefrence");
Also you can check for multi table results: How to get multi table results using SimpleJDBCCall in spring?

Consider to dec lare/implement a mapping method: "java.lang.String map(java.lang.Object value)" during to use the Framework MapStruct

#Mapper(componentModel = "spring")
public interface MapperThings {
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<String, String> toDto(Map<Object, Object> mapEntity);
List<Map <String, String>> toListDto(Collection<Map<Object, Object>> listEntity);
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<Object, Object> toEntity(Map<String, String> mapDto);
List<Map<Object, Object> > toListEntity(Collection<Map<String, String>> listDto);
}
There is to generate without mistakes only :
#MapMapping(keyTargetType = Object.class, valueTargetType = Object.class)
Map<Object, Object> toEntity(Map<String, String> mapDto);
List<Map<Object, Object> > toListEntity(Collection<Map<String, String>> listDto);
I found temporary decision. But I would want to use the annotation #MapMapping.
#Mapper(componentModel = "spring")
public abstract class MapperMoviesAbstract {
public Map<String, String> toDto(Map<Object, Object> mapEntity) {
if(mapEntity == null) return null;
Map<String, String> stringMap = new HashMap<>();
for(Map.Entry<Object, Object> entry : mapEntity.entrySet()){
String key = (String) entry.getKey();
stringMap.put(key, mapEntity.get(key).toString());
}
return stringMap;
}
public abstract List< Map<String, String>> toListDto(Collection<Map<Object, Object>> listEntity);
}
According to the MapStruct documentation, using the #MapMapping annotation should generate a class that will perform the conversion.
But I get an error:
Can't map map key "java.lang.Object" to "java.lang.String ". Consider
to dec lare/implement a mapping method: "java.lang.String
map(java.lang.Object value)".
Do anyone have any ideas to do what?
The error message is telling you what to do. You need to provide a way to map Object into a String.
So you'll need a custom method like:
public String objectToString(Object object) {
// Your custom implementation
}

How to get the type of values from a JDBC query?

I have a spring batch job that takes in a user query, executes that query to find the selected items, and then I want to insert those items into another database. The problem is that I have to convert elements like dates from the resulting query to insert them again. How can I tell the type of the values returned from the query?
This is what I use to read the items which works properly.
#Bean("querySelectiveItems")
#StepScope
public JdbcCursorItemReader querySelectiveItems(#Qualifier("selectiveSourceDatabase") DataSource dataSource,
#Value("#{jobExecutionContext[" + EtlConfiguration.JOB_PARM_MIGRATION_CONFIG + "]}") MigrationDefinition migrationDefinition
) {
JdbcCursorItemReader reader = new JdbcCursorItemReader<>();
reader.setSql(migrationDefinition.getMigrations().getTable().getQuery());
reader.setDataSource(dataSource);
reader.setRowMapper(new ColumnMapRowMapper());
log.info("Queried for items");
return reader;
}
The following is what I wrote to write to the destination database. The problem is that the values I have to insert are unknown because they are the result of a user query. For example if there is a datatype in my insert statement I must have a TO_DATE around the date value. Is there a way to do this?
#Component
#Lazy
class InsertSelectedItems implements ItemWriter<Map<String, Object>> {
private MigrationDefinition migrationDefinition;
private JdbcTemplate destinationTemplate;
public void setDestinationTemplate(JdbcTemplate destinationTemplate) {
this.destinationTemplate = destinationTemplate;
}
public void setMigrationDefinition(MigrationDefinition migrationDefinition) {
this.migrationDefinition = migrationDefinition;
}
#Override
public void write(List<? extends Map<String, Object>> items) throws Exception {
ArrayList<String> columns = new ArrayList<>();
ArrayList<String> values = new ArrayList<>();
System.out.println(items);
for (Map<String, Object> map : items) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
columns.add(entry.getKey());
values.add(String.valueOf(entry.getValue()));
}
}
String sql = String.format("%s ( %s ) VALUES ( %s ) ",
migrationDefinition.getMigrations().getTable().getInsert(),
String.join(",", columns),
String.join(",", values));
log.info(sql);
destinationTemplate.update(sql);
}
}

spring-data-elasticsearch and update

i'm using spring-data-elasticsearch to do CRUD operations.
I have a custom Repository that extends ElasticsearchRepository.
Ultimately ElasticsearchRepository extends CrudRepository which implies updating an existing record is possible.
The question is, how do you accomplish this? I haven't found a method called "update()"
I thought doing the following would work (code stolen from https://github.com/BioMedCentralLtd/spring-data-elasticsearch-sample-application)
//create
Book book = new Book();
book.setId("123455");
book.setName("Spring Data Elasticsearch");
book.setVersion(System.currentTimeMillis());
repository.save(book);
//update
book.setName("THIS IS A COMPLETELY NEW TITLE");
repository.save(book);
However the 2nd save throws an InvocationTargetException
Examining it with the debugger shows:
[book][0] [book][123455]: version conflict, current [1447792071681], provided [1447792071681]
The Book object looks like:
#Document(indexName = "book",type = "book" , shards = 1, replicas = 0, indexStoreType = "memory", refreshInterval = "-1")
public class Book {
#Id
private String id;
private String name;
private Long price;
#Version
private Long version;
public Map<Integer, Collection<String>> getBuckets() {
return buckets;
}
public void setBuckets(Map<Integer, Collection<String>> buckets) {
this.buckets = buckets;
}
#Field(type = FieldType.Nested)
private Map<Integer, Collection<String>> buckets = new HashMap();
public Book(){}
public Book(String id, String name,Long version) {
this.id = id;
this.name = name;
this.version = version;
}
getters and setters removed for space
}
My Repository code is even simpler:
import org.springframework.data.elasticsearch.entities.Book;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface BookRepository extends ElasticsearchRepository<Book, Long> {
}
Do I have to provide an update method?
EDIT:
Nevermind. I changed the update to:
//update
book.setName("THIS IS A COMPLETELY NEW TITLE");
book.setVersion(System.currentTimeMillis());
repository.save(book);
and it updated the record.
You can use UpdateQuery and ElasticSearchTemplate to update any partial document. e.g
final UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index(mainIndexName);
updateRequest.type(typeName);
updateRequest.id(id);
updateRequest.doc(XContentFactory.jsonBuilder().startObject()
.field("accountType", accountType)
.endObject());
final UpdateQuery updateQuery = new UpdateQueryBuilder().withId(id)
.withClass(<DocumentClass>).withUpdateRequest(updateRequest).build();
UpdateResponse updateResponse = elasticSearchTemplate.update(updateQuery);
I updated indexed document as follows code snippet:
IndexRequest indexRequest = new IndexRequest(INDEX_NAME,INDEX_NAME,docid);
indexRequest.source(fldName, fldValue);
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index(INDEX_NAME);
updateRequest.type(INDEX_NAME);
updateRequest.id(docid);
updateRequest.doc(indexRequest);
try {
UpdateResponse res=client.update(updateRequest).get();
logger.info("update es {}:{}",fe,res.getGetResult());
} catch (Exception e) {
logger.error("update",e);
throw e;
}
The second update fails because you're trying to update an entity whose version hasn't changed. The error message you're getting is ES telling you, "hey, you can't save the same version twice!" Try this:
//create
Book book = new Book();
book.setId("123455");
book.setName("Spring Data Elasticsearch");
book.setVersion(System.currentTimeMillis());
repository.save(book);
//update
book.setName("THIS IS A COMPLETELY NEW TITLE");
book.setVersion(System.currentTimeMillis()); // you're saving a new version
repository.save(book);
I think the ElasticSearch is a similar to the JSON storage:
if(exist) {
update it;// push json to cover it
} else {
add it;// new save();
}
It will update the JSON when Id/Entity is exist, or it will add it.
XContentType contentType =
org.elasticsearch.client.Requests.INDEX_CONTENT_TYPE;
public XContentBuilder getBuilder(User assign){
try {
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
builder.startObject();
Map<String,?> assignMap=objectMap.convertValue(assign, Map.class);
builder.field("assignee",assignMap);
return builder;
} catch (IOException e) {
log.error("custom field index",e);
}
IndexRequest indexRequest = new IndexRequest();
indexRequest.source(getBuilder(assign));
UpdateQuery updateQuery = new UpdateQueryBuilder()
.withType(<IndexType>)
.withIndexName(<IndexName>)
.withId(String.valueOf(id))
.withClass(<IndexClass>)
.withIndexRequest(indexRequest)
.build();

how to do edit in spring mvc using only 2 jsp pages

My code below,
#RequestMapping("/employee/edit")
public ModelAndView editEmp(#RequestParam int emp_Id, #RequestParam boolean flag,
#ModelAttribute Employee emp) {
emp = empService.getEmp(emp_Id);
List<String> cityList = new ArrayList<String>();
cityList.add("Hyderabad");
cityList.add("Secunderabad");
Map<String, Object> map = new HashMap<String, Object>();
map.put("cityList", cityList);
map.put("emp", emp);
return new ModelAndView("employee", "map", map);
}

Resources