How does map from Page from Spring data works? - java

As the title says how does the map from org.springframework.data.domain.Page work?
As per the documentation, it suggests that you put a converter as a map parameter, and you get a new map with the converted contents. As per this interpretation, I wrote code to something as follows.
Page<T> results = getPagedResults();
return results.map(x -> {
x.setElement("some constant");
return x;
});
However the above did not work as expected. I got the Page with all the other values intact like count and so on, but the content was an empty list! I actually had to write code this way.
Page<T> results = getPagedResults();
return new Page<T>() {
#Override
public int getTotalPages() {
return results.getTotalPages();
}
...
#Override
public List<T> getContent() {
List<T> contents = result.getContent();
for (T content : contents) {
content.setElement("some constant");
}
return contents;
}
...
};
The second choice is verbose and seems to do redundant work. I would have expected the first piece of code to do the same, but it did not.
Am I reading the documentation wrong? And where would you use the map function of Page if it is not supposed to be used, the way I was using it?

How about putting a real converter like below:
Page<T> results = getPagedResults();
Page<T> convertedResults = results.map(new Converter<T, T>() {
#Override
public T convert(T page) {
Page page2 = new Page();
page2.setElement('some constant');
//set other fields
return page2;
}
});

You can use map the same way as we do:
results.map(mapper::convert)
where the convert is the method of mapper to convert database entity to DTO or any another object.

Related

In Hazelcast jet how can we store IList to normal list as I have to sent it in Response?

I am new to Hazelcast jet and in my application on data I am doing some aggregation and getting data but I want to send that in rest response so how can I change it to normal list?
public class ResponseMessage<T> {
private T responseClassType;
private ResponseMessage() {}
private ResponseMessage(T t) {
this.responseClassType = t;
}
public static <T> ResponseMessage<T> withResponseData(T classType) {
return new ResponseMessage<T>(classType);
}
public static ResponseMessage<Void> empty() {
return new ResponseMessage<>();
}
public T getResponseClassType() {
return responseClassType;
}
public void setResponseClassType(T responseClassType) {
this.responseClassType = responseClassType;
}
}
This is my generic response class and as below I am sending response after all calculations:
public ResponseMessage<?> runProcess(Pipeline pl) {
Map<String, BatchStage<Object>> allBatch = new HashMap<String,BatchStage<Object>>();
allBatch.put(z.get("id").toString(), new SomeCalulation().readSource(pipeline));
BatchStage<Object> h = allBatch.values().iterator().next();
h.writeTo(Sinks.list("abc"));
IList<Object> abc = jetInstance.getList("abc");
List<Object> result = new ArrayList(abc);
abc.destroy();
return ResponseMessage.withResponseData(result);
}
Now this is working but everytime I call rest request it is increasing the list and if I clear the list it is showing blank records, please help how can I convert it to normal list or best way to send response?
It was not working because I was joining it after method call:
runProcess(pl);
job.join(); // so because I am joining it after runProcess not working but if I directly return ResponseMessage.withResponseData(jetInstance.getList("abc")); and then join it will work.
I don't see submitting the pipeline as a job and waiting for the result (job.join()). I suppose you have omitted this from your code sample.
To solve your issue with empty list simply copy the result before destroying the list:
job.join();
IList<Object> abc = jetInstance.getList("abc");
List<Object> result = new ArrayList(abc)
abc.destroy();
return ResponseMessage.withResponseData(result);
Also, the list should have a unique name for each request, otherwise, multiple requests will write to the same list, having unpredictable results.

returning the ArrayList cast to List vs no casting

What is the difference, if any, between the two:
#ModelAttribute(value = "attendanceStatuses")
public List<Code> getAttendanceStatusCodes() {
List<Code> attendanceStatuses= new ArrayList<Code>(
cacheService.getValidCodesOfCodeGroup(CODE_GROUP));
return attendanceStatuses;
}
and
#ModelAttribute(value = "attendanceStatuses")
public List<Code> getAttendanceStatusCodes() {
return cacheService.getValidCodesOfCodeGroup(CODE_GROUP);
}
The cacheService method is:
List<Code> getValidCodesOfCodeGroup(CodeGroupName codeGroupName);
The first snippet returns a copy of the List returned by cacheService.getValidCodesOfCodeGroup(CODE_GROUP):
new ArrayList<Code>(cacheService.getValidCodesOfCodeGroup(CODE_GROUP))
The second snippet does not - it simply returns cacheService.getValidCodesOfCodeGroup(CODE_GROUP).
There is no casting in any of these snippets though.
Note that assigning the List to a local variable before returning it makes no difference. You can change the first snippet to:
public List<Code> getAttendanceStatusCodes() {
return new ArrayList<Code>(cacheService.getValidCodesOfCodeGroup(CODE_GROUP));
}
without changing the behavior.

