java8 stream not showing the expected output - java-8

Iterate the list and create multiple objects.
Below is the sample json:
"accountRequest": [
{
"accountId": "10EIIP",
"custId": "11EE",
"custName": "XYZ",
"comments": null,
"status": "active",
"linkedDetails": [
{
"custCode": "001",
"startOn": "2023-01-01",
"loanType": "auto"
},
{
"custCode": "002",
"startOn": "2023-01-15",
"loanType": "home"
},
{
"custCode": "003",
"startOn": "2023-02-10",
"loanType": "home"
}
]
}
]
Account.java
class Account{
#JsonProperty("accountId")
private Long accountId;
#JsonProperty("custId")
private String custId;
//custName, comments, status
private String custCode;
private LocalDate startOn;
private String loanType;
}
I need to create list of "Account" by storing each of linkedDetails. The sample code below does not store accountId, custId, custName, comments, status in the "Account" object. Expected output is to create List of size 3..
List<Account> accountList = new ArrayList<>();
List<AccountRequest> accountRequestList = request.getAccountRequest();
accountRequestList.get(0).getLinkedDetails().forEach(linkedDetails -> {
Account account = new Account();
//need to set accountId, custId, custName, comments, status in each account object
account.setCustCode(linkedDetails.getCustCode());
account.setStartOn(linkedDetails.getStartOn());
account.setLoanType(linkedDetails.getLoanType());
accountList.add(account);
});
Expected Output :
"accountList": [
{
"accountId": "10EIIP",
"custId": "11EE",
"custName": "XYZ",
"comments": null,
"status": "active",
"custCode": "001",
"startOn": "2023-01-01",
"loanType": "auto"
},
{
"accountId": "10EIIP",
"custId": "11EE",
"custName": "XYZ",
"comments": null,
"status": "active",
"custCode": "002",
"startOn": "2023-01-15",
"loanType": "home"
},
{
"accountId": "10EIIP",
"custId": "11EE",
"custName": "XYZ",
"comments": null,
"status": "active",
"custCode": "003",
"startOn": "2023-02-10",
"loanType": "home"
}
]

Instead of creating a mutable list and modifying it in a foreach you should directly collect to the accountList:
List<AccountRequest> accountRequestList = request.getAccountRequest();
List<Account> accountList = accountRequestList.get(0).getLinkedDetails().stream().map(linkedDetails -> {
Account account = new Account();
//need to set accountId, custId, custName, comments, status in each account object
account.setCustCode(linkedDetails.getCustCode());
account.setStartOn(linkedDetails.getStartOn());
account.setLoanType(linkedDetails.getLoanType());
return account;
}).collect(Collectors.toList());

Related

How to map query result to List<Object> in R2DBC?

