Patching an entity and getting duplicate items - java

I'm having problems with editing an entity. I have ObjectSpecifications that have different conditions. The problem I have has to do with the DimensionalConditions.
Whenever I edit an ObjectSpecification which has let's say "length: 1", and I try to add "width: 2" to it, the conditions end up being "length: 1, length: 1, width: 2".
Long story short, the conditions that were present before, get inserted again for some reason.
public ObjectSpecification modifySpecification(Long id, ObjectSpecificationRequest request) {
ObjectSpecification objectSpecification = this.getObjectSpecificationById(id);
return this.setSpecData(request, objectSpecification);
}
public ObjectSpecification createSpecification(ObjectSpecificationRequest request){
return this.setSpecData(request, new ObjectSpecification());
}
public void deleteSpecification(Long id) {
objectSpecificationRepository.delete(getObjectSpecificationById(id));
}
public ObjectSpecification setSpecData(ObjectSpecificationRequest request, ObjectSpecification objectSpecification) {
if (request.getName() != null) {
objectSpecification.setName(request.getName());
}
if (request.getDimensionalConditions() != null) {
List<DimensionalCondition> dimensionalConditions = new ArrayList<>();
for (DimensionalCondition condition:request.getDimensionalConditions()) {
DimensionalCondition dimensionalCondition = new DimensionalCondition();
dimensionalCondition.setConditionType(condition.getConditionType());
dimensionalCondition.setValue(condition.getValue());
dimensionalCondition.setObjectSpecification(objectSpecification);
dimensionalConditions.add(dimensionalCondition);
}
objectSpecification.setDimensionalConditions(dimensionalConditions);
}
if (request.getMechanisms() != null) {
objectSpecification.setMechanismConditions(request.getMechanisms());
}
if (request.getServices() != null) {
objectSpecification.setServiceConditions(request.getServices());
}
if (request.getWorkDetails() != null) {
List<WorkDetail> workDetails = new ArrayList<>();
for (WorkDetail workDetail:request.getWorkDetails()) {
workDetails.add(workDetailService.getWorkDetailById(workDetail.getId()));
}
objectSpecification.setWorkDetailConditions(workDetails);
}
return objectSpecificationRepository.save(objectSpecification);
}

1) This is a big update list there, make sure you merge your ObjectSpecification so that it is aligned with the PersistenceContext before you do any amendments.
2) You do not take under consideration the existing DimensionalCondition that are linked to ObjectSpecification. You always create a new one:
List<DimensionalCondition> dimensionalConditions = new ArrayList<>();
3) Make sure you have your cascading configured to {PERSIST, MERGE} at least on #OneToMany List<DimensionalCondition>.

Related

Need to add data returned from api to List, but can only post to LiveData?

From my fragment I call: videoViewModel.fetchContentSections();
From my vm I call: public void fetchContentSections(){repository.getContent();}
From my repo I do this:
apiService.getContent(request).enqueue(new Callback<Content>() {
#Override
public void onResponse(Call<Content> call, Response<Content> response) {
List<Section> sections = response.body() != null ? response.body().getSections() : null;
if (sections != null && !sections.isEmpty()) {
final List<Section> sectionList = new ArrayList<>();
for (Section section : sections) {
sectionList.add(section);
}
}
}
#Override
public void onFailure(Call<Content> call, Throwable t) {
Log.d(TAG, "onFailure" + Thread.currentThread().getName());
}
});
Data is returned, but in this scenario the list is null.
If I substitute the if statement for: sectionsMutableLiveList.postValue(response.body().getSections());
...everything works fine. But I need to use a non-LiveData list so I can then write the sectionList to a file. I'm hoping to then read the list from the file and post the value to a LiveData list to my vm.
Does anyone know what I've done wrong?

How to optimalize if else statemants with many specifications