How can I use fetchMap() with a RecordMapper?

I know I can fetch a map something like this:
this.ctx.select(
shopSubscription.field(SHOP_SUBSCRIPTION.SHOP_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID),
PAYMENT_GATEWAY_SUBSCRIPTION.SUBSCRIPTION_ID_TOKEN
)
.from(PAYMENT_GATEWAY_SUBSCRIPTION)
.join(shopSubscription)
.on(PAYMENT_GATEWAY_SUBSCRIPTION.ID.eq(shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID))
.and(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID.eq(paymentGatewayType)))
.fetchMap(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID, ShopSubscriptionDTO.class);
but to detect issues at compile time I'd prefer if I could additionally add a RecordMapper to this query.
So is there a way to call fetchMap() but also provide a RecordMapper?
What I'm thinking of would look something like this:
this.ctx.select(
shopSubscription.field(SHOP_SUBSCRIPTION.SHOP_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID),
shopSubscription.field(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID),
PAYMENT_GATEWAY_SUBSCRIPTION.SUBSCRIPTION_ID_TOKEN
)
.from(PAYMENT_GATEWAY_SUBSCRIPTION)
.join(shopSubscription)
.on(PAYMENT_GATEWAY_SUBSCRIPTION.ID.eq(shopSubscription.field(SHOP_SUBSCRIPTION.PAYMENT_GATEWAY_SUBSCRIPTION_ID))
.and(PAYMENT_GATEWAY_SUBSCRIPTION.PAYMENT_GATEWAY_TYPE_ID.eq(paymentGatewayType)))
// For each record apply the map() function
.map(new RecordMapper<Record<?>, ShopSubscriptionDTO>() {
#Override
public ShopSubscriptionDTO map(Record<?> record) {
ShopSubscriptionDTO shopSubscriptionDto = new ShopSubscriptionDTO();
shopSubscriptionDto.setShopId(record.getValue(SHOP_SUBSCRIPTION.SHOP_ID)
// ...
return shopSubscriptionDto;
}
});
// Fetch the result into a map where the key is SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID
.fetchMap(SHOP_SUBSCRIPTION.ADMIN_TOOL_FEATURE_TYPE_ID);
Since there are quite a lot of different implementations of fetchMap() I didn't see that there is fetchMap(Field<K>, RecordMapper<? super R, R>) too. So just going with that helps solving this issue:
// ...
.fetchMap(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_ADD_ON_TYPE_ID, new RecordMapper<Record, AdminToolAddOnDTO>() {
#Override
public AdminToolAddOnDTO map(Record record) {
AdminToolAddOnDTO dto = new AdminToolAddOnDTO();
dto.setId(record.getValue(ADMIN_TOOL_ADD_ON.ID));
dto.setAdminToolFeatureTypeId(record.getValue(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_FEATURE_TYPE_ID));
dto.setAdminToolAddOnTypeId(record.getValue(ADMIN_TOOL_ADD_ON.ADMIN_TOOL_ADD_ON_TYPE_ID));
dto.setPrice(record.getValue(ADMIN_TOOL_ADD_ON.PRICE));
dto.setCountryId(record.getValue(ADMIN_TOOL_ADD_ON.COUNTRY_ID));
dto.setAddOnIdToken(record.getValue(ADMIN_TOOL_ADD_ON_TYPE.ADD_ON_ID_TOKEN));
return dto;
}
});
java 8 or higher
.fetchMap(CN_TASKS.AGENTID,
r -> new CnTaskMessage(r.getValue(CN_TASKS.CN_TASKID), r.getValue(CN_TASKS.TASK_TYPE),
r.getValue(CN_TASKS.STATUS)));

How to resolve a promise inside another promise?

I have an action which requires to get a list of emails from a remote server. Then I want to use the emails to get a list of emailDomainInformation from another remote server (note that this second piece of info depends on the first). After all this, I want to output data from both servers onto a map and render it onto the page with dust.
I managed to get this to work without the second piece of data by doing it like this:
public static Result index()
{
F.Promise<Email> emailPromise = getEmailPromise(...);
F.Promise<Result> results = emailPromise.map( new F.Function<Email, Result>()
{
public Result apply(Email email)
{
Map<String, Object> data = new HashMap<String, Object>();
data.put("email", email.getAddress());
data.put("domain", email.getDomain());
dustRenderer.render(data);
}
}
async(results);
}
Now, since I want to make an async call to getEmailDomainData(email.getDomain()); inside the emailPromise.map() method. What do I do with the Promise<EmailDomain> object I get back? How do I put that into the data map to pass to the dustRenderer?
Here is an example that essentially does what you need:
public static Result social() {
final F.Promise<WS.Response> twitterPromise = WS.url("http://search.twitter.com/search.json").setQueryParameter("q", "playframework").get();
final F.Promise<WS.Response> githubPromise = WS.url("https://api.github.com/legacy/repos/search/playframework").get();
return async(
twitterPromise.flatMap(
new F.Function<WS.Response, F.Promise<Result>>() {
public F.Promise<Result> apply(final WS.Response twitterResponse) {
return githubPromise.map(
new F.Function<WS.Response, Result>() {
public Result apply(final WS.Response githubResponse) {
return ok(views.html.social.render(twitterResponse.asJson().findValuesAsText("text"), githubResponse.asJson().findValuesAsText("name")));
}
}
);
}
}
)
);
}
In this case the two run in parallel but you could move the second Promise creation into the handler for the first Promise.

Transforming Form Field to Object on Submit

I'm rather new to Play Framework so I hope this is intelligible.
How can I tell play to map a form element to an Object field in the Form's class?
I have a form with a select dropdown of names of objects from my ORM. The values of the dropdown items are the ID field of the ORM objects.
The form object on the Java side has a field with the type of the ORM object, and a setter taking a string and translating it to the object, but on form submission I only get a form error "Invalid Value" indicating the translation is not taking place at all.
My template has a form component:
#helper.select(
createAccountForm("industry"),
helper.options(industries)
)
Where industries is defined in the template constructor by : industries: Map[String, String]
and consists of ID strings to User-Readable names.
My controller defines the class:
public static class CreateAccountForm {
public String name;
public Industry industry;
public void setIndustry(String industryId) {
this.industry = Industry.getIndustry(Integer.parseInt(industryId));
}
}
EDIT: I was doing the setter in the class because this answer indicated to do so, but that didn't work.
EDIT2:
Turns out the setter method was totally not the way to go for this. After banging my head a bit on trying to get an annotation working, I noticed the Formatters.SimpleFormatter and tried that out. It worked, though I don't understand why the extra block around it is necessary.
Global.java:
public class Global extends GlobalSettings {
// Yes, this block is necessary; no, I don't know why.
{
Formatters.register(Industry.class, new Formatters.SimpleFormatter<Industry>() {
#Override
public Industry parse(String industryId, Locale locale) throws ParseException {
return Industry.getIndustry(Integer.parseInt(industryId));
}
#Override
public String print(Industry industry, Locale locale) {
return industry.name;
}
});
}
}
Play is binding the form to an object for you when you use it like described in the documentation: https://github.com/playframework/Play20/wiki/JavaForms
So your controller should look like:
Form<models.Task> taskForm = form(models.Task.class).bindFromRequest();
if (taskForm.hasErrors()) {
return badRequest(views.html.tasks.create.render(taskForm));
}
Task task = taskForm.get();
The task object can have a Priority options list. And you use it in the form (view) like:
#select(editForm("priority.id"), options(Task.priorities), 'class -> "input-xlarge", '_label -> Messages("priority"), '_default -> Messages("make.choice"), 'showConstraints -> false, '_help -> "")
Notice that I am using priorities.id to tell play that a chosen value should be binded by a priority ID. And of course getting the priorities of the Tasks:
public static Map<String, String> priorities() {
LinkedHashMap<String, String> prioritiesList = new LinkedHashMap<String, String>();
List<Priority> priorities = Priority.getPrioritiesForTask("task");
for (Priority orderPrio : priorities) {
prioritiesList.put(orderPrio.getId().toString(), orderPrio.getDescription());
}
return prioritiesList;
}

Categories