I am trying to migrate my JDBC based application to R2DBC, however I struggle with List based objects. How can I map the result from query to "List" field?
I was using ResultSetExtractor, but in R2DBC there is no option like that so I try to do it manually. I use DatabaseClient with manual mapping, however this mapping does not work with List type fields.
public class Employee {
private Long id;
private String name;
private String surname;
private List\<Account\> accounts;
}
public class Account {
private String employeeId;
private String accountNumber;
}
public class EmployeeRepository {
private final DatabaseClient databaseClient;
public static final BiFunction<Row, RowMetadata, Employee> EMPLOYEE_MAPPER =
(row, rowMetadata) -> Employee.builder()
.id(row.get("id", String.class))
.name(row.get("name", String.class))
.surname(row.get("surname", String.class))
.accounts(List.of(new Account((row.get("employeeId"), (row.get("accountNumber")))))
.build();
public Flux<Employee> findCardByPan(Long id) {
return databaseClient.sql("SELECT id, name, surname, a.accountNumber FROM employee e JOIN account a on e.id = a.employeeId WHERE e.id = :id")
.bind("id", id)
.map(EMPLOYEE_MAPPER)
.all();
}
}
Current response model:
[
{
"id": "1",
"name": "Test User",
"surname": "Test User",
"accounts": [
{
"employeeId": "1",
"accountNumber": "98034"
}
]
},
{
"id": "1",
"name": "Test User",
"surname": "Test User",
"accounts": [
{
"employeeId": "1",
"accountNumber": "12456"
}
]
}
]
Expected response model:
[
{
"id": "1",
"name": "Test User",
"surname": "Test User",
"accounts": [
{
"employeeId": "1",
"accountNumber": "98034"
},
{
"employeeId": "1",
"accountNumber": "12456"
}
]
}
]

Show only object id in OneToMany List

I have these two classes:
Quota Class
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
#Table(name="quotas")
#Relation(collectionRelation = "quotas", itemRelation = "quota")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Quota extends RepresentationModel<Quota> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "quota")
#JsonIdentityReference(alwaysAsId = true)
private List<Customer> customers;
private String type;
private boolean flatrate;
#CreatedDate
private long createdDate;
#LastModifiedDate
private long lastModifiedDate;
And Customer:
#Entity
#Data
#EqualsAndHashCode(callSuper=false)
#Table(name = "customers")
#Relation(collectionRelation = "customers", itemRelation = "customer")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Customer extends RepresentationModel<Customer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String customerNumber;
#CreatedDate
private long createdDate;
#LastModifiedDate
private long lastModifiedDate;
#ManyToOne
#JoinColumn(name = "quota_id", referencedColumnName = "id")
#JsonProperty("quotaId")
#JsonIdentityReference(alwaysAsId = true)
private Quota quota;
}
What i get when i make a GET request on all Customers:
{
"_embedded": {
"customers": [
{
"id": 3,
"name": "Customer Test3",
"customerNumber": "45678",
"createdDate": 1596117132045,
"lastModifiedDate": 1596117132045,
"_links": {
"self": {
"href": "http://localhost:2502/seminars/3"
}
},
"quotaId": 1
},
{
"id": 1,
"name": "test3",
"customerNumber": "12345",
"createdDate": 1596111304535,
"lastModifiedDate": 1596186450456,
"_links": {
"self": {
"href": "http://localhost:2502/seminars/1"
}
},
"quotaId": 1
},
{
"id": 2,
"name": "Customer Test2",
"customerNumber": "23456",
"createdDate": 1596112131934,
"lastModifiedDate": 1596112131934,
"_links": {
"self": {
"href": "http://localhost:2502/seminars/2"
}
},
"quotaId": 2
},
{
"id": 4,
"name": "Customer Test4",
"customerNumber": "34567",
"createdDate": 1596117145795,
"lastModifiedDate": 1596117145795,
"_links": {
"self": {
"href": "http://localhost:2502/seminars/4"
}
},
"quotaId": 2
},
{
"id": 6,
"name": "Customer Test6",
"customerNumber": "12345",
"createdDate": 1596187250598,
"lastModifiedDate": 1596187250598,
"_links": {
"self": {
"href": "http://localhost:2502/seminars/6"
}
},
"quotaId": null
}
]
},
"_links": {
"self": {
"href": "http://localhost:2502/seminars?page=0&size=20&sort=quota,asc"
}
},
"page": {
"size": 20,
"totalElements": 5,
"totalPages": 1,
"number": 0
}
}
What i get when i make a GET request in all Quotas:
{
"_embedded": {
"quotas": [
{
"id": 1,
"customers": [
{
"id": 3,
"name": "Customer Test3",
"customerNumber": "45678",
"createdDate": 1596117132045,
"lastModifiedDate": 1596117132045,
"quotaId": 1
},
{
"id": 1,
"name": "test3",
"customerNumber": "12345",
"createdDate": 1596111304535,
"lastModifiedDate": 1596186450456,
"quotaId": 1
}
],
"type": "paid",
"createdDate": 1596111304535,
"lastModifiedDate": 1596111304535,
"flatrate": true,
"_links": {
"self": {
"href": "http://localhost:2502/quotas/1"
}
}
},
{
"id": 2,
"customers": [
{
"id": 2,
"name": "Customer Test2",
"customerNumber": "23456",
"createdDate": 1596112131934,
"lastModifiedDate": 1596112131934,
"quotaId": 2
},
{
"id": 4,
"name": "Customer Test4",
"customerNumber": "34567",
"createdDate": 1596117145795,
"lastModifiedDate": 1596117145795,
"quotaId": 2
}
],
"type": "demo",
"createdDate": 1596111304535,
"lastModifiedDate": 1596111304535,
"flatrate": true,
"_links": {
"self": {
"href": "http://localhost:2502/quotas/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:2502/quotas?page=0&size=10&sort=id,asc"
}
},
"page": {
"size": 10,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
I am happy with the result from Customers, but the result from quotas i would want to look like this:
{
"_embedded": {
"quotas": [
{
"id": 1,
"customers": [3,1],
"type": "paid",
"createdDate": 1596111304535,
"lastModifiedDate": 1596111304535,
"flatrate": true,
"_links": {
"self": {
"href": "http://localhost:2502/quotas/1"
}
}
},
{
"id": 2,
"customers": [2,4],
"type": "demo",
"createdDate": 1596111304535,
"lastModifiedDate": 1596111304535,
"flatrate": true,
"_links": {
"self": {
"href": "http://localhost:2502/quotas/2"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:2502/quotas?page=0&size=10&sort=id,asc"
}
},
"page": {
"size": 10,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
For json serialization i use 'com.fasterxml.jackson.annotation.*'
I already tried it with using the #JsonBackRefference, #JsonIgnore, #JsonIngoreProperties and #JsonIdentityReference annotations but never got the wanted result.
I would recommend you introduce DTOs to model this rather than trying to annotate the entity classes with JSON annotations. At some point you will probably have conflicting needs i.e. one use case needs a field and another doesn't and then you will have to think about abstracting this anyway.
Having said that, this is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A DTO mapping for your model could look as simple as the following
#EntityView(Quota.class)
public abstract class QuotaDto extends RepresentationModel<QuotaDto> {
public abstract Integer getId();
#Mapping("customers.id")
public abstract List<Integer> getCustomers();
public abstract String getType();
public abstract boolean isFlatrate();
public abstract long getCreatedDate();
public abstract long getLastModifiedDate();
}
#EntityView(Customer.class)
public abstract class CustomerDto extends RepresentationModel<CustomerDto> {
public abstract Integer getId();
public abstract String getName();
public abstract String getCustomerNumber();
public abstract long getCreatedDate();
public abstract long getLastModifiedDate();
#Mapping("quota.id")
public abstract Integer getQuotaId();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
CustomerDto dto = entityViewManager.find(entityManager, CustomerDto.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
You are in control of the structure and can map anything to the properties. It will only fetch the mappings that you tell it to fetch.

change hasManyThrough() relation attribute name through accessor

I have 3 Models
Campaign PK(id)
CampaignMedium FK(campaign_id)
AccountReceivable FK(campaign_medium_id) (has an amount column)
Controller function:
public function all()
{
return Campaign::with(['customer', 'receivedPayments'])->get();
}
In Campaign Model relationships are defined as follows:
public function customer()
{
return $this->belongsTo(Customer::class);
}
public function accountReceivable()
{
return $this->hasManyThrough(AccountReceivable::class, CampaignMedium::class);
}
public function receivedPayments()
{
return $this->accountReceivable()
->selectRaw('sum(account_receivables.amount) as total')
->groupBy('campaign_id');
}
public function getReceivedPaymentsAttribute()
{
if (!array_key_exists('receivedPayments', $this->relations)) {
$this->load('receivedPayments');
}
$relation = $this->getRelation('receivedPayments')->first();
return ($relation) ? $relation->total : 0;
}
Final Output:
{
"data": [
{
"id": 8,
"name": "example",
"image": "campaign/90375849f6c3cc6b0e542a0e3e6295b890375849f6c3cc6b0e542a0e3e6295b8.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": [
{
"total": "700",
"laravel_through_key": 8
}
]
},
{
"id": 9,
"name": "example",
"image": "campaign/fff9fadc92a809513dc28134379851aafff9fadc92a809513dc28134379851aa.jpeg",
"amount": 10,
"description": "saddsa",
"start_at": "2019-02-12 00:00:00",
"end_at": "2019-02-12 00:00:00",
"due_at": "2019-02-12 00:00:00",
"status": "active",
"customer": {
"id": 1,
"name": "test",
"email": "info#test.com",
"image": "customer/ec812116705ff3ae85298234fe6c4e97ec812116705ff3ae85298234fe6c4e97.jpeg",
"address": "sample address"
},
"received_payments": []
}
]
}
summary: trying to get the sum of AccountReceivable amount attribute, which is working fine but the getReceivedPaymentsAttribute() isn't working which needs to return the total value only. also can anyone please help me to explain why laravel_through_key is added with received_payments?
I've never tried to use an attribute modifier to modify a relation this way. You are overriding the expected result of receivedPayments(). You might be better off to define a separate attribute like so:
public function getSumReceivedPaymentsAttribute()
{
// ...your code...
}
Now you can access the attribute using $model->sum_received_payments or always preload it using:
// model.php
protected $appends = ['sum_received_payments'];

How to convert hierarchical list to flat list?

Usually we convert flat list to hierarchical list but in my case I want to convert hierarchical list to flat list.
I have a hierarchical list<T> and I want convert this hierarchical list into flat list<T> class.
Let's say I have a below hierarchical list
Parent1
--- Child1 ,F1
----Child2, F2
----Child2, F3
Parent2
----Child2,F2
----Child4,F6
Parent3
--- Child1 ,F1
I need output like below:
Parent1, Child1,F1
Parent1, Child2,F2
Parent1, Child2,F3
Parent2,Child2,F2
Parent2,Child4, F6
Parent3, Child1,F1
It may not be the most optimized solution (it could be further minimized I think, and I have a more concise version - but this form is something I'm using the most)
It's what I've been using
public static IEnumerable<T> FlattenHierarchy<T>(this T node,
Func<T, IEnumerable<T>> getChildEnumerator)
{
yield return node;
if (getChildEnumerator(node) != null)
{
foreach (var child in getChildEnumerator(node))
{
foreach (var childOrDescendant
in child.FlattenHierarchy(getChildEnumerator))
{
yield return childOrDescendant;
}
}
}
}
and you can use it like
folder.FlattenHierarchy(x => x.SubFolders)
Finally i got the solution:
public class RNode
{
public string Id;
public long ID;
public string name;
public string subTitle;
public IList<RNode> children;
}
public class FlatObj //For Data format
{
public long Id;
public long ParentId;
public long Position;
public string name;
public string subTitle;
}
List<FlatObj> GlobalFlatObj = new List<FlatObj>();
List<long> ParentIdList = new List<long>();
long CurrentParentId=0;
long CurrentPosition = 0;
public List<FlatObj> FlatData(IList<RNode> HData) //Converting Heirarchical to Flat
{
foreach (RNode node in HData)
{
FlatObj ObjFlatObj = new FlatObj();
ObjFlatObj.Id = node.ID;
ObjFlatObj.name = node.name;
ObjFlatObj.ParentId = CurrentParentId;
ObjFlatObj.Position = CurrentPosition;
GlobalFlatObj.Add(ObjFlatObj);
if (node.children.Count > 0)
{
CurrentParentId = node.ID;
ParentIdList.Add(node.ID);
FlatData(node.children);
}
CurrentPosition++;
}
if (ParentIdList.Count > 0)
{
ParentIdList.RemoveAt(ParentIdList.Count - 1);
if (ParentIdList.Count > 0)
CurrentParentId = ParentIdList[ParentIdList.Count - 1];
CurrentPosition = 0;
}
return GlobalFlatObj;
}
public dynamic Test(List<RNode> EmployeeHierarchy)
{
var HierarchyResult = FlatData(EmployeeHierarchy); //Calling
return Ok(HierarchyResult);
}
Input:
[
{
"$id": "1",
"ID": 1,
"name": "root",
"subTitle": "root",
"children": [
{
"$id": "2",
"ID": 2,
"name": "child 1",
"subTitle": "child 1",
"children": [
{
"$id": "3",
"ID": 5,
"name": "grandchild",
"subTitle": "grandchild",
"children": []
}]
},
{
"$id": "4",
"ID": 3,
"name": "child 2",
"subTitle": "child 2",
"children": []
},
{
"$id": "5",
"ID": 4,
"name": "child 3",
"subTitle": "child 3",
"children": []
}
]
}
]
Output:
[
{
"$id": "1",
"Id": 1,
"ParentId": 0,
"Position": 0,
"name": "root",
"subTitle": null
},
{
"$id": "2",
"Id": 2,
"ParentId": 1,
"Position": 0,
"name": "child 1",
"subTitle": null
},
{
"$id": "3",
"Id": 5,
"ParentId": 2,
"Position": 0,
"name": "grandchild",
"subTitle": null
},
{
"$id": "4",
"Id": 3,
"ParentId": 1,
"Position": 1,
"name": "child 2",
"subTitle": null
},
{
"$id": "5",
"Id": 4,
"ParentId": 1,
"Position": 2,
"name": "child 3",
"subTitle": null
}
]

WebAPI 2 Odata Filter not working

I have a problem with using multiple filters on my WebAPI 2 Odata project.
We only want JSON output and only have one object type to query so we set the url to "/" without the possibility to use a different controller.
I have an object I want to query with the following properties:
public class Content
{
public int Id { get; set; }
public string Title { get; set; }
public string Excerpt { get; set; }
public string Link { get; set; }
public IList<Tag> Tags { get; set; }
}
public class Tag
{
public string Id { get; set; }
public string Name { get; set; }
}
And the controller code looks like this:
public class ContentController : ApiController
{
private readonly IContentRepository _repository;
// constructor
public ContentController(IContentRepository repository)
{
_repository = repository;
}
[Queryable]
public IQueryable<Content> Index()
{
// IContentRepository.GetAll returns an IEnumerable List of Content
return _repository.GetAll().AsQueryable();
}
}
Now, I've mocked some testdata with adding multiple objects to the repository that have multiple tags with values set to either (test1, test2 or test3). Now when i Query
http://localhost:xxx?$filter=Tags/any(o: o/Id eq 'test1')
I get all objects with Tag/Id set to 'test1'. But if I query
http://localhost:xxx?$filter=Tags/any(o: o/Id eq 'test1' and o/Id eq 'test2')
I get no result (JSON return = []). But it should return objects that have both tags.
What am I doing wrong?
EDIT:
My sample data JSON looks like this:
[
{
"Id": 1,
"Title": "TESTOBJECT 1",
"Excerpt": "",
"Link": "",
"Tags": [
{
"Id": "test1",
"Name": "Test Tag 1",
}
],
},
{
"Id": 2,
"Title": "TESTOBJECT 2",
"Excerpt": "",
"Link": "",
"Tags": [
{
"Id": "test2",
"Name": "Test Tag 2",
}
],
},
{
"Id": 3,
"Title": "TESTOBJECT 3",
"Excerpt": "",
"Link": "",
"Tags": [
{
"Id": "test3",
"Name": "Test Tag 3",
}
],
},
{
"Id": 4,
"Title": "TESTOBJECT 4",
"Excerpt": "",
"Link": "",
"Tags": [
{
"Id": "test1",
"Name": "Test Tag 1",
},
{
"Id": "test2",
"Name": "Test Tag 2",
}
],
},
{
"Id": 5,
"Title": "TESTOBJECT 5",
"Excerpt": "",
"Link": "",
"Tags": [
{
"Id": "test1",
"Name": "Test Tag 1",
}
],
}
]
Now query one gives me object 1,4,5 and I would expect query two to give me object 4. How can I accomplish this with odata?
Did you want this?
http://localhost:xxx?$filter=Tags/any(o: o/Id eq 'test1') and Tags/any(o: o/Id eq 'test2')
((X == "test1") AND (X == "test2")) will always return false!
If you want to identify Tags whose property Id is test1 or test2, use the or operator.

Resources