I have a List collection where each Metric contains several properties such as: metricName, namespace, fleet, type, component, firstSeenTime, lastSeenTime, etc. There are duplicates in this list such that all properties are same except for firstSeenTime and lastSeenTime. I am looking for an elegant way to filter this list and only return the metrics with the most recent lastSeenTime when there are such duplicates.
Something better than this:
private List<Metric> processResults(List<Metric metrics) {
List<Metric> results = new ArrayList<>();
for (Metric incomingMetric: metrics) {
// We need to implement "contains" below so that only properties
// other than the two dates are checked.
if (results.contains(incomingMetric) {
int index = results.indexOf(incomingMetric);
Metric existing = results.get(index);
if (incomingMetric.getLastSeen().after(existing.getLastSeen())) {
results.set(index, metricName);
} else {
// do nothing, metric in results is already the latest
}
} else {
// add incomingMetric to results for the first time
results.add(incomingMetric);
}
}
return results;
}
The results.contains check is done by iterating over all the Metrics in results and checking if each object matches the properties except for the two dates.
What could be a better approach that this for both elegance and performance?
In java the most elegant way to compare things is the Comparator interface. You should remove the duplicates using something like:
public List<Metric> removeDuplicates(List<Metric> metrics) {
List<Metric> copy = new ArrayList<>(metrics);
//first sort the metrics list from most recent to older
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(new Comparator<Metric>() {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
// compare the two metrics given your rules
return result;
}
});
for(Metric metric : copy) {
set.add(metric);
}
List<Metric> result = Arrays.asList(set.toArray());
return result;
}
class SortComparator implements Comparator<Metric> {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
if(o2.getLastSeenTime() != null && o1.getLastSeenTime() != null) {
result = o2.getLastSeenTime().compareTo(o1.getLastSeenTime());
}
return result;
}
}
The strong of this approach is that you could write a family of comparators and provide a Factory to choose at runtime the best way to compare your metrics and remove or not instances as duplicates among the runtime conditions:
public void removeDuplicates(List<Metric> metrics, Comparator<Metric> comparator) {
List<Metric> copy = new ArrayList<>(metrics);
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(comparator);
for(Metric metric : copy) {
set.add(metric);
}
List<Object> result = Arrays.asList(set.toArray());
return result;
}
I’m not sure how you are generating List<Metric>. But if you can maintain a Map<String, Metric> instead of that list you may can try the below approach.
So the key of this map will be a combination of all these values you need to compare. (except the date attributes.)
Key: “{metricName}${type}$.....”
For this you can maintain another attribute in Metric object with getter. When you call the getter it will return the key.
Then check the key is exist or not before you put into the map. If it’s exist, get the stored Metric in map for that key and do the date comparison to find the latest Metric object. If it’s the latest replace the map's stored object with new object.
PS : Do the execution time comparison for both cases. So you will find the best approach.
Thanks for the answers. I went with the map approach since it does not incur additional sorts and copies.
#VisibleForTesting
Set<Metric> removeDuplicates(List<Metric> metrics) {
Map<RawMetric, Metric> metricsMap = new HashMap<>();
for (Metric metric : metrics) {
RawMetric rawMetric = RawMetric.builder()
.metricName(metric.getName())
.metricType(metricName.getMetricType())
... // and more
.build();
// pick the latest updated metric (based on lastSeen date)
BiFunction<RawMetric, Metric, Metric> biFunction =
(k, v) -> Metric.builder()
.name(k.getMetricName())
.metricType(k.getMetricType())
... // and more
.lastSeen(v.getLastSeen().after(
metricName.getLastSeen()) ? v.getLastSeen() :
metricName.getLastSeen())
.firstSeen(v.getFirstSeen())
.build();
metricsMap.putIfAbsent(rawMetric, metric);
metricsMap.computeIfPresent(rawMetric, biFunction);
}
return ImmutableSet.copyOf(metricsMap.values());
}
#Value
#Builder
static class RawMetricName {
private String metricName;
private String metricType;
private String ad;
private String project;
private String fleet;
private String host;
private int granularity;
}
Related
I have made various methods for someone to add a key which then includes various values from another created object.
I need to then allow a user to search using a method name which will then return all the people that match their search.
public Set findPerson(String aTrade)
{
Set<String> suitablePeople = new HashSet<>();
for (String test : this.contractors.keySet())
{
System.out.println(contractors.get(test));
if (contractors.containsValue(aTrade))
{
suitablePeople.add(test);
}
}
return suitablePeople;
}
I know this code is wrong but I'm just not sure how I can simply go through and find a value and return all the people that have this value within a range of values. For instance, their age, job, location.
Some assumptions, because your question is rather unclear:
contractors is a Map<String, ContractorData> field. Possibly ContractorData is some collection type (such as MyList<Contractor>), or named differently. The String represents a username.
aTrade is a string, and you want to search for it within the various ContractorData objects stored in your map. Then you want to return a collection of all username strings that are mapped to a ContractorData object that contains a trade that matches aTrade.
Whatever ContractorData might be, it has method containsValue(String) which returns true if the contractordata is considered a match. (If that was pseudocode and it's actually a List<String>, just .contains() would do the job. If it's something else you're going to have to elaborate in your question.
Then, there is no fast search available; maps allow you to do quick searches on their key (and not any particular property of their key, and not on their value or any particular property of their value). Thus, any search inherently implies you go through all the key/value mappings and check for each, individually, if it matches or not. If this is not an acceptable performance cost, you'd have to make another map, one that maps this property to something. This may have to be a multimap, and is considerably more complicated.
The performance cost is not important
Okay, then, just.. loop, but note that the .entrySet() gives you both key (which you'll need in case it's a match) and value (which you need to check if it matches), so that's considerably simpler.
var out = new ArrayList<String>();
for (var e : contracts.entrySet()) {
if (e.getValue().containsValue(aTrade)) out.add(e.getKey());
}
return out;
or if you prefer stream syntax:
return contracts.entrySet().stream()
.filter(e -> e.getValue().containsValue(aTrade))
.map(Map.Entry::getKey)
.toList();
The performance cost is important
Then it gets complicated. You'd need a single object that 'wraps' around at least two maps, and you need this because you want these maps to never go 'out of sync'. You need one map for each thing you want to have a find method for.
Thus, if you want a getTradesForUser(String username) as well as a findAllUsersWithTrade(String aTrade), you need two maps; one that maps users to trades, one that maps trades to users. In addition, you need the concept of a multimap: A map that maps one key to (potentially) more than one value.
You can use guava's MultiMaps (guava is a third party library with some useful stuff, such as multimaps), or, you roll your own, which is trivial:
given:
class ContractData {
private List<String> trades;
public boolean containsValue(String trade) {
return trades.contains(trade);
}
public List<String> getTrades() {
return trades;
}
}
then:
class TradesStore {
Map<String, ContractData> usersToTrades = new HashMap<>();
Map<String, List<String>> tradesToUsers = new HashMap<>();
public void put(String username, ContractData contract) {
usersToTrades.put(username, contract);
for (String trade : contract.getTrades()) {
tradesToUsers.computeIfAbsent(username, k -> new ArrayList<>()).add(username);
}
}
public Collection<String> getUsersForTrade(String trade) {
return tradesToUsers.getOrDefault(trade, List.of());
}
}
The getOrDefault method lets you specify a default value in case the trade isn't in the map. Thus, if you ask for 'get me all users which have trade [SOME_VALUE_NOBODY_IS_TRADING]', this returns an empty list (List.of() gives you an empty list), which is the right answer (null would be wrong - there IS an answer, and it is: Nobody. null is means: Unknown / irrelevant, and is therefore incorrect here).
The computeIfAbsent method just gets you the value associated with a key, but, if there is no such key/value mapping yet, you also give it the code required to make it. Here, we pass a function (k -> new ArrayList<>()) which says: just.. make a new arraylist first if I ask for a key that isn't yet in there, put it in the map, and then return that (k is the key, which we don't need to make the initial value).
Thus, computeIfAbsent and getOrDefault in combination make the concept of a multimap easy to write.
Assuming that your Map's values are instances of Contractor and the Contractor class has a Set<String> of trades (implied by the contains method call) and a getTrades() method that returns the list you could do it like this. Not certain what value the Map key would play in this.
get the values from the map and stream them.
filter only those Contractors that have the appropriate trade.
aggregate to a set of able contractors.
Set<Contractor> suitablePeople =
contractors.values()
.stream()
.filter(c->c.getTrades().contains(aTrade))
.collect(Collectors.toSet());
Note that a possible improvement would be to have a map like the following.
Map<String, Set<Contractors>> // where the key is the desired trade.
Then you could just get the Contractors with a single lookup up for each desired trade.
Set<Contractors> plumbers = mapByTrade.get("plumbers"); // all done.
Here is how you would set it up. The Contractor class is at the end. It takes a name and a variable array of trades.
Set<Contractor> contractors = Set.of(
new Contractor("Acme", "plumbing", "electrical", "masonry", "roofing", "carpet"),
new Contractor("Joe's plumbing", "plumbing"),
new Contractor("Smith", "HVAC", "electrical"),
new Contractor("Ace", "electrical"));
Then, iterate over the list of contractors to create the map. Then those are grouped by trade, and each contractor that matches is put in the associated set for that trade.
Map<String,Set<Contractor>> mapByTrade = new HashMap<>();
for (Contractor c : contractors) {
for (String trade : c.getTrades()) {
mapByTrade.computeIfAbsent(trade, v->new HashSet<>()).add(c);
}
}
And here it is in action.
Set<Contractor> plumbers = mapByTrade.get("plumbing");
System.out.println(plumbers);
System.out.println(mapByTrade.get("electrical"));
System.out.println(mapByTrade.get("HVAC"));
prints
[Acme, Joe's plumbing]
[Ace, Acme, Smith]
[Smith]
And here is the Contractor class.
class Contractor {
private Set<String> trades;
private String name;
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object ob) {
if (ob == name) {
return true;
}
if (ob == null) {
return false;
}
if (ob instanceof Contractor) {
return ((Contractor)ob).name.equals(this.name);
}
return false;
}
public Contractor(String name, String...trades) {
this.name = name;
this.trades = new HashSet<>(Arrays.asList(trades));
}
public Set<String> getTrades() {
return trades;
}
#Override
public String toString() {
return name;
}
}
I have a requirement where I want to read multiple project pom files and display data in below format
{
"java" : {"1.7" : ["project1"],"1.8": ["project2"]},
"junit" : {"4.12" : ["project1"]},
"hsqldb" : {"1.8" : ["project3"],"1.1": ["project6"]}
}
My coding is getting input on project , ver and technlogy and displaying, but however I couldnt second value inside the internal
private void addTechnologyData(String projName,String techName,String ver)
{
String keyFormat=techName;
if (technologyMap.containsKey(keyFormat)) {
Map preValue=technologyMap.get(keyFormat);
if(!preValue.containsValue(projName)) {
Map<String,String> temp = new HashMap();
temp=preValue;
temp.put(ver,projName);
technologyMap.put(keyFormat, temp);
}
} else {
Map<String,String> projectVersiomap = new HashMap();
projectVersiomap.put(ver,projName);
technologyMap.put(keyFormat, projectVersiomap);
}
}
Please help me understand why I couldnt add 2nd key value pair to Internal map?
Is there a better way than what Im doing?
If my understanding is correct, you are expecting more than one project per version (since you have an array), so your Map is preventing this, you can only have one value per key. You can use a List like Map<String,Map<String, List<String>>> but I would suggest to use some POJO to keep the code cleaner.
Create a Technology class that will hold a list of Project for each version in a Map. This would look like :
public class Technology{
private String name;
private Map<String, List<Project>> projects;
public Technology(String name){
this.name = name;
this.projects = new HashMap<>();
}
public void addProjectToVersion(String version, Project project){
List<Project> l = this.projects.get(version);
if(l == null){ //doesn't exist
l = new ArrayList<>();
this.projects.put(version, l);
}
l.add(project);
}
}
Here, the logic is in the POJO. You just need to use a Collection to hold your instance, a Map if you like, or a List (implement equals and hashCode to recover easily the instance). And you can use it like :
private Map<String, Technology> techs = new HashMap<>();
....
public void addProject(String tech, String version, String projectName){
Technology t = techs.get(tech);
if(t == null){ //doesn't exist
t = new Technology(tech);
techs.put(tech, t);
}
t.addProjectToVersion(version, new Project(projectName));
}
public void insertData(){
addProject("java", "1.7", "project1");
addProject("java", "1.7", "project2");
addProject("java", "1.8", "project1");
addProject("junit", "4.12", "project1");
System.out.println(techs);
}
This will input correctly :
{junit={4.12=[project1]}, java={1.7=[project1, project2], 1.8=[project1]}}
Note that I reused your logic, based on the requirements it could be better to have a List<Project> holding each Technology with the version. But this is based on the context.
I am having the use case where I construct my result by joining two tables ITEM and ITEM_DESCRIPTION. From there I am taking several columns which I then would like to conveniently convert into a list of objects. In my case these objects are actually DTO objects but of course they could be business objects as well.
This is the way I am doing it now:
public Map<Long, List<StoreItemDTO>> getItems(Long storeId) {
LOGGER.debug("getItems");
// Get all item_ids for the store
SelectHavingStep<Record1<Long>> where = this.ctx
.select(STORE_ITEM.ID)
.from(STORE_ITEM)
.where(STORE_ITEM.STORE_ID.eq(storeId))
// GROUP BY store_item.id
.groupBy(STORE_ITEM.ID);
// Get all store_item_details according to the fetched item_ids
TableLike<?> storeItemDetails = this.ctx
.select(
STORE_ITEM_DETAILS.ID,
STORE_ITEM_DETAILS.STORE_ITEM_ID,
STORE_ITEM_DETAILS.NAME,
STORE_ITEM_DETAILS.DESCRIPTION,
STORE_ITEM_DETAILS.STORE_LANGUAGE_ID
)
.from(STORE_ITEM_DETAILS)
.where(STORE_ITEM_DETAILS.STORE_ITEM_ID.in(where))
.asTable("storeItemDetails");
// Join the result and use
Field<Long> itemIdField = STORE_ITEM.ID.as("item_id");
Result<?> fetch = this.ctx
.select(
STORE_ITEM.ID.as("item_id"),
itemIdField,
storeItemDetails.field(STORE_ITEM_DETAILS.ID),
storeItemDetails.field(STORE_ITEM_DETAILS.NAME),
storeItemDetails.field(STORE_ITEM_DETAILS.DESCRIPTION),
storeItemDetails.field(STORE_ITEM_DETAILS.STORE_LANGUAGE_ID)
)
.from(STORE_ITEM)
.join(storeItemDetails)
.on(storeItemDetails.field(STORE_ITEM_DETAILS.STORE_ITEM_ID).eq(STORE_ITEM.ID))
.fetch();
Map<Long, ?> groups = fetch.intoGroups(STORE_ITEM.ID);
return null;
}
As you can see, the result should be a list of items where each item has an item-details in different languages:
StoreItemDTO
- Long id
// Maps language-id to item details
- Map<Long, StoreItemDetails> itemDetails
StoreItemDetails
- Long id
- String name
- String description
I couldn't find a version of intoGroups() that would return a useful type. I could imagine to have something like Map<Long, List<Record>> but I can't manage to do so.
However, there is a intoGroups(RecordMapper<? super R, K> keyMapper) which could be what I am looking for. If a mapper would also allow me to actually convert the resulting records into a custom object like MyCustomPojo then I could retrieve and convert the data quite conveniently. I don't know if this is somehow possible. Something like:
public static class MyCustomPojo {
public Long itemId;
// etc.
}
// ..
Map<Long, List<MyCustomPojo>> result = fetch.intoGroups(STORE_ITEM.ID, new RecordMapper<Record, List<MyCustomPojo>>() {
#Override
public List<MyCustomPojo> map(List<Record> record) {
// 'record' is grouped by STORE_ITEM.ID
// Now map each 'record' into every item of each group ..
return resultList;
}
});
But unfortunately the compiler only allows
fetch.intoGroups(new RecordMapper<Record, Result<?>>() {
#Override
public Result<?> map(Record record) {
return null;
}
});
After some fiddling around with the compiler it turns out that it can be done.
I had to "cheat" a little by declaring my resulting map as final outside of the anonymous and I am actually not "using" the keyMapper parameter as I am just returning null.
This is what I came up with:
public Map<Long, StoreItemDTO> getItems(Long storeId) {
// Get all item_ids for the store
SelectHavingStep<Record1<Long>> where = this.ctx
.select(STORE_ITEM.ID)
.from(STORE_ITEM)
.where(STORE_ITEM.STORE_ID.eq(storeId))
.groupBy(STORE_ITEM.ID);
// Get all store_item_details according to the fetched item_ids
TableLike<?> storeItemDetails = this.ctx
.select(
STORE_ITEM_DETAILS.ID,
STORE_ITEM_DETAILS.STORE_ITEM_ID,
STORE_ITEM_DETAILS.NAME,
STORE_ITEM_DETAILS.DESCRIPTION,
STORE_ITEM_DETAILS.STORE_LANGUAGE_ID
)
.from(STORE_ITEM_DETAILS)
.where(STORE_ITEM_DETAILS.STORE_ITEM_ID.in(where))
.asTable("storeItemDetails");
// Join the result and use
final Field<Long> itemIdField = STORE_ITEM.ID.as("item_id");
Result<?> fetch = fetch = this.ctx
.select(
itemIdField,
storeItemDetails.field(STORE_ITEM_DETAILS.ID),
storeItemDetails.field(STORE_ITEM_DETAILS.NAME),
storeItemDetails.field(STORE_ITEM_DETAILS.DESCRIPTION),
storeItemDetails.field(STORE_ITEM_DETAILS.STORE_LANGUAGE_ID)
)
.from(STORE_ITEM)
.join(storeItemDetails)
.on(storeItemDetails.field(STORE_ITEM_DETAILS.STORE_ITEM_ID).eq(STORE_ITEM.ID))
.fetch();
final Map<Long, StoreItemDTO> itemIdToItemMap = new HashMap<>();
fetch.intoGroups(
record -> {
Long itemDetailsId = record.getValue(STORE_ITEM_DETAILS.ID);
// ... sake of compactness
StoreItemDetailsDTO storeItemDetailsDto = new StoreItemDetailsDTO();
storeItemDetailsDto.setId(itemDetailsId);
// ... sake of compactness
Long itemId = record.getValue(itemIdField);
StoreItemDTO storeItemDto = new StoreItemDTO();
storeItemDto.setId(itemId);
storeItemDto.getItemDetailsTranslations().put(languageId, storeItemDetailsDto);
StoreItemDTO itemDetailsList = itemIdToItemMap.get(itemId);
if(itemDetailsList == null) {
itemDetailsList = new StoreItemDTO();
itemIdToItemMap.put(itemId, itemDetailsList);
}
itemDetailsList.getItemDetailsTranslations().put(languageId, storeItemDetailsDto);
return null;
});
return itemIdToItemMap;
}
Since I am not sure if this is the most elegant solution one could have I'm still open for suggestions and willing to accept any answer that can elegantly shorten this code - if that is possible at this point. :)
In my project I have two entities: Race and RaceDriver, which has-a Race in it:
class RaceDriver {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "race", nullable = false)
private Race race;
...
#Column(name = "starting_nr")
private Integer startingNr;
...
#Column(name = "disqualified", nullable = false)
private boolean disqualified;
}
Now, what I wanted is to get the list of the startingNrs of the disqualified RaceDrivers in a Race, which looked like this:
public List<Integer> findDisqualifiedDriversStartingNumbers(Integer raceId) {
ProjectionList projection = Projections.projectionList()
.add(Projections.property("startingNr").as("startingNr"));
return getSession()
.createCriteria(RaceDriver.class)
.setProjection(projection)
.add(Restrictions.eq("race.id", raceId))
.add(Restrictions.eq("disqualified", true))
.list();
}
The thing is that now I need the same, but for the few Races. How can I achieve this without making a separate DAO calls? Because I've heard that it is better to make as much as possible in a single database call.
My idea is to simply get the list of the drivers which are disqualified in the given races, and then parse it in the Java code, which I think will require few loops, and make some map of disqualified RaceDriver's starting numbers, where the key would be Race.id.
The DAO attempt looks like that:
public List<RaceDriver> findDisqualifiedDriversInRaces(List<Integer> raceIds) {
return getSession()
.createCriteria(RaceDriver.class)
.add(Restrictions.in("race.id", raceIds))
.add(Restrictions.eq("disqualified", true))
.list();
}
The problem is that I will get that big objects, instead of some map or list of the only data I need (startingNr and race.id).
So the question is - can I do it somehow using only Hibernate?
Personally I would use the solution in plain java because it'll be more clear for any developer supporting your code in the future.
Answering the question "can it be done via Hibernate?": yes, it can be, ResultTransformer is the right way, especially if map-of-lists conversion is required more than once in your program. There is no standard transformer for your needs but you can write your own one:
public class MapOfListsResultTransformer<K, V> extends BasicTransformerAdapter {
public List transformList(List collection) {
final Map<K, List<V>> map = new HashMap<>();
for (Object object : collection) {
final Object[] objects = (Object[]) object;
final K key = (K) objects[0];
final V value = (V) objects[1];
if (!map.containsKey(key)) {
final List<V> list = new ArrayList<V>();
list.add(value);
map.put(key, list);
} else {
map.get(key).add(value);
}
}
return Arrays.asList(map);
}
}
And its usage is the following:
public Map<Integer, List<Integer>> findDisqualifiedDriversInRaces(List<Integer> raceIds) {
ProjectionList projection = Projections.projectionList()
.add(Projections.property("race.id").as("race.id"))
.add(Projections.property("startingNr").as("startingNr"));
return (Map<Integer, List<Integer>>) getSession()
.createCriteria(RaceDriver.class)
.setProjection(projection)
.add(Restrictions.in("race.id", raceIds))
.add(Restrictions.eq("disqualified", true))
.setResultTransformer(new MapOfListsResultTransformer<Integer, Integer>())
.uniqueResult();
}
You'll want to create a Query with hibernate that has two WHERE conditions.
#PersistenceContext protected EntityManager em;
List<Integer> raceIds = Arrays.asList(1, 2, 3);
Query query = em.createQuery("FROM RaceDriver r WHERE r.raceId IN (:raceIds) AND r.disqualified = true");
query.setParameter("raceIds", raceIds);
List<RaceDriver> drivers = query.getResultList();
This will return a list of disqualified drivers within the raceIds that you've provided.
Update
After getting List<RaceDriver> you can for loop on drivers and make List of HashMaps with raceId as a key, and a List of startingNr (of disqualified drivers) as a value.
HashMap<Integer,List<Integer>> startingNrHashMap=new...;
for(RaceDriver driver:drivers)
{
List<Integer> strNr = new ArrayList<Integer>();
if(startingNrHashMap.containsKey(driver.race.raceid))
{
//if race id is already present in hash map then
strNr = startingNrHashMap.get(driver.race.raceid);
strNr.add(driver.startingNr);
startingNrHashMap.put(driver.race.raceid,strNr);
}
else
{
// if race id is NOT present in hash map
strNr.add(driver.startingNr);
startingNrHashMap.put(driver.race.raceid,strNr);
}
strNr =null;
}
This is how I did it:
DAO method:
public List<RaceDriver> findDisqualifiedDriversInRaces(List<Integer> raceIds) {
if (!raceIds.isEmpty()) { // it will crash if the list will have no elements!
return getSession()
.createCriteria(RaceDriver.class)
.add(Restrictions.in("race.id", raceIds))
.add(Restrictions.eq("disqualified", true))
.list();
}
return Collections.emptyList();
}
Then to extract from it what I wanted, I created an auxiliary method:
// key = race.id, value = list of disqualified raceDrivers' starting numbers
private HashMap<Integer, List<Integer>> extractStartingNumbersToMap(List<RaceDriver> disqualifiedDrivers) {
HashMap<Integer, List<Integer>> disqualifiedDriversMap = new HashMap<>();
for (RaceDriver raceDriver : disqualifiedDrivers) {
Integer raceId = raceDriver.getRace().getId();
if (!disqualifiedDriversMap.containsKey(raceId)) {
disqualifiedDriversMap.put(raceId, new ArrayList<>(Arrays.asList(raceDriver.getStartingNr())));
} else {
disqualifiedDriversMap.get(raceId).add(raceDriver.getStartingNr());
}
}
return disqualifiedDriversMap;
}
I did it before Mark T has answered, as you can see it's very similar. However I'm posting it, as it could be helpful to someone.
I asked the same kind of question, Its about NHibernate and .NET MVC but you can get idea of DAO and ResultTransformer
Question
I am performing a scan on a DynamoDB table and I need to then add respective attributes from the returned items to a list of type User (User has a single constructor User(String uuid)). The code currently successfully scans the DB and returns a List of the scan results. However my iteration seems to return null for some reason.
AmazonDynamoDBClient client = dynamoClient.getDynamoClient();
DynamoDBMapper mapper = new DynamoDBMapper(client);
try {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
Map<String, Condition> scanFilter = new HashMap<String, Condition>();
Condition scanCondition =
new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL);
scanFilter.put("uuid", scanCondition);
scanExpression.setScanFilter(scanFilter);
List scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (Iterator it = scanResults.iterator(); it.hasNext();) {
//User user = (User) it.next();
allUserSummary.add(new User(scanResults.get(1).toString()));
}
} catch (Exception e) {
// TODO
}
I suggest you start using the modern and compact list iteration by means of The For-Each Loop, which helps to avoid many common errors when using the old iteration style:
[...]
The iterator is just clutter. Furthermore, it is an opportunity for
error. The iterator variable occurs three times in each loop: that is
two chances to get it wrong. The for-each construct gets rid of the
clutter and the opportunity for error. Here is how the example looks
with the for-each construct:
void cancelAll(Collection<TimerTask> c) {
for (TimerTask t : c)
t.cancel();
}
Applying this to your use case yields the following approximately:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.toString()));
}
In case this doesn't work already, it could hint towards the actual error as well, insofar your code assumes the toString() of class UserAccounts to return the uuid, which may or may not be the case. The usual approach is to have a getKey() or getUuidAttribute() method and respective annotations #DynamoDBHashKey or #DynamoDBAttribute, as shown in the example for Class DynamoDBMapper, e.g.:
#DynamoDBTable(tableName = "UserAccounts")
public class UserAccounts{
private String key; // or uuid right away
#DynamoDBHashKey
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
// ...
}
This would obviously yield the following for your example:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.getKey()));
}