I have been taking advantage of Jackson JSON parser to map out my objects. While testing out the api, I have come across an issue with creating/updating my object that includes nested objects.
Creating and Updating main object
Airplane plane = airplanes.get(planeId);
if(plane == null){
plane = mapper.readValue(jsonNode, Airplane.class)
}else{
mapper.readerForUpdating(plane).readValue(jsonNode);
}
Example of objects:
public class Airplane {
private static final String TAG = "Airplane Model";
#JsonProperty("id")
private String mId;
#JsonProperty("company")
private String mCompany;
#JsonProperty("pilot")
private Pilot mPilot;
#JsonProperty("passenger")
private Passenger mPassenger;
public Airplane() {
}
}
public class Pilot {
private static final String TAG = "Pilot Model";
#JsonProperty("id")
private String mId;
#JsonProperty("name")
private String mName;
public Pilot() {
//keeps getting called on airplane reader update
}
}
Everything maps correctly, however the issue is that every time an airplane object is updated, it creates a new nested object of 'Pilot' as commented in the Pilot() constructor. This becomes a larger issue because the airplane model is being updated by a web socket at a small time interval (Unnecessary object instantiation). Additionally, I am setting non-mapped fields in the Pilot object which are being lost due to a new Pilot object being created on every update.
What is the proper way to update an object via Jackson with nested objects? Am I missing any annotations to prevent repetitive instantiation of my nested objects.
Related
I'm trying to identify the best way to do mongodb object versioning.
Based on the mongodb document versioning pattern, storing revisions in a history collection and having current version in main collection is recommended. According to that, each revision contains the complete object instead of storing diffs.
Then I went through ways to implement data versioning in mongoDB where it recommends a method to store a single document containing all the revisions inside it having a separate history collection.
Therefore, I'm trying to implement my own object versioning implementation for the following document model due its complexity.
Invoice.java
public class Invoice {
private long invoiceId;
private String userName;
private String userId;
private LocalDateTime createdDate;
private LocalDateTime lastModifiedDate;
private List<String> operationalUnits;
private List<BodyModel> body;
private List<ReviewModel> reviews;
private BigDecimal changedPrice;
private String submitterId;
private LocalDateTime submittedTime;
private String approverId;
private LocalDateTime approvedTime;
}
BodyModel.java
public class BodyModel {
private String packageId;
private List<ItemModel> items;
private List<String> reviews;
}
ReviewModel.java
public class ReviewModel {
private String publishedTime;
private String authorName;
private String authorId;
private String text;
}
ItemModel.java
public class ItemModel {
private String itemNumber;
private String description;
private String brandId;
private String packId;
private List<String> reviews;
}
ER Diagram (Simplified)
At the moment, I'm using Javers library. But, Javers keeps the Invoice model as the main entity and other models such as BodyModel, ReviewModel, ItemModel as separated valueObjects. As a result, instead of creating a single revision document, it creates separated documents for the valueObjects. Additionally, it always constructs the current objects from the base version plus all changes which leads to huge read time. Addtionally, I identified a valueObjects issue that comes with javers. Refer this question for more info: MongoDB document version update issue with JaVers
Following are the issues, I've if I'm going to create my own implementation using spring boot.
If I'm going to put revisionId in each of the revisions (as shown in the below object) when mongoDB save(), how do I find the current revisionId to be included ?
{
_Id: <ObjectId>,
invoiceId: <InvoiceId>,
invoice: <Invoice>,
revisionId: <revisionId>
}
For this I can keep a field for revisionId in InvoiceModel which can be updated when saving to the main collection. And at the same time, it can be used to save the revision into history collection. Is it the best possible way to do it in this case ?
If I'm only going to store diffs, then how do I obtain the current version of the object ?
For this, it feels essential to fetch the current object from the main collection and then compare it with new version (I already have the new version, because that's what I'm going to store in main collection) to create the diff. Then diff can be stored in history collection and new version in main collection. Is it the best possible way to store diffs ?
In both scnearios, aspects coming from AOP can be used to intercept the save() method of the base repository to accomplish the task. But I don't mainly consider about coding implementation details, I would like to know which method or methods would be efficient in storing revisions for a data model such as given above (would like to discuss about methods I've mentioned as well) ?
I'm developing a simple system for parcel service. I don't fully grasp on how to do this part when setting status of the parcel. When you login you can see all available orders. As a courier you can mark new orders as "accepted" or "rejected". You can mark "accepted" orders as "in transit". And "in transit" orders you can mark as "delivered" or "failed to deliver". My question is do I need to create a field "status" and every time set some kind of string, or I should do a boolean field? By the way, if I would "reject" an order. How could I 'remember' that this particular courier has already rejected that particular oder and do not show it to him? Thank you for your ideas.
Create an Enum and define a variable of that type in your Order object and your business logic should do the setting and interpreting the enum values in your application.
public enum ORDER_STATUS {
ACCEPTED,REJECTED,DELIVERED,FAILED_TO_DELIVER,REJECT;
}
public class Order {
private Long id;
private ORDER_STATUS orderStatus;
}
You can make the enum as an inner static class. Its simpler and faster because you do not have to create an object of ORDER_STATUS.
public class Order {
private final String id;
private final String name;
private final ORDER_STATUS order_status;
public static enum ORDER_STATUS {
ACCEPTED,REJECTED,DELIVERED,FAILED_TO_DELIVER,REJECT;
}
}
I am new to Java and am using Java Eclipse, so please be kind! I hope I'm going to pose this question correctly so it makes sense.
I have four domains - each domain is pulling data from four different servers, hence the need to have them separate. But now I need to create a report that links all the data from the four domains into one report. Someone suggested using hashmaps, which I haven't used before. My four domains each have two fields that can be used as a key - CostCenter and Serial. The data being pulled is from machines all over the country. I need all the data for each machine in one report.
This is all being added to an existing project that creates a webpage with tabs for the user to click on for various tables and get data specific to a location, or to create a report for each page for all machines/locations. I just need to add a new link for the user to click on that will create this spreadsheet for them.
I've already created the domains (DAO, DAOImpl, DTO, and so on) and then I was going to create the combined report in my MainService.java. Here are the domains (lists) as declared in my MainService:
public List<Volume> getVolumeReport();
public List<BadFmPumps> getBadFmPumpsReport();
public List<BadCorobPumps> getBadCorobPumpsReport();
public List<McService> getMcServiceReport();
And here is data being pulled from the databases for each of them (domains):
public class Volume {
private String costCenter;
private String DAD;
private String division;
private String model;
private String serial;
private String numDispensers;
private String colorantSys;
private String CCEGals2017;
private String BACGals2017;
private String CCEGals2018;
private String BACGals2018;
private String DNR2017;
private String DNR2018;
private String DNR2019;
public class BadFmPumps {
private String costCenter;
private String model;
private String serial;
private String badFmPumps;
private String over10;
private String under10;
public class BadCorobPumps {
private String costCenter;
private String model;
private String serial;
private String badPumpCount;
public class McService {
private String costCenter;
private String model;
private String serial;
private String crChargeTotals;
private String emgCalls;
So I need to pull this data into one report wherever CostCenter + Serial matches. How do I declare the hashmaps for each object and how do I declare the key?
EDIT ----
I think I have something close here with
public List<Volume> getVolumeReport();
Map<String, Volume> VolumeMap = new HashMap<String, Volume>();
for (Volume dispenser : VolumeList)
{
String volumeKey = new StringBuilder().append(Volume.getCostCenter()).append(Volume.getSerial()).toString();
VolumeMap.put(volumeKey, dispenser);
}
Is this correct? I am getting one syntax error - the Map declaration
Map<String, Volume> VolumeMap = new HashMap<String, Volume>();
is giving me the error
Syntax error on token ";", { expected after this token
Is there something I need to change there?
There are some unusual things in your code. My guess is that you came from C# you are not using proper naming conventions see it here: https://www.oracle.com/technetwork/java/codeconventions-135099.html
You defined your method wrong, the error is not in the Map but the method definition
public List<Volume> getVolumeReport(); <-------- this
Should be
public List<Volume> getVolumeReport() {
And then close your method at its end (using }).
And inside your FOR you trying to direct access the Volume methods when you should use the variable you created: dispenser
String volumeKey = new StringBuilder()
.append(Volume.getCostCenter())
.append(Volume.getSerial())
.toString();
Should be
String volumeKey = new StringBuilder()
.append(dispenser.getCostCenter())
.append(dispenser.getSerial())
.toString();
I need to find a proper solution to have a Spring-Boot #Component (singleton) class hold a List of database table objects, which could be accessed throughout the life of an application. I need to get a value of a certain language column value (there could be many language columns) depending on the parameters.
My idea was to do it like this:
#Component
public class CardTypeValueComponent {
private List<CardTypesTabModel> listOfCardTypes;
private CardTypesModelRepository cardTypesModelRepository;
private static final String UNKNOWN = "-";
#Autowired
public CardTypeValueComponent(CardTypesModelRepository cardTypesModelRepository) {
Assert.notNull(cardTypesModelRepository, "CardTypesModelRepository cannot be null");
this.cardTypesModelRepository = cardTypesModelRepository;
}
#PostConstruct
private void getAllCardTypesFromDb() {
this.listOfCardTypes = cardTypesModelRepository.findAll();
}
public String getCardTypeLanguageValue(int cardType, String language) {
String cardTypeLangValue = UNKNOWN;
for (CardTypesTabModel cardTypesTabModel : listOfCardTypes) {
if (cardTypesTabModel.getTypeId() == cardType && "spanish".equals(language)) {
cardTypeLangValue = cardTypesTabModel.getSpanishValue();
} else {
cardTypeLangValue = cardTypesTabModel.getEnglishValue();
}
}
return cardTypeLangValue;
}
}
Is it a proper way of completing such a task whilst keeping in mind that the table object column count could increase in the future?
Excuse me for the pseudo code. Thanks.
Added more details:
CardTypesTabModel Entity class:
#Entity
public class CardTypesTabModel {
private int type;
private String englishValue;
private String spanishValue;
// other values, getters & setters
}
What you're trying to do is re-inventing the caching mechanisme.
You may consider to relay on the Spring Cache Abstraction http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html then choose JCache (JSR-107) as implementation.
I am using gson to produce json of a collection of objects in Java (Some objects have other collections too). This json will be used to populate the web page for users with different clearance levels. Therefore the detail which users can see differs. Web page only shows what it needs to show however if I use the same json for two different pages then html source code will have more data than it should have. Is there a way to inform gson which variables in which class should be added to the json? As far as I search I could not find an easy way. Either I will produce json myself or clear extra data from the json which gson produced.
I need to use same classes for different clearance levels and get different json.
You are trying to use Gson to generate multiple different JSON outputs of the same objects in the same JVM, which is going to be difficult, both in Gson and any good serialization library, because their express goal is essentially the opposite of what you're looking for.
The right thing to do would be to instead represent these different clearance levels with different classes, and simply serialize those different classes with Gson as normal. This way you separate the security model from the serialization, letting you safely pass this information around.
/**
* Core data class, contains all information the application needs.
* Should never be serialized for display to any end user, no matter their level.
*/
public class GlobalData {
private final String username;
private final String private_data;
private final String secure_data;
}
/** Interface for all data display operations */
public interface DisplayData {
/** Returns a JSON representation of the data to be displayed */
public String toJson();
}
/**
* Class for safe display to an untrusted user, only holds onto public
* data anyone should see.
*/
public class UserDisplayData implements DisplayData {
private final String username;
public UserDisplayData(GlobalData gd) {
username = gd.username;
}
public String toJson() {
return gson.toJson(this);
}
}
/**
* Class for safe display to a trusted user, holds private information but
* does not display secure content (passwords, credit cards, etc.) that even
* admins should not see.
*/
public class AdminDisplayData implements DisplayData {
private final String username;
private final String private_data;
public AdminDisplayData(GlobalData gd) {
username = gd.username;
private_data = gd.private_data;
}
public String toJson() {
// these could be different Gson instances, for instance
// admin might want to see nulls, while users might not.
return gson.toJson(this);
}
}
Now you can sanitize and serialize your data as two separate steps, and use type safety to ensure your GlobalData is never displayed.
public void getDisplayData(GlobalData gd, User user) {
if(user.isAdmin()) {
return new AdminDisplayData(gd);
} else {
return new UserDisplayData(gd);
}
}
public void showData(DisplayData data) {
String json = data.toJson();
// display json however you want
}
If you erroneously tried to call showData(gd) you'd get a clear compilation error that you've done something wrong, and it's a quick fix to get the correct result by calling showData(getDisplayData(gd, user)) which safely and clearly does exactly what you want.
you can add a Expose annotations like this on the filed you don't want:
#Expose(serialize = false, deserialize = false)
private String address;
some more information here:
https://sites.google.com/site/gson/gson-user-guide#TOC-Gson-s-Expose