I am trying to create dynamic search based on fields send in request body.
I prepared many Specifications and in "summary specification" (which is called in method) I want to call them if field is different than null.
It works but the problem is I will never know which parameter will start creating condition so I had to add boolean parameter which resulted in the creation of many if else statements.
Code:
public Specification<ShapeEntity> conditionalSearch(ShapeParams shapeParams) {
Specification spec = null;
boolean isFirstParam = true;
if (shapeParams.getType() != null) {
if (isFirstParam) {
spec = Specification.where(isTypeEqual(shapeParams.getType()));
isFirstParam = false;
} else {
spec = spec.and(isTypeEqual(shapeParams.getType()));
}
}
if (shapeParams.getWidthTo() != null) {
if (isFirstParam) {
spec = Specification.where(isWidthLessThan(shapeParams.getWidthTo()));
isFirstParam = false;
} else {
spec = spec.and(isWidthLessThan(shapeParams.getWidthTo()));
}
}
if (shapeParams.getWidthFrom() != null) {
if (isFirstParam) {
spec = Specification.where(isWidthGreaterThan(shapeParams.getWidthTo()));
isFirstParam = false;
} else {
spec = spec.and(isWidthGreaterThan(shapeParams.getWidthTo()));
}
}
return spec;
}
Is there any way to optimalize it? Specification has to always start with ".where" as first, and next I can add other conditions and I would like to have even 10+ params
You can write some methods that receive some values to validate and return boolean.
boolean checkType(CustomObject type){
return type == null;
}
You can check the use of Optional, it maybe helps with some if blocks.
Optional.ofNullable(type).ifPresent(t -> /*some operations*/);
You can check if you can merge some conditions.
if (shapeParams.getType() != null && isFirstParam) {
//do something....
} else {
//do other....
}

merge two lists of different objects

i'm using api requests that returns a list.
-the first api request returns a list of object that contains (user_id,content,date,title)
-the second response returns list of object too that contains (user_id,user_name).
i want to merge the two list the display them into one recycler view but keep user name instead of user_id.this image breaks down what i want clearly.
apprecuiate any help i'm really stuck in this and i need it ty .
EDIT
this is the first api call :
followuplist=new ArrayList<>();
Retrofit retrofit = RetrofitInstance.getRetrofitInstance();
final Api api = retrofit.create(Api.class);
Call<List<TraitementTicketModel>> call = api.getfollowup(id, sestoken);
call.enqueue(new Callback<List<TraitementTicketModel>>() {
#Override
public void onResponse(Call<List<TraitementTicketModel>> call, Response<List<TraitementTicketModel>> response) {
if (!response.isSuccessful()) {
Toast.makeText(getApplicationContext(), "Something is wrong !! ", Toast.LENGTH_LONG).show();
Log.e("TAG", "onResponse: something is wrong");
} else if (response.body() == null) {
return;
}
List<TraitementTicketModel> followups = response.body();
for (TraitementTicketModel followup : followups) {
followuplist.add(followup);
}
followuplist.add(firstfollowup());
}
#Override
public void onFailure(Call<List<TraitementTicketModel>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"Pas de connextion internet",Toast.LENGTH_LONG).show();
}
});
this is the second api call :
List<User> userList;
SharedPreferences sp =getApplicationContext().getSharedPreferences("tokenPref", Context.MODE_PRIVATE);
String sestoken = sp.getString("token","");
Retrofit retrofit= RetrofitInstance.getRetrofitInstance();
final Api api= retrofit.create(Api.class);
Call<List<User>> call = api.getUser(sestoken);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.code() != 200){
Log.e("TAG", "onResponse: something is wrong"+response.code() );
}
List<User> users = response.body();
for (User user : users){
userList.add(user);
}
swipeRefreshLayout.setRefreshing(false);
}
so I have two liststhe first one is :
followuplist (user_id,title,content,date)
and the second :
userList(user_id,user_name)
but i didn't know what to do after that to get to my goal
You can do something like that.
In this example UserDetails is the object on the left in your image, UserInfo the one on the right, and MergeData the result.
You should use Kotlin instead of Java, it's far easier to manipulate lists.
List<MergedData> mergeList(
List<UserDetails> listUserDetails,
List<UserInfo> listUserInfo
) {
// Resulting list
final List<MergedData> result = new ArrayList<>();
// We iterate through the first list
for (UserDetails details : listUserDetails) {
// For each element of the list we will try to find one with the same user id in the other list
for (UserInfo info : listUserInfo) {
// if the current element of the second list has the same user id as the current one from the first list, we merge the data in a new object and this object is then added to the result list.
if (details.getUserId().equals(info.getUserId())) {
result.add(
new MergedData(
info.getName(),
details.getContent(),
details.getTitre(),
details.getDate()
)
);
// Once the object is found it is unnecessary to continue looping though the second list, so we break the for loop.
break;
}
}
}
// Once we finished to iterate through the first list, we return the result.
return result;
}
Same example in Kotlin:
fun mergeList(
listUserDetails: List<UserDetails>,
listUserInfo: List<UserInfo>
): List<MergedData> =
listUserDetails.mapNotNull { details ->
listUserInfo
.firstOrNull { it.userId == details.userId }
?.let { info ->
MergedData(
info.name,
details.content,
details.titre,
details.date
)
}
}

