GroupBy in Groovy - java

I'd like to GroupBy the subscription details by its ProductType with their corresponding
List<Long> planReferenceIds
key: planSubscription.productSubscription.productType
value: planSubscription.planReferenceId
I can't seem to know how to do this the 'groovy way'
private static Map<ProductType, List<Long>> buildPlanSubscriptionDetails(List<PlanSubscription> planSubscriptions) {
Map<ProductType, List<Long>> subscriptionDetails = [:]
subscriptionDetails = planSubscriptions.groupBy({it.productSubscription.productType })
return subscriptionDetails
Edit:
Here is what I've come up with but its not the Groovy way i guess
planSubscriptions.each { PlanSubscription planSubscription ->
List<Long> planRefIds = subscriptionDetails.get(planSubscription.productSubscription.productType)
if (planRefIds) {
planRefIds.add(planSubscription.planReferenceId)
} else {
subscriptionDetails.put(planSubscription.productSubscription.productType, [planSubscription.planReferenceId] as List)
}
}
thank you in advance!

You should use collectEntries after grouping:
Map<ProductType, List<Long>> result = subscriptionDetails
.groupBy { it.productSubscription.productType }
.collectEntries { pt, sd -> [pt, sd*.planReferenceId]}

Related

JPA many to many constraint violation exception

I am using JPA to store data and faced two problems during implementation. I have two entities (Station and Commodity) that have many-to-many relationship with intermediate table so that I had to created the third one. When app receives message it converts its data to entites and should save but sometimes app throwing a ConstraintViolationException because there is null value at foreign key field referencing to Commodity entity.
I've tried simple approach: selecting needed commodity from database and saving it if there is no one. Then I started to use bulk searching all commodities of message and then putting it where are needed. None of them did a trick.
In my opinion the problem could be caused by multi-threading read\insert.
The second problem is that service stop running when exception is thrown. App can lost some of transactions that's not a big deal but it simply stops after rollback.
How can I resolve these conflicts?
Here is code of data handling class and diagram of entities :
#Service
#AllArgsConstructor
#Slf4j
public class ZeromqCommoditiesServiceImpl implements ZeromqCommoditesService {
private final CategoryTransactionHandler categoryHandler;
private final CommodityTransactionHandler commodityHandler;
private final EconomyTransactionHandler economyHandler;
private final StationTransactionHandler stationHandler;
private final SystemTransactionHandler systemHandler;
#Override
#Transactional(
isolation = Isolation.READ_COMMITTED,
propagation = Propagation.REQUIRES_NEW,
rollbackFor = Throwable.class)
#Modifying
public void saveData(ZeromqCommodityPayload payload) {
CommodityContent content = payload.getContent();
var station = stationHandler.createOrFindStation(content.getStationName());
var system = systemHandler.createOrFindSystem(content.getSystemName());
var commodityReferences = getMapOfCommodities(content);
station.setSystem(system);
updateEconomies(station, content);
updateProhibited(station, content, commodityReferences);
updateStationCommodities(station, content, commodityReferences);
try {
saveStation(station);
} catch (ConstraintViolationException | PersistentObjectException | DataAccessException e) {
log.error("Error saving commodity info \n" + content, e);
}
}
public void saveStation(StationEntity station) {
stationHandler.saveStation(station);
if (station.getId() != null) {
log.debug(String.format("Updated \"%s\" station info", station.getName()));
} else {
log.debug(String.format("Updated \"%s\" station info", station.getName()));
}
}
private void updateEconomies(StationEntity station, CommodityContent content) {
station.getEconomies().clear();
if (content.getEconomies() != null) {
var economies = content.getEconomies()
.stream()
.map(economy -> {
var stationEconomyEntity = economyHandler.createOrFindEconomy(economy.getName());
Double proportion = economy.getProportion();
stationEconomyEntity.setProportion(proportion != null ? proportion : 1.0);
return stationEconomyEntity;
})
.peek(economy -> economy.setStation(station))
.toList();
station.getEconomies().addAll(economies);
}
}
private void updateProhibited(
StationEntity station,
CommodityContent content,
Map<String, CommodityEntity> commodityEntityMap) {
station.getProhibited().clear();
if (content.getProhibited() != null) {
var prohibitedCommodityEntities = content.getProhibited()
.stream()
.map(prohibited -> {
String eddnName = prohibited.toLowerCase(Locale.ROOT);
CommodityEntity commodityReference = getCommodityEntity(commodityEntityMap, eddnName);
return new ProhibitedCommodityEntity(station, commodityReference);
}
)
.toList();
station.getProhibited().addAll(prohibitedCommodityEntities);
}
}
private void updateStationCommodities(
StationEntity station,
CommodityContent content,
Map<String, CommodityEntity> commodityEntityMap) {
station.getCommodities().clear();
if (content.getCommodities() != null) {
var commodities = content.getCommodities()
.stream()
.map(commodity -> {
CommodityEntity commodityReference = getCommodityEntity(
commodityEntityMap,
commodity.getEddnName());
return StationCommodityEntity.builder()
.commodity(commodityReference)
.buyPrice(commodity.getBuyPrice())
.sellPrice(commodity.getSellPrice())
.demand(commodity.getDemand())
.stock(commodity.getStock())
.station(station)
.build();
})
.toList();
station.getCommodities().addAll(commodities);
}
}
private CommodityEntity getCommodityEntity(Map<String, CommodityEntity> commodityEntityMap, String eddnName) {
return commodityEntityMap.get(eddnName);
}
private Map<String, CommodityEntity> getMapOfCommodities(#NotNull CommodityContent content) {
Set<String> commodities = content.getCommodities()
.stream()
.map(Commodity::getEddnName)
.collect(Collectors.toSet());
if (content.getProhibited() != null && content.getProhibited().size() > 0) {
commodities.addAll(content.getProhibited().
stream()
.map(item -> item.toLowerCase(Locale.ROOT))
.collect(Collectors.toSet()));
}
var commodityReferencesMap = commodityHandler.findAllByEddnName(commodities)
.stream()
.collect(Collectors.toMap(
CommodityEntity::getEddnName,
item -> item
));
commodities.forEach(commodity -> {
if (commodityReferencesMap.get(commodity.toLowerCase()) == null) {
CommodityCategoryEntity category = categoryHandler.createOrFindCategory("Unknown");
CommodityEntity newCommodity = new CommodityEntity(commodity, commodity, category);
CommodityEntity managedCommodity = commodityHandler.saveCommodity(newCommodity);
commodityReferencesMap.put(managedCommodity.getEddnName(), managedCommodity);
}
});
return commodityReferencesMap;
}
}
Thanks in advance

