I want to use DynamoDBMapper to query a table, this table has a global secondary index. And, I wish to query the global secondary index. So, I have a class corresponding to each item in the table. And, the field which is the Hash key in the global secondary index is annotated as following
#DynamoDBIndexHashKey(globalSecondaryIndexName="Index-Name", attributeName = "EmailSent")
public String getEmailSent() {
return emailSent;
}
And, I am querying using the mapper as shown below
public <T extends Object> List<T> queryGlobalIndex(final String tableName, final String indexName, final T inputObj) {
final Class<T> clazz = (Class<T>) inputObj.getClass();
DynamoDBQueryExpression<T> queryExpression = new DynamoDBQueryExpression<T>().withIndexName(indexName).withConsistentRead(false).withHashKeyValues(inputObj);
return mapper.query(clazz, queryExpression, new DynamoDBMapperConfig(
new TableNameOverride(tableName)));
}
This is working, my quest is that I want to remove the field globalSecondaryIndexName from my annotation #DynamoDBIndexHashKey on the field. Any inputs on how to go about it?
Related
I have created method where I can pass SQL string, arguments and DTO class.
public <T> List selectObjectList(String sql, Object[] args, Class<T> dto) {
return jdbcTemplate.query(
sql,
args,
new BeanPropertyRowMapper(dto)
);
}
So my DTO is
#Data
#NoArgsConstructor
public class SimpleDto{
private Integer id;
private Date createDate;
...
private ArrayList<String> recipents;
private ArrayList<Integer> objects;
...
}
Then I pass SQL "select * from simple_dto n where n.create_date >= now()", no argsuments and
ArrayList<Notification> notifications =
(ArrayList<Notification>) comDao.selectObjectList(sql, args, SimpleDto.class);
And I get exception:
Caused by: org.springframework.beans.ConversionNotSupportedException:
Failed to convert property value of type 'org.postgresql.jdbc.PgArray'
to required type 'java.util.ArrayList' for property 'recipents';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type 'org.postgresql.jdbc.PgArray' to required type
'java.util.ArrayList' for property 'recipents': no matching editors or
conversion strategy found
SQL script to create simple_dto table:
create table notification
(
id SERIAL PRIMARY KEY,
create_date timestamp not null,
recipents varchar ARRAY,
objects integer ARRAY
);
I know that problem in array. But how can I solve it ?
UPD:
Main purpose to make DRY code. BeanPropertyRowMapper really cant deserialize arrays to list.
But here method where you can put BeanPropertyRowMapper(DTO.class) and custom RowMapper both.
#Override
public <T> List<T> selectObjectList(String sql, Object[] args, RowMapper<T> mapper) {
return jdbcTemplate.query(
sql,
args,
mapper
);
}
BeanPropertyRowMapper cannot map java.sql.Array to ArrayList
Create a RowMapper (implement also mapping of 'objects' like i did for "recipents" And also 'create_date')
public class NotificationMapper implements RowMapper<Notification> {
#Override
public Notification mapRow(ResultSet rs, int rowNum) throws SQLException {
Notification employee = new Notification();
employee.setId(rs.getInt("id"));
String[] arr = (String[])rs.getArray("recipents").getArray();
Collections.addAll(employee.getRecipents(), arr);
return employee;
}
}
And use it to map result set. (You can remove 'dto' param.)
jdbcTemplate.query(
sql,
args,
new NotificationMapper()
);
Note that your lists from the pojo need to be initialized
public class SimpleDto{
private Integer id;
private Date createDate;
...
private ArrayList<String> recipents = new ArrayList<>();
private ArrayList<Integer> objects = new ArrayList<>();
...
}
I'm trying to use querydsl and projection to map the results of a query onto an object.
I have something like the following:
public record DataTransferObject(MyTable obj1, MyTable obj2) {
public static final ConstructorExpression<DataTransferObject> PROJECTION =
Projections.constructor(obj1, obj2);
}
#Service
public class QueryService {
private final JPQLQueryFactory queryFactory;
public DataTransferObject getData(String id1, String id2) {
return queryFactory.select(DataTransferObject.PROJECTION)
.from(QMyTable.myTable)
.where(QMyTable.myTable.id.eq(id1)
.or(QMyTable.myTable.id.eq(id2))
.fetchOne();
}
}
But this doesn't work as I run into com.querydsl.core.NonUniqueResultException.
Using joins like in the following results in obj1 and obj2 being the same object (even though the 2 ids map to 2 unique rows):
.leftJoin(QMyTable.myTable)
.on(QMyTable.myTable.id.eq(id1))
.leftJoin(QMyTable.myTable)
.on(QMyTable.myTable.id.eq(id2))
I want to match DB row corresponding to String id1 to the MyTable obj1 field in the DataTransferObject object. Similarly, I want to match DB row corresponding to String id2 to the MyTable obj2 field in the DataTransferObject object.
What's a preferable/best way to accomplish this?
Try changing fetchOne() to fetchFirst() or fetchAll()
Looks like I preemptively defining the objects worked. It's working with something like:
#Service
public class QueryService {
private static final QMyTable OBJ1 = new QMyTable("obj1");
private static final QMyTable OBJ2 = new QMyTable("obj2");
public static final ConstructorExpression<DataTransferObject> PROJECTION = Projections.constructor(
MyTable.projection(OBJ1),
MyTable.projection(OBJ2)
);
private final JPQLQueryFactory queryFactory;
public DataTransferObject getData(String id1, String id2) {
return queryFactory.select(PROJECTION)
.from(QMyTable.myTable)
.leftJoin(OBJ1)
.on(OBJ1.id.eq(id1))
.leftJoin(OBJ2)
.on(OBJ2.id.eq(id2))
.fetchOne();
}
}
Left some stuff out, but that's the gist.
I am trying to create a map from all the attributes that a class have.My class looks like :
public class MyInventory
{
private int tiers = 80;
private int stearing =135;
private int battery = 46;
}
Now I have collected all the methods that the class has as :
Field[] fields = this.getClass().getDeclaredFields();
Now , I am trying to create a Map out of it where keys are the values of the fields and the values are the name of the fields. Example :
Map<46,battery> ...etc
Is there a way to do it?
The attribute values for the above mentioned class were generated by mapping to properties file and by using spring annotation #ConfigurationProperties. Now I need to create the Map but keys the values of the attributes. I tried to use reflect. However did not find a way to get the value of the fields.
Thanks
You can use Introspector class.
public Map<Object, String> populateMap(final Object o) throws Exception {
Map<Object, String> result = new HashMap<>();
for (PropertyDescriptor pd : Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors()) {
String fieldName = pd.getName();
if("class".equals(fieldName) continue;
Object value = pd.getReadMethod().invoke(o);
result.put(value, fieldName);
}
return result;
}
You can call the above method, passing your class as argument.
MyInventory mi = new MyInventory();
// Sets the properties of mi
mi.setXXX...
// Populates map
populateMap(mi);
Map<Integer, String> map() throws IllegalArgumentException, IllegalAccessException {
Field[] fields = getClass().getDeclaredFields();
Map<Integer,String> map = new HashMap<>();
for (Field field : fields) {
map.put(field.getInt(this), field.getName());
}
return map;
}
Of course it will not map properly if different fields have the same value.
I think, you can have getter method in your class
public class MyInventory
{
private int tiers = 80;
private int stearing =135;
private int battery = 46;
public int getBattery()
{
return battery;
}
//and other getter
}
and then you can populate your map as
map.put(inventory.getBattery(),"battery");
Because, when you have value, which means you know what is the type for which you are populating map.
You can use json parser. For example jackson:
import com.fasterxml.jackson.databind.ObjectMapper;
...
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(mapper.writeValueAsString(fooOject), HashMap.class);
I am fetching attributes of an object as List, where AvsAttribute is as follows:
class AvsAttribute {
String attrName,
String value
}
I am fetching the attribute values for any entity as follows, as I did not want to use reflection, that's why did like that:
#Override
public List<AvsAttribute> getAttributes(List<String> attributeNameList, final #NonNull T entity)
throws JsonProcessingException, JSONException {
List<AvsAttribute> attributeList = new ArrayList<>();
objectMapper.setSerializationInclusion(Include.NON_NULL);
String jsonString = objectMapper.writeValueAsString(entity);
JSONObject jsonObject = new JSONObject(jsonString);
Iterator<String> keysItr = jsonObject.keys();
while (keysItr.hasNext()) {
String key = keysItr.next();
String value = jsonObject.get(key).toString().trim();
attributeList.add(AvsAttribute.builder().name(key).value(value).build());
}
if (CollectionUtils.isNotEmpty(attributeNameList)) {
return attributeList.stream().filter(item -> attributeNameList.contains(item.getName()))
.collect(Collectors.toList());
}
return attributeList;
}
But I want to make AvsAttribute generic like below:
class AvsAttribute<T> {
String attrName,
T value
}
But I cannot figure out what change should I do to the above getAttributes() function so that it works with above generic class.
I think it already works with a generic type parameter. You need to setup the .value(String value) function in AvsAttribute builder to safely parse a given string into type T.
Then, you can rest assured that the following line will correctly fetch the generic attribute value:
AvsAttribute.builder().name(key).value(value).build()
Does that make sense?
Anyway, Good luck with your code.
Use wildcard ? in attributeList declaration and return type.
We have
String sql = "SELECT * FROM user WHERE id = '" +userId +"'";
List<Object> userList = template.queryForList(sql);
The query shall return the different users with the given id.
The template is an object of JdbcTemplate class.
Here the query returns a List of Map<String,Object> and the left hand side is List<Object>. Is there any way to do the conversion. This is part of the spring transaction management example.
You can always use only the values which you are interested in the first place like:
List<Object> userList = new ArrayList<Object>(template.queryForList(sql).values());
In case it returns List<Map<String, Object>> instead of Map<String,Object>:
List < Object > userList = new ArrayList < Object > ();
for (Map < String, Object > obj: template.queryForList(sql)) {
userList.addAll(obj.values());
}
This should give you a list of Object as you need. If you need to explore why Map.values() cannot be directly casted to list and we need to create a new ArrayList for that purpose can be found here: why HashMap Values are not cast in List
I think you should take another approach. Spring can not build objects from the data returned from the database, but you can use a RowMapper to construct user objects. Additionally you should not build the query on your own but use PreparedStatements. Otherwise you might be vulnerable to SQL injection.
Here is a example on how to do it:
template.query("SELECT * FROM user WHERE id = ?", new Object[] { userId }, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) {
// Build a user from the current row and return it
}
});
I think you have confused it a lot. The best way to do it is indeed what is suggested by Steffen Kreutz. Consider the following example:
User Table in DB has following fields:
1. UserID -> Type: Int
2. User_Name -> Type: Varchar
3. User_Contact -> Type: Varchar
...
Now, you can simple write a RowMapper to map all these fields to your custom POJO Object like follows:
POJO Class:
public class User{
private int userId;
private String userName;
private String userContact;
public int getUserId() {
return this.userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
/* Other Getter-Setter(s) */
}
Query String:
private static final String SELECT_QUERY = "SELECT * FROM user WHERE id = ?";
JdbcTemplate Call:
Here you are passing userId for ?. JdbcTemplate will automatically take care of it.
List<User> = (List<User>) jdbcTemplate.query(SELECT_QUERY, new Object[] { userId }, new UserRowMapper());
Finally the UserRowMapper Class:
class UserRowMapper implements RowMapper {
#Override
public Object mapRow(ResultSet resultSet, int row) throws SQLException {
User user = new User();
user.setUserId(resultSet.getInt("UserID"));
user.setUserName(resultSet.getString("User_Name"));
user.setUserContact(resultSet.getString("User_Contact"));
return user;
}
}
This is indeed the best and recommended way of using JdbcTemplate.