How to map multiple data rows to different fields - java

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.

Related

How to reduce all the fields of object in java

I have a Pojo class with all numeric fields
public class Pojo {
private long field1;
private long field2;
private long field3;
private long field4;
private long field5;
private double field6;
private double field7;
private double field8;
private double field9;
}
And there is a list of Pojo, I want map this list to one Pojo object which will contain in its field the of pojos foe, list. I mean someething like this :
public static void main(String[] args) {
List<Pojo> pojoList = getPogoList();
Pojo pojoInSum = reduceAllFields(pojoList);
}
What is the simplest way to reduce all fields of pojos from list without reflection?
You could use the Stream#reduce method:
public void reducePojoList() {
PojoReduce pojoReduce = new PojoReduce();
List<Pojo> pojoList = Arrays.asList(
new Pojo(3L, 4.0),
new Pojo(6L, 1.1));
Optional<Pojo> reducedPojo = pojoList.stream().reduce(this::combine);
reducedPojo.ifPresent(System.out::println);
}
private Pojo combine(Pojo pojo1, Pojo pojo2) {
return new Pojo(
pojo1.getLongField() + pojo2.getLongField(),
pojo1.getDoubleField() + pojo2.getDoubleField()
);
}
You would have to update the combine method for every field you add though. You'd also be creating a lot of new objects.
The simplest way is to write a method in that pojo. Because if you are modeling a thing in a class you should expose behavior and not data.
But I doubt that is what you are looking for so you might want to look at reflection.
Basically you retrieve all the fields of a class, get the values for the instance and then sum them in a loop or stream.

Java 8 - Map between class to one of its function