Best way to create a service that inserts a complex entity

Hope you are all healthy!
I have a situation in which I have a backend application that exposes a rest api to create data related to expense reporting. The main entity that this API allows to be created is an "Expense Report" which has other entities related to it, such as the country on which the expenses took place and the user that created it.
Thing is, my controller receives a DTO, converts it into a JPA entity and then sends it to the service class. In my service class, I have to check if a related entity field like the username for User or the country code for Country, and then go into the corresponding entity repository and get the corresponding entity.
public ExpenseReport save(ExpenseReport expenseReport) {
if (expenseReport.getId() != null) {
expenseReportRepository.findById(expenseReport.getId())
.ifPresent(currentObject -> {
expenseReport.setId(currentObject.getId());
expenseReport.setVersion(currentObject.getVersion());
});
}
if (expenseReport.getUser() != null && expenseReport.getUser().getUsername() != null) {
String username = expenseReport.getUser().getUsername();
userRepository.findByUsername(username)
.ifPresentOrElse(user -> {
expenseReport.setUser(user);
},
() -> {
throw new InvalidDataException(User.class, "username", username);
});
}
if (expenseReport.getCountry() != null && expenseReport.getCountry().getCode() != null) {
String countryCode = expenseReport.getCountry().getCode();
countryRepository.findByCode(countryCode)
.ifPresentOrElse(country -> {
expenseReport.setCountry(country);
},
() -> {
throw new InvalidDataException(Country.class, "countryCode", countryCode);
});
}
for (ExpenseItem expenseItem : expenseReport.getExpenses()) {
if (expenseItem.getCurrency() != null && expenseItem.getCurrency().getCode() != null) {
String currencyCode = expenseItem.getCurrency().getCode();
currencyRepository.findByCode(currencyCode)
.ifPresentOrElse(currency -> {
expenseItem.setCurrency(currency);
},
() -> {
throw new InvalidDataException(Currency.class, "currencyCode", currencyCode);
});
}
if (expenseItem.getExpenseCity() != null && expenseItem.getExpenseCity().getCode() != null) {
String expenseCityCode = expenseItem.getExpenseCity().getCode();
cityRepository.findByCode(expenseCityCode)
.ifPresentOrElse(city -> {
expenseItem.setExpenseCity(city);
},
() -> {
throw new InvalidDataException(City.class, "expenseCityCode", expenseCityCode);
});
}
if (expenseItem.getCategory() != null && expenseItem.getCategory().getCode() != null) {
String categoryCode = expenseItem.getCategory().getCode();
categoryRepository.findByCode(categoryCode)
.ifPresentOrElse(expenseCategory -> {
expenseItem.setCategory(expenseCategory);
},
() -> {
throw new InvalidDataException(ExpenseCategory.class, "expenseCategoryCode", categoryCode);
});
}
}
return expenseReportRepository.save(expenseReport);
}
My question is, is this the best way to do this? I feel that if the object gets too complex, I'll have to create this super huge save method.
Does JPA offer a better solution to this? I was thinking also to change the parameterized types (like country, city, state) to use the code itself as it's primary key, rather than an auto-generated id.
Regards.

GXT-3 issue to load data for my Chart

