how to retrofit and room handle nested object - android-room

"provinsi": [
{
"createdDate": 1490089930310,
"createdBy": "SYSTEM",
"updatedDate": 1490089930310,
"updatedBy": "SYSTEM",
"id": 31,
"provinceName": "Kepulauan Riau",
"provinceCode": "0",
"mCities": [
{
"createdDate": 1490092020000,
"createdBy": "SYSTEM",
"updatedDate": 1490092020000,
"updatedBy": "SYSTEM",
"provinceName": "Kepulauan Riau",
"id": 198,
"cityName": "KAB.BINTAN",
"cityCode": "2102",
"centralBankCode": ""
},
{
"createdDate": 1490092020000,
"createdBy": "SYSTEM",
"updatedDate": 1490092020000,
"updatedBy": "SYSTEM",
"provinceName": "Kepulauan Riau",
"id": 350,
"cityName": "KAB.KARIMUN",
"cityCode": "2101",
"centralBankCode": "3801"
},
}
]
I tried to make it a relation table, or two tables. find good documentation for this but it's hard hahaha but can't find something like this

I think this will be a one-to-many relation. For example
Entity
#Entity
data class Dog(
#PrimaryKey val dogId: Long,
val dogOwnerId: Long,
val name: String,
val cuteness: Int,
val barkVolume: Int,
val breed: String
)
#Entity
data class Owner(#PrimaryKey val ownerId: Long, val name: String)
data class OwnerWithDogs(
val owner: Owner,
val dogs: List<Dog>
)
data class OwnerWithDogs(
#Embedded val owner: Owner,
#Relation(
parentColumn = "ownerId",
entityColumn = "dogOwnerId"
)
val dogs: List<Dog>
)
Dao
#Transaction
#Query("SELECT * FROM Owner")
fun getDogsAndOwners(): List<OwnerWithDogs>
You can see a good article at here.
I hope this will be helpful.

Related

java8 stream not showing the expected output

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());

Array in Android Room Relation

I have a class for Room relations:
data class PersonWithRelations(
#Embedded val person: Person,
#Relation(
parentColumn = "country_id",
entityColumn = "country_id",
) val country: Country?,
#Relation(
entity = EmergencyContact::class,
parentColumn = "person_id",
entityColumn = "person_id",
) val emergencyContacts: Array<EmergencyContactPerson>?,
// More relations here...
)
EmergencyContactPerson is another class helper for relation:
data class EmergencyContactPerson (
#Embedded val emergencyContact: EmergencyContact,
#Relation(
entity = Person::class,
parentColumn = "ec_person_id",
entityColumn = "person_id",
projection = ["person_id", "party_id", "first_name", "last_name"]
) val person: PersonWithRelations,
)
But I've the following error:
.../PersonWithRelations.java:18: error: Entity type in a Relation must be a class or an interface.
private final com.data.model.relations.EmergencyContactPerson[] emergencyContacts = null;
Everything works except when I add the Array<T> type. How to have an array of these Room relation helper classes inside another relation helper class?
This one of the classes with I'm strugglin with. Additionally, I've the following classes presenting the same problem (How to have array of relation helper classes?):
data class PartyWithRelations(
#Embedded val party: Party,
#Relation(
parentColumn = "party_id",
entityColumn = "party_id",
) val emailAddresses: Array<Email>?,
#Relation(
entity = Phone::class,
parentColumn = "party_id",
entityColumn = "party_id",
) val phoneNumbers: Array<PhoneWithRelations>?,
#Relation(
entity = PostalAddress::class,
parentColumn = "party_id",
entityColumn = "party_id",
) val postalAddresses: Array<PostalAddressWithRelations>?,
)
PhoneWithRelations and PostalAddressWithRelations are as follows:
data class PhoneWithRelations (
#Embedded val phone: Phone,
#Relation(
parentColumn = "phone_type_id",
entityColumn = "phone_type_id",
) val type: PhoneType?,
)
data class PostalAddressWithRelations(
#Embedded val postalAddress: PostalAddress,
#Relation(
parentColumn = "county_id",
entityColumn = "county_id",
) val county: County,
#Relation(
parentColumn = "geopoint_id",
entityColumn = "geopoint_id",
) val geopoint: Geopoint,
#Relation(
parentColumn = "default_geopoint_id",
entityColumn = "geopoint_id",
) val defaultGeopoint: Geopoint,
)
If I remove the Array type from all of theses relations, they do work, but I'm needing to return an Array of those types. For further information, those classes should look like the following JSON (Not intended to be a JSON/String type of return, the following JSON is just for having a picture):
"party": {
"partyId": 76730,
"emailAddresses": [],
"phoneNumbers": [
{
"phoneNumberId": 73376,
"areaCode": "111",
"phoneNumber": "5554433",
"type": {
"phoneNumberTypeId": 61,
"displayText": "Cell",
"sortOrder": 2
},
"priority": 1
}
],
"postalAddresses": [
{
"postalAddressId": 47857,
"street1": "STREET1",
"street2": "STREET2",
"city": "Lafayette",
"stateCode": "IN",
"zipCode": "CODE",
"county": {
"countyId": 3114,
"name": "Tippecanoe",
"isIndependentCity": false,
"stateCode": "IN"
},
"geopoint": {
"geopointId": 41278,
"lat": "LAT",
"lng": "LNG"
},
"defaultGeopoint": {
"geopointId": 41278,
"lat": "LAT",
"lng": "LNG"
}
}
]
},
Also, EmergencyContactPerson should look like this:
"person": {
"personId": 68206,
"firstName": "First Name",
"middleName": "",
"lastName": "Last Name",
"country": {
"countryId": 981,
"displayText": "United States"
},
"emergencyContacts": [
{
"emergencyContactId": 22949,
"person": {
"personId": 68207,
"party": {
"partyId": 76731,
"emailAddresses": [],
"phoneNumbers": [],
"postalAddresses": []
},
"firstName": "First Name",
"lastName": "Last Name"
},
"priority": 1,
"relationship": "Daughter",
"notes": "Primary Contact",
"showOnRouteSheets": false
},
],
},