RxJava2 - group results of objects processing into the Map<String, List<Object>>

I have a list of REST URIs and there's a method that returns some value for the passed URI:
String searchForValueBy(RestURI uri);
For instance, we have the following list:
List<RestURI> restUriList = List.of(uri1, uri2, uri3, uri4, uri5);
And searchForValueBy returns the following values for each URI:
uri1 : "1"
uri2 : "2"
uri3 : "1"
uri4 : "1"
uri5 : "2"
What I want to achieve is to group URIs and values into the following map Map<String, List<RestURI>>.
I did the following implementation, but it uses two different approaches - traditional and reactive ones:
Map<String, List<RestURI>> valueToUriMap = new HashMap<>();
Observable.fromIterable(restUriList)
.flatMapCompletable(uri -> searchForValueBy(uri))
.flatMapCompletable(value -> {
valueToUriMap.computeIfAbsent(value, key -> new ArrayList<>()).add(uri);
return Completable.complete();
}))
.andThen(/*work with valueToUriMap*/)
Is it possible to achieve the same with some sort of grouping like groupBy?
Here is the working example to copy and play:
public static void main(String[] args) {
final List<RestUri> restUriList = List.of(
new RestUri("uri1", "1"),
new RestUri("uri2", "2"),
new RestUri("uri3", "1"),
new RestUri("uri4", "1"),
new RestUri("uri5", "2"));
Map<String, List<RestUri>> valueToUriMap = new HashMap<>();
Observable.fromIterable(restUriList)
.flatMapCompletable(uri -> searchForValueBy(uri)
.flatMapCompletable(value -> {
valueToUriMap.computeIfAbsent(value, key -> new ArrayList<>()).add(uri);
return Completable.complete();
})).subscribe();
// .andThen(/*work with valueToUriMap*/)
valueToUriMap.keySet().forEach(key -> System.out.println("Key: " + key + ", Value: " + valueToUriMap.get(key)));
}
public static Single<String> searchForValueBy(RestUri uri) {
return Single.just(uri.getValue());
}
static class RestUri {
String uri;
String value;
public RestUri(String uri, String value) {
this.uri = uri;
this.value = value;
}
public String getUri() {
return uri;
}
public String getValue() {
return value;
}
#Override
public String toString() {
return uri;
}
}
Updated after #akarnokd comment
It seems that toMultimap does the trick, but at the end it appears to be a List of single entry maps instead of Map with a bunch of key:value pairs:
Observable<Single<Map<String, Collection<RestUri>>>> result = Observable.fromIterable(restUriList)
.map(uri -> searchForValueBy(uri)
.toMultimap(key -> key, value -> uri));
Try this:
Observable.fromIterable(restUriList)
.flatMapSingle(uri ->
searchForValueBy(uri)
.map(v -> new RestUri(uri.getUri(), v))
)
.toMultimap(RestUri::getValue, RestUri::getUri))