My problem is annoying. My server side is generating 12 random numbers (double here).
My Client side received the correct data but nothing is displayed in my Chart. That worked fine with hardcoded data in the store but not with a REST call.
The transfer between my server and my client is that :
[{"key":"key0","value":0.47222548599297787},{"key":"key1","value":0.6009173797369691},{"key":"key2","value":0.13880104282435624},{"key":"key3","value":0.01804674319345545},{"key":"key4","value":0.5547733564202956},{"key":"key5","value":0.8229999661308851},{"key":"key6","value":0.8959346004391032},{"key":"key7","value":0.6848052288628435},{"key":"key8","value":0.10222856671111813},{"key":"key9","value":0.6931371931409103},{"key":"key10","value":0.2994297934549003},{"key":"key11","value":0.47566752196381334}]
Here my simple class used for my test. I am a newbie with GXT 3
public void onModuleLoad() {
final ListStore<JSOModel> store;
final ContentPanel panel = new FramedPanel();
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/ws/DocumentService/v1/test");
builder.setHeader("Accept", "application/json");
HttpProxy proxy = new HttpProxy(builder);
final Loader<ListLoadConfig, ListLoadResult<JSOModel>> loader = new ListLoader<ListLoadConfig, ListLoadResult<JSOModel>>(proxy, new DataReader<ListLoadResult<JSOModel>, String>() {
#Override
public ListLoadResult<JSOModel> read(Object loadConfig, String data) {
List<JSOModel> jsoModels = new ArrayList<JSOModel>();
JsArray<JSOModel> jsoModelJsArray = JSOModel.arrayFromJson(data);
if(jsoModelJsArray != null) {
for(int i = 0; i < jsoModelJsArray.length(); i++) {
jsoModels.add(jsoModelJsArray.get(i));
}
}
return new ListLoadResultBean<JSOModel>(jsoModels);
}
});
store = new ListStore<JSOModel>(new ModelKeyProvider<JSOModel>() {
#Override
public String getKey(JSOModel item) {
return item.get("key");
}
});
loader.addLoadHandler(new LoadResultListStoreBinding<ListLoadConfig, JSOModel, ListLoadResult<JSOModel>>(store) {
#Override
public void onLoad(LoadEvent<ListLoadConfig, ListLoadResult<JSOModel>> event) {
ListLoadResult<JSOModel> loaded = event.getLoadResult();
if(loaded.getData() == null) {
store.replaceAll(new ArrayList<JSOModel>());
} else {
store.replaceAll(loaded.getData());
}
}
});
Chart<JSOModel> chart = new Chart<JSOModel>();
chart.setStore(store);
chart.setShadowChart(true);
NumericAxis<JSOModel> axis = new NumericAxis<JSOModel>();
axis.setPosition(Chart.Position.LEFT);
axis.addField(new ValueProvider<JSOModel, Number>() {
#Override
public Number getValue(JSOModel JSOModel) {
return JSOModel.getNumber("value");
}
#Override
public void setValue(JSOModel JSOModel, Number number) {
}
#Override
public String getPath() {
return "key";
}
});
axis.setTitleConfig(new TextSprite("Number of hits"));
axis.setWidth(50);
axis.setMinimum(0);
axis.setMaximum(100);
chart.addAxis(axis);
PathSprite odd = new PathSprite();
odd.setOpacity(1);
odd.setFill(new Color("#dff"));
odd.setStroke(new Color("#aaa"));
odd.setStrokeWidth(0.5);
axis.setGridOddConfig(odd);
CategoryAxis<JSOModel, String> horizontalAxis = new CategoryAxis<JSOModel, String>();
horizontalAxis.setPosition(Chart.Position.BOTTOM);
horizontalAxis.setField(new ValueProvider<JSOModel, String>() {
#Override
public String getValue(JSOModel JSOModel) {
return JSOModel.get("key");
}
#Override
public void setValue(JSOModel JSOModel, String s) {
}
#Override
public String getPath() {
return "key";
}
});
horizontalAxis.setTitleConfig(new TextSprite("month of year"));
chart.addAxis(horizontalAxis);
LineSeries<JSOModel> column = new LineSeries<JSOModel>();
column.setYAxisPosition(Chart.Position.LEFT);
column.setStroke(new RGB(148,174,10));
column.setHighlighting(true);
chart.addSeries(column);
axis.addField(column.getYField());
chart.addSeries(column);
chart.setHeight(100);
chart.setWidth(100);
Button b = new Button("ha");
b.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent clickEvent) {
loader.load();
}
});
RootPanel.get().add(b);
panel.setCollapsible(true);
panel.setHeadingText("Column Chart");
panel.setPixelSize(620, 500);
panel.setBodyBorder(true);
VerticalLayoutContainer layout = new VerticalLayoutContainer();
panel.add(layout);
chart.setLayoutData(new VerticalLayoutContainer.VerticalLayoutData(1,1));
layout.add(chart);
chart.setBackground(new Color("#dff"));
RootPanel.get().add(panel);
There are two ways to wire the chart into a store. One is to simply specify that the chart is using a store via setStore, as you have done:
chart.setStore(store);
When you do this, you must also inform the chart when it must redraw everything - you must call:
chart.redrawChart();
This call must be made shortly after the load is completed - consider doing it at the end of onLoad.
Why is this required? In some cases, developers want to make many changes to the store, one at a time, and if the chart automatically updated after each change, that would spawn many slow changes to the data model, and could end up looking strange. In a case like this, you would only call redrawChart() after all changes were complete.
There is another option however - instead of calling setStore, you can call bindStore, and ask the Chart to automatically update whenever any change occurs to the chart:
chart.bindStore(store);
In your case, this is likely the correct answer.

Categories

Resources