Load model with with association

I would like to load the post and is author id and email without loading anything else.
It doesn't need to be eager loading I just need to get all posts and include only the authors id and username
My current query
posts := []interfaces.Post{}
db.Model("User").Find(&posts)
type Post struct{
gorm.Model
Title string
Body string
UserID uint
User User
}
type User struct{
gorm.Model
Username string
Email string
Password string
}
Current response
{
"ID": 1,
"CreatedAt": "2021-01-09T19:11:42.063274-05:00",
"UpdatedAt": "2021-01-09T19:11:42.063274-05:00",
"DeletedAt": null,
"Title": "What does the fox say",
"Body": "whawhhwjg",
"UserID": 1,
"User": {
"ID": 1,
"CreatedAt": "2021-01-09T19:01:28.70267-05:00",
"UpdatedAt": "2021-01-09T19:01:28.70267-05:00",
"DeletedAt": null,
"Username": "12345",
"Email": "1112#gmail.com",
"Password": "$2a$04$T1841Dc52MwjSJ2PaPnTwuFASai6zkGw8WFcuQbO1fi9Nug7R3Iqq"
}
},
Response I'm looking for
{
"ID": 1,
"CreatedAt": "2021-01-09T19:11:42.063274-05:00",
"UpdatedAt": "2021-01-09T19:11:42.063274-05:00",
"DeletedAt": null,
"Title": "What does the fox say",
"Body": "whawhhwjg",
"UserID": 1,
"User": {
"ID": 1,
"Username": "12345",
}
},
You can use the Select method to select the specific fields.
dm.Model("User").Select("ID", "Email", "Username").Find(&posts)
Or, you can use the Preload method like this.
db.Preload("User", func (db *gorm.DB) *gorm.DB {
return db.Select("ID", "Email", "Username")
}).
Find(&posts)
You can also do it in this way
db.Preload("User").Select("ID", "Username").Find(&posts)

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.

Spring Data Rest output join column object as JSON field when querying single resource

I have Apartment entity:
#Entity
public class Apartment extends AbstractEntity {
private String name;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(nullable = false)
#RestResource(exported = false)
private Address address;
private String website;
#OneToMany(mappedBy = "apartment")
#RestResource(exported = false)
private Set<FloorPlan> floorPlans;
...
FloorPlan entity:
#Entity
public class FloorPlan extends AbstractEntity {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "apt_id", nullable = false)
private Apartment apartment;
private float bed;
private float bath;
private int priceFrom;
...
I applied excerptProjection to Floorplan to only show bed, bath and priceFrom. When I query apartments collection, the json output looks ok:
{
"_embedded": {
"apartments": [
{
"name": "Avalon Silicon Valley",
"website": "https://www.avaloncommunities.com/california/sunnyvale-apartments/avalon-silicon-valley",
"address": {
"streetNumber": "1257",
"street": "Lakeside Drive",
"city": "Sunnyvale",
"state": "CA",
"zipCode": "94085",
"fullAddress": "1257 Lakeside Drive, Sunnyvale, CA 94085"
},
"floorPlans": [
{
"bed": 3,
"bath": 3,
"priceFrom": 4495
},
{
"bed": 3,
"bath": 2,
"priceFrom": 4760
},
However if I do a single resource like http://localhost:8080/ag-api/apartments/1
floorplans will output Apartment Object as one of its field:
{
"name": "Avalon Silicon Valley",
"address": {
"streetNumber": "1257",
"street": "Lakeside Drive",
"city": "Sunnyvale",
"state": "CA",
"zipCode": "94085",
"fullAddress": "1257 Lakeside Drive, Sunnyvale, CA 94085"
},
"website": "https://www.avaloncommunities.com/california/sunnyvale-apartments/avalon-silicon-valley",
"floorPlans": [
{
"bed": 3,
"bath": 3,
"priceFrom": 4495,
"_embedded": {
"apartment": {
"name": "Avalon Silicon Valley",
"website": "https://www.avaloncommunities.com/california/sunnyvale-apartments/avalon-silicon-valley",
"address": {
"streetNumber": "1257",
"street": "Lakeside Drive",
"city": "Sunnyvale",
"state": "CA",
"zipCode": "94085",
"fullAddress": "1257 Lakeside Drive, Sunnyvale, CA 94085"
},
"floorPlans": [
Anyone got an idea what might be going on? really appreciate it. Thanks
Not sure if it will help, but in documentation I found in order to omit "_embedded" in you response you'll need to add:
"spring.hateoas.use-hal-as-default-json-media-type=false" to application.properties.

Resources