How to return list in SpringBoot

code below return me list of two objects like this:
[
{
value: "ssss",
},
{
value: "ssss",
},
]
but i want to return list like this:
{
value1:"xxxx",
value2:"xxxx"
}
here is my code
#Override
public List<OrderModel> getOrderByCode(UserEntity user) throws ErrorException {
List<UserEntity> userOrder = userRepository.findByUserAndOrderCodeIn(user, Arrays.asList(OrderCodes.OK, OrderCodes.DONE));
List<OrderModel> om = new ArrayList<OrderModel>();
for(UserAnswerEntity userAnswerEntity : userAnswers){
OrderModel orderModel = new OrderModel();
orderModel.setValue(userAnswerEntity.getValue());
om.add(orderModel );
}
return om;
}
public class OrderModel{
String value;
//get,set
}
can someon tell me how can i return only list like above?
You are trying to make a custom response. One of the possible way is this
Map<String,String> map = new HashMap<String,String>();
map.put("key1", "value1");
map.put("key2", "value2");
System.out.println(map.toString().replace("{", "[").replace("}", "]"));
Change the return type of getOrderByCode() to string and convert your List object to JSON string.
private static final ObjectMapper jsonMapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL);
return jsonMapper.writeValueAsString(om);
for you example, you can use Map instead of List
try this:
AtomicInteger index = new AtomicInteger()
Function<UserAnswerEntity, String> createIndex = (userAnswer) -> "value" + index.getAndIncrement();
return userAnswers.stream().collect(Collectors.toMap(createIndex, UserAnswerEntity::getValue));

Is there a cleaner way adapt a standard observer/listener service using RxJava?