I have multiple types of objects, I'd like to generalise the 'id' of the objects in a way that will dynamically change what field is selected as the id.
Example
public class ObjectA{
//Attribute name attA
private String attA;
.... More attributes
public String getAttA(){
return attA
}
.....More getters/setters
}
public class ObjectB{
//Attribute named attB
private String attB;
.... More attributes
public String getAttB(){
return attB
}
.... More getters and setters
}
Id like to be able to run something like this:
Map<????, ????> customIdMap = new HashMap<>();
//We decide that ObjectA main attribute is AttA
customIdMap.add(ObjectA.class, ObjectA::getAttA);
//We decide that ObjectB main attribute is AttB
customIdMap.add(ObjectB.class, ObjectB::getAttB);
Then I'll be able to have a list of general objects and ill be able to retrieve their ids from the map if it is a known object with:
public String getCustomId(Object object){
if(customIdMap.contains(object.getClass()){
//Parameters are messed up, but this is the general idea of how
//i thought this would look
return customIdMap.get(object.getClass()).apply(object);
}
}
The code above does not run since getAttA is a call to a none static method in a static context so i assume this maybe should be wrapped in some kind of generic object.
Can it be done?
Preferably you change ObjectA and ObjectB to have a common interface. If that's not possible you can put them into a map like this:
Map<Class<? extends Object>, Function<Object, String>> map = new HashMap<>();
map.put(ObjectA.class, a -> ((ObjectA) a).getAttA());
map.put(ObjectB.class, b -> ((ObjectB) b).getAttB());
EDIT:
Or if you would like to encapsulate it into a typesafe heterogeneous container:
public static class ToIdMap {
private final Map<Class<?>, Function<Object, String>> map = new HashMap<>();
public <X> void put(Class<X> clazz, Function<X, String> func) {
map.put(clazz, (Function<Object, String>) func);
}
public String toIdString(Object o) {
return map.get(o.getClass()).apply(o);
}
}
EDIT2: Note that neither of these solutions work for subclasses, but it could be supported by traversing the class hierarchy in toIdString.
Your wording is a bit unclear, but I assume you want to get the ID of an object, even when they are different classes. This is the problem that interfaces solve.
You can create an interface, with one method called getId(), which will return the id. Then, you can just call getId() on any type of object with an id.
For example:
public interface Identifiable {
String getId();
}
public class ObjectA implements Identifiable {
// same for ObjectB
#Override
public String getId() {
return id;
}
}
Then, in your code:
Identifiable i1 = new ObjectA();
Identifiable i2 = new ObjectB();
System.out.println(i1.getId());
System.out.println(i2.getId());
EDIT:
It still looks like an interface is the cleanest way of solving your problem. For completeness, the following will work:
Map<Class, Function<?, String> map = new HashMap<>();
map.put(Object1.class, (Object1 o) -> o.getAttrA); // repeat for ObjectB
It can then be called with:
if (obj instanceof Object1) return map.get(Object1.class).apply((ObjectA) obj);
Ended up doing this weird solution:
class Mapping<T> {
private Function<T, String> idFunc;
public Mapping(Function<T, String> idFunc) {
this.idFunc = idFunc;
}
public String apply(T obj) {
return idFunc.apply(obj);
}
}
}
private Map<Class, Mapping> mappings = new HashMap<>();
mappings.put(ObjectA.class, new Mapping<>(ObjectA::getAttA);
mappings.put(ObjectB.class, new Mapping<>(ObjectB::getAttB);
public String getObjectID(Object object){
String id = null;
if(mappings.containsKey(object.getClass())){
id = mappings.get(object.getClass()).apply(object);
}
return id;
}

Type mismatch: cannot convert from List<Map<String,Object>> to List<Object>

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.

DynamoDB and Global Secondary Index and ObjectMapper

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?

How to use string to compare with field names in java?

I have a class named MyClass. It has many fields of type MyField. How do I return a reference to a particular field whose name matches a String's value?
public class MyClass{
public MyField field1;
public MyField field2;
public MyField field3;
public MyField whichField(String nameOfField){
//e.g. String = "field3", then return field3
//of course I can do if else, but it will be tedious If I have long list of MyField fields, can I iterate over all field names, and return whose name matches?
}
}
edit
I tried reflection from the answers below, I create a temp placeholder, and I wish to reutrn it but,
MyField temp = MyClass.class.getDeclaredField(whichFieldString);
doesnt work, I get type mismatch, cant convert error
How do I cast this?
How do I return this field?
As an alternative:
If all fields are of the same type and are accessed by their field name (most of the time) you could avoid the hassle and brittleness of using reflection by utilizing a Map.
The map associates a key (in your case the "field name") with a value. Instead of an arbitrary number of fields, MyClass would look like:
public class MyClass {
private final Map<String, MyField> fields = new HashMap<>();
/* code to initially fill the map */
public MyField whichField(String fieldName) {
return fields.get(fieldName);
}
}
You can do this with reflection. Class A has the fields we want to search through:
public class A {
private String field1;
private String field2;
private String field3;
}
And B shows how to iterate over the fields declared in A, matching on a particular field name:
public class B {
public B() {
Field field = findFieldByName("field1");
System.out.println(field);
}
private Field findFieldByName(String name) {
Field[] fields = A.class.getDeclaredFields();
for(Field f : fields) {
if(f.getName().equals(name)) {
return f;
}
}
return null;
}
public static void main(String[] args) {
new B();
}
}
You'll have to use reflection:
import java.lang.reflect.Field;
public class MyClass {
public MyField field1;
public MyField field2;
public MyField field3;
public MyField whichField(String nameOfField) {
MyField fieldName = null;
Field[] fields = MyClass.class.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals(nameOfField)) {
// Do whatever you want to do
}
}
return null;
}
}
class MyField {
}
You may want to use a collection, e.g. Map<String, MyField>.
You can do it easily with reflection
Class<MyClass> clazz = MyClass.class;
Field requieredField = clazz.getDeclaredField("myFielldName");
EDIT
This solution is pertinent is the number of fields is fixed. As it was mentioned in comments and answers, if you want to store a dynamic number of values, then a Map (or a Collection if you only need to enumerate the values) is much more suitable.

Categories

Resources