I'm trying to map DTOs to entities. I created a service that only takes care of mapping objects - ObjectMapper. DTO objects have relationships with each other. When I map a single object, for example when I create User, Group, Note, everything works. But when I want to use a method that returns a Note with a specific ID - /notes/{id}, I get the following error.
Handler dispatch failed; nested exception is java.langStackOverflowError] with root cause
To get specific Note, I need to use this mapping method that also cause this error. As u can see, I have to also convert Group and Tags.
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
When I don't have relationships defined as another DTO in the DTO class, but as an entity, everything works, since I don't have to convert the DTO to an entity.
Do you know where I'm making a mistake when mapping? Am I making a mistake in mapping multiple objects at once?
ObjectMapper
#Service
public class ObjectMapper {
//User
public UserDTO UserEntityToDtoGet(User user) {
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setName(user.getName());
userDTO.setEmail(user.getEmail());
userDTO.setGroup(user.getGroups());
return userDTO;
}
private UserCreationDTO UserEntityToDtoCreate(User user) {
UserCreationDTO userCreationDTO = new UserCreationDTO();
userCreationDTO.setName(user.getName());
userCreationDTO.setEmail(user.getEmail());
return userCreationDTO;
}
private User UserDtoToEntityCreate(UserCreationDTO userCreationDTO) {
User user = new User();
user.setName(userCreationDTO.getName());
user.setEmail(userCreationDTO.getEmail());
return user;
}
//Group
public GroupDTO GroupEntityToDtoGet(Group group) {
GroupDTO groupDTO = new GroupDTO();
groupDTO.setId(group.getId());
groupDTO.setName(group.getName());
groupDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupDTO.setNotes(NoteConvertList(group.getNotes()));
groupDTO.setTags(TagConvertSet(group.getTags()));
return groupDTO;
}
public GroupCreationDTO GroupEntityToDtoCreate(Group group) {
GroupCreationDTO groupCreationDTO = new GroupCreationDTO();
groupCreationDTO.setName(group.getName());
groupCreationDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupCreationDTO.setTags(TagConvertSet(group.getTags()));
return groupCreationDTO;
}
public Group GroupDtoToEntityCreate(GroupCreationDTO groupCreationDTO) {
Group group = new Group();
group.setName(groupCreationDTO.getName());
return group;
}
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
public Note NoteDtoToEntityCreate(NoteCreationDTO noteCreationDTO) {
Note note = new Note();
note.setTitle(noteCreationDTO.getTitle());
note.setDescription(noteCreationDTO.getDescription());
return note;
}
public NoteCreationDTO NoteEntityToDtoCreate(Note note) {
NoteCreationDTO noteCreationDTO = new NoteCreationDTO();
noteCreationDTO.setTitle(note.getTitle());
noteCreationDTO.setDescription(note.getDescription());
return noteCreationDTO;
}
public List<NoteDTO> NoteConvertList(List<Note> note) {
return note.stream()
.map(this::NoteEntityToDtoGet)
.collect(Collectors.toList());
}
//Tag
public TagDTO TagEntityToDtoGet(Tag tag) {
TagDTO tagDTO = new TagDTO();
tagDTO.setId(tag.getId());
tagDTO.setName(tag.getName());
tagDTO.setNotes(tag.getNotes());
tagDTO.setGroups(tag.getGroups());
return tagDTO;
}
public TagCreationDTO TagEntityToDtoCreate(Tag tag) {
TagCreationDTO tagCreationDTO = new TagCreationDTO();
tagCreationDTO.setId(tag.getId());
tagCreationDTO.setName(tag.getName());
tagCreationDTO.setNotes(tag.getNotes());
return tagCreationDTO;
}
public Set<TagDTO> TagConvertSet(Set<Tag> groups) {
return groups.stream()
.map(this::TagEntityToDtoGet)
.collect(Collectors.toSet());
}
}
You get StackOverFlowError because you end up with infinite recursive methods call and your application creates infinite amount of objects, so you just run out of memory:
1) your NoteEntityToDtoGet method gets Note's group and calls GroupEntityToDtoGet method on the Group object;
2) in GroupEntityToDtoGet method you get all Group's notes and call NoteConvertList method on them, which calls NoteEntityToDtoGet on each of the 'Note'
3) step 1 again...
... the same cycle goes over and over without a stop until your stack memory, you know, overflows :)
So you should decide do your DTO classes really need to hold references to other entity collections.
Related
I want to dynamic search with Criteria API in Java.
In the code I wrote, we need to write each entity in the url bar in JSON. I don't want to write "plaka".
The URL : <localhost:8080/api/city/query?city=Ankara&plaka=> I want to only "city" or "plaka"
Here we need to write each entity, even if we are going to search with only 1 entity. Type Entity and it should be empty.
My code is as below. Suppose there is more than one entity, what I want to do is to search using a single entity it wants to search. As you can see in the photo, I don't want to write an entity that I don't need. can you help me what should I do?
My code in Repository
public interface CityRepository extends JpaRepository<City, Integer> , JpaSpecificationExecutor<City> {
}
My code in Service
#Service
public class CityServiceImp implements CityService{
private static final String CITY = "city";
private static final String PLAKA = "plaka";
#Override
public List<City> findCityByNameAndPlaka(String cityName, int plaka) {
GenericSpecification genericSpecification = new GenericSpecification<City>();
if (!cityName.equals("_"))
genericSpecification.add(new SearchCriteria(CITY,cityName, SearchOperation.EQUAL));
if (plaka != -1)
genericSpecification.add(new SearchCriteria(PLAKA,plaka, SearchOperation.EQUAL));
return cityDao.findAll(genericSpecification);
}
#Autowired
CityRepository cityDao;
My code in Controller
#RestController
#RequestMapping("api/city")
public class CityController {
#Autowired
private final CityService cityService;
public CityController(CityService cityService) {
this.cityService = cityService;
#GetMapping("/query")
public List<City> query(#RequestParam String city, #RequestParam String plaka){
String c = city;
int p;
if (city.length() == 0)
c = "_";
if (plaka.length() == 0) {
p = -1;
}
else
p = Integer.parseInt(plaka);
return cityService.findCityByNameAndPlaka(c,p);
}
My code in SearchCriteria
public class SearchCriteria {
private String key;
private Object value;
private SearchOperation operation;
public SearchCriteria(String key, Object value, SearchOperation operation) {
this.key = key;
this.value = value;
this.operation = operation;
}
public String getKey() {
return key;
}
public Object getValue() {
return value;
}
public SearchOperation getOperation() {
return operation;
}
My code in GenericSpecification
public class GenericSpecification<T> implements Specification<T> {
private List<SearchCriteria> list;
public GenericSpecification() {
this.list = new ArrayList<>();
}
public void add(SearchCriteria criteria){
list.add(criteria);
}
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
for (SearchCriteria criteria : list) {
if (criteria.getOperation().equals(SearchOperation.GREATER_THAN)) {
predicates.add(builder.greaterThan(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.LESS_THAN)) {
predicates.add(builder.lessThan(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.GREATER_THAN_EQUAL)) {
predicates.add(builder.greaterThanOrEqualTo(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.LESS_THAN_EQUAL)) {
predicates.add(builder.lessThanOrEqualTo(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.NOT_EQUAL)) {
predicates.add(builder.notEqual(
root.get(criteria.getKey()), criteria.getValue()));
} else if (criteria.getOperation().equals(SearchOperation.EQUAL)) {
predicates.add(builder.equal(
root.get(criteria.getKey()), criteria.getValue()));
} else if (criteria.getOperation().equals(SearchOperation.MATCH)) {
predicates.add(builder.like(
builder.lower(root.get(criteria.getKey())),
"%" + criteria.getValue().toString().toLowerCase() + "%"));
} else if (criteria.getOperation().equals(SearchOperation.MATCH_END)) {
predicates.add(builder.like(
builder.lower(root.get(criteria.getKey())),
criteria.getValue().toString().toLowerCase() + "%"));
}
}
return builder.and(predicates.toArray(new Predicate[0]));
}
My code in SearchOperation
public enum SearchOperation {
GREATER_THAN,
LESS_THAN,
GREATER_THAN_EQUAL,
LESS_THAN_EQUAL,
NOT_EQUAL,
EQUAL,
MATCH,
MATCH_END,
}
The good thing about the Criteria API is that you can use the CriteriaBuilder to build complex SQL statements based on the fields that you have. You can combine multiple criteria fields using and and or statements with ease.
How I approached something similar int he past is using a GenericDao class that takes a Filter that has builders for the most common operations (equals, qualsIgnoreCase, lessThan, greaterThan and so on). I actually have something similar in an open-source project I started: https://gitlab.com/pazvanti/logaritmical/-/blob/master/app/data/dao/GenericDao.java
https://gitlab.com/pazvanti/logaritmical/-/blob/master/app/data/filter/JPAFilter.java
Next, the implicit DAO class extends this GenericDao and when I want to do an operation (ex: find a user with the provided username) and there I create a Filter.
Now, the magic is in the filter. This is the one that creates the Predicate.
In your request, you will receive something like this: field1=something&field2=somethingElse and so on. The value can be preceded by the '<' or '>' if you want smaller or greater and you initialize your filter with the values. If you can retrieve the parameters as a Map<String, String>, even better.
Now, for each field in the request, you create a predicate using the helper methods from the JPAFilter class and return he resulted Predicate. In the example below I assume that you don't have it as a Map, but as individual fields (it is easy to adapt the code for a Map):
public class SearchFilter extends JPAFilter {
private Optional<String> field1 = Optional.empty();
private Optional<String> field2 = Optional.empty();
#Override
public Predicate getPredicate(CriteriaBuilder criteriaBuilder, Root root) {
Predicate predicateField1 = field1.map(f -> equals(criteriaBuilder, root, "field1", f)).orElse(null);
Predicate predicateField2 = field2.map(f -> equals(criteriaBuilder, root, "field2", f)).orElse(null);
return andPredicateBuilder(criteriaBuilder, predicateField1, predicateField2);
}
}
Now, I have the fields as Optional since in this case I assumed that you have them as Optional in your request mapping (Spring has this) and I know it is a bit controversial to have Optional as input params, but in this case I think it is acceptable (more on this here: https://petrepopescu.tech/2021/10/an-argument-for-using-optional-as-input-parameters/)
The way the andPredicateBuilder() is made is that it works properly even if one of the supplied predicates is null. Also, I made s simple mapping function, adjust to include for < and >.
Now, in your DAO class, just supply the appropriate filter:
public class SearchDao extends GenericDAO {
public List<MyEntity> search(Filter filter) {
return get(filter);
}
}
Some adjustments need to be made (this is just starter code), like an easier way to initialize the filter (and doing this inside the DAO) and making sure that that the filter can only by applied for the specified entity (probably using generics, JPAFIlter<T> and having SearchFilter extends JPAFilter<MyEntity>). Also, some error handling can be added.
One disadvantage is that the fields have to match the variable names in your entity class.
I have list of "User" validations to be performed, each validation implements Predicate (from Guava). I have following method which fills the List of validations to be performed and creates composed Predicate of all validations.
List<Validation> validations = new ArrayList();
Predicate<User> composedUserValidations;
private void setupValidations() {
validations.add(userEmailIsValid());
validations.add(userPhoneIsValid());
validations.add(userLoginIsValid());
.....
//creating composite
composedUserValidations = and(validations);
}
Question: when I will trigger apply to composedUserValidations and one of them will fail, is there a way to find which one exactly is failing.
Now I have:
public boolean isUserValid(User user) {
if (!composedUserValidations.apply(user)) {
return false;
}
}
and I want to have something like:
public void validateUser(User user) {
if (!composedUserValidations.apply(ticket)) {
throw new ValidationExcepction("Predicate which failed");
}
}
What will be the best way to do it?
You could design a builder for your User class, let's name it UserBuilder.
For each of the User's properties (login, email, phone) in the builder class, you could not only provide the value you would like to set for the User instance, but also the Predicate, which you can immediately apply. If any of the predicates fail, you could easily throw an Exception and/or output a message, etc.
For all the instances you create, you could optionally pass different validation rules, or you could additionally design your builder to skip the validation for any of the User fields.
Example:
public class UserBuilder {
private String phone;
private String email;
//add more properties here;
public void phone(String phone, Predicate<String> phoneValidator) {
if (!phoneValidator.test(phone)) {
throw new UserBuilderException("Invalid phone provided");
}
this.phone = phone;
return this;
}
public void email(String email, Predicate<String> emailValidator) {
if (!emailValidator.test(email)) {
throw new UserBuilderException("Invalid email provided");
}
this.email = email;
return this;
}
//add more builder methods here
public User build() {
return new User(phone, email);
}
}
You could use that with:
Predicate<String> phoneValidator = ....
Predicate<String> emailValidator = ....
User user = new UserBuilder()
.phone("123567", phoneValidator)
.email("something#example.com", emailValidator)
.build();
What you get with that is:
Flexibility to provide custom validation rules (or omit some of them) for each User instance you have.
Better visibility about which properties fails their corresponding validation rules.
No User instances created with invalid content. A User instance will be created only for the property tuples, which pass all the Predicates provided in their builders.
I have an abstract class called sessions. Lectures and tutorials extend sessions. Then I have a class called enrollment which holds a list of sessions (Lectures & tutorials). How can I loop through the session list in Enrolment and return a list of Lectures only from the session list?
My next question is should I instead store 2 lists. One list of Lectures and one list of Tutorials, instead of 1 session list? This is because the sessions list is useless to me and I have to loop through it each time to get information about lectures and tutorials. Is there a way I am missing to get all the lectures objects? I am new to java.
public class Enrolment {
private List<Session> sessions;
public Enrolment() {
this.sessions = new ArrayList<>();
}
public addSession(Session session) {
this.sessions.add(session);
}
}
public class Session {
private int time;
public Session(int time) {
this.time = time;
}
}
public class Lecture extends Session {
private String lecturer;
public Lecture(int time, String lecturer) {
super(time);
this.lecturer = lecturer;
}
}
public class Tutorial extends Session {
private String tutor;
private int tutorScore;
public Tutorial(int time, String tutor, int tutorScore) {
super(time);
this.tutor = tutor;
this.tutorScore = tutorScore;
}
}
public class test {
public static void main(String[] args) {
Enrolment newEnrolment = new Enrolment();
Lecture morningLec = new Lecture(900, "Dr. Mike");
newEnrolment.addSession(morningLec);
Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
newEnrolment.addSession(afternoonTut);
Lecture middayLec = new Lecture(1200, "Mr. Micheals");
newEnrolment.addSession(middayLec);
Tutorial NightTut = new Tutorial(1900, "Harry Pauls", 4);
newEnrolment.addSession(NightTut);
}
}
Stream the sessions list and use instanceof to filter the Lectures type objects
List<Lecture> l = sessions.stream()
.filter(Lecture.class::isInstance)
.map(Lecture.class::cast)
.collect(Collectors.toList());
By using for loop use two different lists for each type
List<Lecture> l = new ArrayList<>();
List<Tutorial> t = new ArrayList<>();
for (Session s : sessions) {
if (s instanceof Lecture) {
l.add((Lecture) s);
}
else if(s instanceof Tutorial) {
t.add((Tutorial) s);
}
}
Maybe you should store in two lists, just like:
public class Enrolment {
private List<Lecture> lectures;
private List<Tutorial> tutorials;
public Enrolment() {
this.lectures = new ArrayList<>();
this.tutorials = new ArrayList<>();
}
public void addSession(Session session) {
if (session instanceof Lecture) {
lectures.add((Lecture) session);
} else if (session instanceof Tutorial) {
tutorials.add((Tutorial) session);
}
}
public List<Lecture> getLectures() {
return lectures;
}
public List<Tutorial> getTutorials() {
return tutorials;
}
public List<Session> getAllSessions() {
ArrayList<Session> sessions = new ArrayList<>(lectures);
sessions.addAll(tutorials);
return sessions;
}
}
Is that what you need?
My next question is should I instead store 2 lists. One list of
Lectures and one list of Tutorials, instead of 1 session list? This is
because the sessions list is useless to me and I have to loop through
it each time to get information about lectures and tutorials. Is there
a way I am missing to get all the lectures objects?
You answered yourself to your problem.
When you start to write too complex/boiler plate code to make things that should be simple such as iterating on a list of objects that you have just added, it is a sign that you should step back and redesign the thing.
By introducing Enrolment.addSession(Session session),
you introduced an undesirable abstraction :
public class Enrolment {
private List<Session> sessions;
public Enrolment() {
this.sessions = new ArrayList<>();
}
public addSession(Session session) {
this.sessions.add(session);
}
}
You don't want to handle uniformally Lecture and Tutorial from the Enrolment point of view, so just don't merge them in the same List only because these rely on the same interface (Session).
Abstraction has to be used when it is required and not systematically because that is possible.
Don't you add all objects in a List of Object because all is Object ? No.
Instead of, create this distinction both from the API method and from its implementation :
public class Enrolment {
private List<Conference> conferences = new ArrayList<>();
private List<Tutorial> tutorials = new ArrayList<>();
public addConference(Conference conference) {
this.conferences.add(conference);
}
public addTutorial(Tutorial tutorial) {
this.tutorials.add(tutorial);
}
}
And use it :
Lecture morningLec = new Lecture(900, "Dr. Mike");
newEnrolment.addLecture(morningLec);
Tutorial afternoonTut = new Tutorial(1400, "John Smith", 3);
newEnrolment.addTutorial(afternoonTut);
Note that you could have a scenario where you need to manipulate uniformally Tutorial and Lecture for some processings but that for others you want to distinguish them.
In this case, you have some common ways :
instanceOf : easy to use but also easy to make a code brittle. For example, later you could add a new subclass in the Session hierarchy and without be aware of it, instances of this subclass could be included or excluded in some processing without that the compiler warns you.
provide a abstract method that returns a boolean or an enum to convey the nature of the object (ex: isLecture()). More robust than instanceOf since the compiler constraints you to override the method but it may also lead to error prone code if multiple subclasses are added and that the filters are not only on Lecture but Lecture and another type. So I would favor this way while the filtering condition stays simple.
define three lists : one for lectures, another for conferences and another that contains all of these that should be handled uniformally. More complex way but more robust way too. I would favor it only for cases with complex/changing filtering conditions.
List<Lecture> l = newEnrolment.getSessions()
.stream()
.filter(s-> s.getClass().equals(Lecture.class))
.map(session -> (Lecture) session)
.collect(Collectors.toList());
!!! Don't use typeof
Quoting from your question,
This is because the sessions list is useless to me.
So, this is probably not the right place to have the list(?).
My preference would be to have
public interface Enrolment {
public abstract addSession(Session session);
public abstract getSessions();
}
And List<LectureEnrolment> and List<TutorialEnrolment> must be in their respective classes.(I have renamed Lecture to LectureEnrolment and Tutorial to TutorialEnrolment)
main() must have something like,
Enrolment lectureEnrolment= new LectureEnrolment()
Enrolment tutorialEnrolement = new TutorialEnrolment()
call the respective addSession() or getSession() depending on requirement.
change private List<Session> sessions; to public List<Session> sessions; in class Enrolment
public static void main(String[] args) {
....
var lecturesAndTutorials = newEnrolment.sessions.where(x => x.getType() == typeof(Lecture) || x.getType() == typeof(Tutorial));
....
}
I am new to Reactor framework and trying to utilize it in one of our existing implementations. LocationProfileService and InventoryService both return a Mono and are to executed in parallel and have no dependency on each other (from the MainService). Within LocationProfileService - there are 4 queries issued and the last 2 queries have a dependency on the first query.
What is a better way to write this? I see the calls getting executed sequentially, while some of them should be executed in parallel. What is the right way to do it?
public class LocationProfileService {
static final Cache<String, String> customerIdCache //define Cache
#Override
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
//These 2 are not interdependent and can be executed immediately
Mono<String> customerAccountMono = getCustomerArNumber(customerId,location) LocationNumber).subscribeOn(Schedulers.parallel()).switchIfEmpty(Mono.error(new CustomerNotFoundException(location, customerId))).log();
Mono<LocationProfile> locationProfileMono = Mono.fromFuture(//location query).subscribeOn(Schedulers.parallel()).log();
//Should block be called, or is there a better way to do ?
String custAccount = customerAccountMono.block(); // This is needed to execute and the value from this is needed for the next 2 calls
Mono<Customer> customerMono = Mono.fromFuture(//query uses custAccount from earlier step).subscribeOn(Schedulers.parallel()).log();
Mono<Result<LocationPricing>> locationPricingMono = Mono.fromFuture(//query uses custAccount from earlier step).subscribeOn(Schedulers.parallel()).log();
return Mono.zip(locationProfileMono,customerMono,locationPricingMono).flatMap(tuple -> {
LocationProfileInfo locationProfileInfo = new LocationProfileInfo();
//populate values from tuple
return Mono.just(locationProfileInfo);
});
}
private Mono<String> getCustomerAccount(String conversationId, String customerId, String location) {
return CacheMono.lookup((Map)customerIdCache.asMap(),customerId).onCacheMissResume(Mono.fromFuture(//query).subscribeOn(Schedulers.parallel()).map(x -> x.getAccountNumber()));
}
}
public class InventoryService {
#Override
public Mono<InventoryInfo> getInventoryInfo(String inventoryId) {
Mono<Inventory> inventoryMono = Mono.fromFuture(//inventory query).subscribeOn(Schedulers.parallel()).log();
Mono<List<InventorySale>> isMono = Mono.fromFuture(//inventory sale query).subscribeOn(Schedulers.parallel()).log();
return Mono.zip(inventoryMono,isMono).flatMap(tuple -> {
InventoryInfo inventoryInfo = new InventoryInfo();
//populate value from tuple
return Mono.just(inventoryInfo);
});
}
}
public class MainService {
#Autowired
LocationProfileService locationProfileService;
#Autowired
InventoryService inventoryService
public void mainService(String customerId, String location, String inventoryId) {
Mono<LocationProfileInfo> locationProfileMono = locationProfileService.getProfileInfoByLocationAndCustomer(....);
Mono<InventoryInfo> inventoryMono = inventoryService.getInventoryInfo(....);
//is using block fine or is there a better way to do?
Mono.zip(locationProfileMono,inventoryMono).subscribeOn(Schedulers.parallel()).block();
}
}
You don't need to block in order to get the pass that parameter your code is very close to the solution. I wrote the code using the class names that you provided. Just replace all the Mono.just(....) with the call to the correct service.
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
Mono<String> customerAccountMono = Mono.just("customerAccount");
Mono<LocationProfile> locationProfileMono = Mono.just(new LocationProfile());
return Mono.zip(customerAccountMono, locationProfileMono)
.flatMap(tuple -> {
Mono<Customer> customerMono = Mono.just(new Customer(tuple.getT1()));
Mono<Result<LocationPricing>> result = Mono.just(new Result<LocationPricing>());
Mono<LocationProfile> locationProfile = Mono.just(tuple.getT2());
return Mono.zip(customerMono, result, locationProfile);
})
.map(LocationProfileInfo::new)
;
}
public static class LocationProfileInfo {
public LocationProfileInfo(Tuple3<Customer, Result<LocationPricing>, LocationProfile> tuple){
//do wathever
}
}
public static class LocationProfile {}
private static class Customer {
public Customer(String cutomerAccount) {
}
}
private static class Result<T> {}
private static class LocationPricing {}
Pleas remember that the first zip is not necessary. I re write it to mach your solution. But I would solve the problem a little bit differently. It would be clearer.
public Mono<LocationProfileInfo> getProfileInfoByLocationAndCustomer(String customerId, String location) {
return Mono.just("customerAccount") //call the service
.flatMap(customerAccount -> {
//declare the call to get the customer
Mono<Customer> customerMono = Mono.just(new Customer(customerAccount));
//declare the call to get the location pricing
Mono<Result<LocationPricing>> result = Mono.just(new Result<LocationPricing>());
//declare the call to get the location profile
Mono<LocationProfile> locationProfileMono = Mono.just(new LocationProfile());
//in the zip call all the services actually are executed
return Mono.zip(customerMono, result, locationProfileMono);
})
.map(LocationProfileInfo::new)
;
}
Here's my code to delete all entities for a given type:
#Override
public boolean deleteEntities(String instance, final String storeName) {
final boolean[] success = {false};
final PersistentEntityStore entityStore = manager.getPersistentEntityStore(xodusRoot, instance);
try {
entityStore.executeInTransaction(new StoreTransactionalExecutable() {
#Override
public void execute(#NotNull final StoreTransaction txn) {
EntityIterable result = txn.getAll(storeName);
final boolean[] hasError = {false};
for(Entity entity : result) {
if(!entity.delete()) {
hasError[0] = true;
}
}
success[0] = !hasError[0];
}
});
} finally {
////entityStore.close();
}
return success[0];
}
Question:
Is this the right approach to delete all existing entities for a given entity type?
When this method is executed, all entities are indeed removed but the Enity type is sitll there, how to properly delete a enity type?
There is PersistentEntityStore#renameEntityType to rename entity type as part of a public api. To delete entity type at all you can use PersistentEntityStoreImpl#deleteEntityType. It's not a part of PersistentEntityStore api but method is public and you can use it.
Also when you deleting entity type do not forget that you also need to clear all links points to entities of this type.