I've been tinkering with wrapping an old style listener interface using RxJava. What i've come up with seems to work, but the usage of Observable.using feels a bit awkward.
The requirements are:
Only one subscription per id to the underlying service.
The latest value for a given id should be cached and served to new subscribers.
We must unsubscribe from the underlying service if nothing is listening to an id.
Is there a better way? The following is what I've got.
static class MockServiceRXAdapterImpl1 implements MockServiceRXAdapter {
PublishSubject<MockResponse> mockResponseObservable = PublishSubject.create();
MockService mockService = new MockService(mockResponse -> mockResponseObservable.onNext(mockResponse));
final ConcurrentMap<String, Observable<String>> subscriptionMap = new ConcurrentHashMap<>();
public Observable<String> getObservable(String id) {
return Observable.using(() -> subscriptionMap.computeIfAbsent(
id,
key -> mockResponseObservable.filter(mockResponse -> mockResponse.id.equals(id))
.doOnSubscribe(disposable -> mockService.subscribe(id))
.doOnDispose(() -> {
mockService.unsubscribe(id);
subscriptionMap.remove(id);
})
.map(mockResponse -> mockResponse.value)
.replay(1)
.refCount()),
observable -> observable,
observable -> {
}
);
}
}
You may use Observable.create
So code may look like this
final Map<String, Observable<String>> subscriptionMap = new HashMap<>();
MockService mockService = new MockService();
public Observable<String> getObservable(String id) {
log.info("looking for root observable");
if (subscriptionMap.containsKey(id)) {
log.info("found root observable");
return subscriptionMap.get(id);
} else {
synchronized (subscriptionMap) {
if (!subscriptionMap.containsKey(id)) {
log.info("creating new root observable");
final Observable<String> responseObservable = Observable.create(emitter -> {
MockServiceListener listener = emitter::onNext;
mockService.addListener(listener);
emitter.setCancellable(() -> {
mockServices.removeListener(listener);
mockService.unsubscribe(id);
synchronized (subscriptionMap) {
subscriptionMap.remove(id);
}
});
mockService.subscribe(id);
})
.filter(mockResponse -> mockResponse.id.equals(id))
.map(mockResponse -> mockResponse.value)
.replay(1)
.refCount();
subscriptionMap.put(id, responseObservable);
} else {
log.info("Another thread created the observable for us");
}
return subscriptionMap.get(id);
}
}
}
I think I've gotten it to work using .groupBy(...).
In my case Response.getValue() returns an int, but you get the idea:
class Adapter
{
Subject<Response> msgSubject;
ThirdPartyService service;
Map<String, Observable<Integer>> observables;
Observable<GroupedObservable<String, Response>> groupedObservables;
public Adapter()
{
msgSubject = PublishSubject.<Response>create().toSerialized();
service = new MockThirdPartyService( msgSubject::onNext );
groupedObservables = msgSubject.groupBy( Response::getId );
observables = Collections.synchronizedMap( new HashMap<>() );
}
public Observable<Integer> getObservable( String id )
{
return observables.computeIfAbsent( id, this::doCreateObservable );
}
private Observable<Integer> doCreateObservable( String id )
{
service.subscribe( id );
return groupedObservables
.filter( group -> group.getKey().equals( id ))
.doOnDispose( () -> {
synchronized ( observables )
{
service.unsubscribe( id );
observables.remove( id );
}
} )
.concatMap( Functions.identity() )
.map( Response::getValue )
.replay( 1 )
.refCount();
}
}

how to render Map<String, String> to template and use there

i am trying to render a key,value Map to a template and then show there in this way:
#(twittsIfollow: Map[String, String])
.....
#if(twittsIfollow != null) {
#for((key, value) <- twittsIfollow) {
#key
#value
}
}
it says, it is wrong. is there a scala tag for Map keys values?
here is my method:
public static Map<String, String> alltwitts(List<Long> otherIDs) {
Map<String, String> results=new HashMap<String, String>();
for (Long id: otherIDs) {
Query selected_twitt = JPA.em().createQuery("select u.twitt from Twitt u where " + " u.whose = ?").setParameter(1, id);
String twOwner = User.getOneUser(id);
String twitt = (String) selected_twitt.getSingleResult();
results.put(twOwner, twitt);
}
return results;
}
then i render to template in this place:
Map<String, String> twittsIfollow = Twitt.alltwitts(IDusersIamFollowing);
return ok(microblog.render(twittsIfollow));
now it is saying: [NonUniqueResultException: result returns more than one elements]
thanks
Simply
#for((key, value) <- twittsIfollow) {
#key
#value
}
BTW, if you are using Scala, twittsIfollow should never be null. Prefer using Option.
how i solved:
public static Map<String, List<String>> alltwitts(List<Long> otherIDs) {
Map<String, List<String>> results=new HashMap<String, List<String>>();
for (Long id: otherIDs) {
Query selected_twitt = JPA.em().createQuery("select twitt from Twitt where " + " whose = ? order by id").setParameter(1, id);
String twOwner = User.getOneUser(id);
List<String> twitt = selected_twitt.getResultList();
results.put(twOwner, twitt);
}
System.out.println(results);
return results;
}
then in my Application:
Map<String, List<String>> twittsIfollow = Twitt.alltwitts(IDusersIamFollowing);
return ok(microblog.render(twittsIfollow));
then in my template:
#(twittsIfollow: Map[String, List[String]])
and
#if(twittsIfollow != null) {
#for((key, value) <- twittsIfollow) {
#for(innervalue <- value){
Username: #key
Twitt: #innervalue
}
}
}
just for those who are dumb like me :D and looking for solution like this..
thanks for help,guys..
#doniyor, I'm surpriesed as I showed you the way in other question not so long ago.
By the way I think it will be better to build, pass and iterate twittsIfollow as a List of Maps
then you can use
#if(twittsIfollow != null) {
#for(twitts <- twittsIfollow) {
the value is: #twitts.get("key")
}
}

Categories