JPA many to many constraint violation exception - java

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

Related

Implementation of for loop using java8 features

Can somebody please help me to restructure the following code using java8 features.
Here is my code.
private List<CreateChildItemResponse> createChildItems(String[] childUpcNumbers, String[] childItemNbrs,
ItemVo parentItem, UserVo user, boolean isEnableReverseSyncFlag, Integer parentItemNbr, Long parentUpcNbr,int childUpcNbrsize, ItemManagerDelegate managerDelegate) throws NumberFormatException, ValidationException,ChildNotFoundException, ResourceException, ChildItemException {
List<ItemVo> resultList = new ArrayList<ItemVo>();
List<CreateChildItemAssortmentResponse> relations = null;
CreateChildItemResponse response = new CreateChildItemResponse();
try {
for (int i = 0; i < childUpcNumbers.length; i++) {
// parentItem.setItemNbr(itemNumberList.get(i));
logger.info("-------Item Nbrs-----" + parentItem.getItemNbr());
ItemVo child = createItemForMigration(
populateChildItemVo(parentItem, getGtin(childUpcNumbers[i]), Integer.valueOf(childItemNbrs[i])),
user, isEnableReverseSyncFlag, managerDelegate);// null scales for all except scales integration
resultList.add(child);
}
relations = this.populateAssortmentRelationVo(parentItem.getItemNbr(), resultList);
response = getSuccessResponse(parentItemNbr, relations, parentUpcNbr);
// utx.commit();
} catch (Exception e) {
logger.info("Exception occurs while creating child item", e);
relations = this.populateAssortmentRelationVoForException(e, resultList);
response = getFailureResponseForException(parentItemNbr, relations, parentUpcNbr, e, resultList.size(),
childUpcNbrsize);
finalResponse.add(response);
throw new ChildItemException(e.getMessage(), finalResponse, e);
}
finalResponse.add(response);
return finalResponse;
}
here I am calling the below method in the above code
relations = this.populateAssortmentRelationVo(parentItem.getItemNbr(), resultList);
And the implementation is
private List<CreateChildItemAssortmentResponse> populateAssortmentRelationVo(Integer parentItemNumber,
List<ItemVo> childs) {
List<CreateChildItemAssortmentResponse> relationList = new ArrayList<CreateChildItemAssortmentResponse>();
for (ItemVo child : childs) {
CreateChildItemAssortmentResponse relation = new CreateChildItemAssortmentResponse();
relation.setItemNbr(child.getItemNbr());
relation.setUpcNbr(convertUpcToString(child.getEachGtin().getGtinNbr()));
relation.setStatus("SUCCESS");
relation.setMessage("");
relationList.add(relation);
}
return relationList;
}
Here I want to take a constructor for the populateAssortmentRelationVo() and how to use stream and mapper inside this.
First declare a mapping function:
private CreateChildItemAssortmentResponse> itemVoToResponse(ItemVo item) {
CreateChildItemAssortmentResponse response = new CreateChildItemAssortmentResponse();
response.setItemNbr(item.getItemNbr());
response.setUpcNbr(convertUpcToString(item.getEachGtin().getGtinNbr()));
response.setStatus("SUCCESS");
response.setMessage("");
return relationList;
}
and simply map all the elements of one list into another one:
relations = resultList.stream()
.map(this::itemVoToResponse)
.collect(toList());

Merge List of Custom Objects to a Single List Object using Streams Java 8

I have ProductInfo object which looks like this
ProductInfo.java
public class ProductInfo
{
private List<String> servicetagInfo;
}
I have Order object like this which has list of Products info
OrderDetail.java
public class OrderDetail
{
private String orderNum;
private List<ProductInfo> productInfo;
}
And then I have a Response object which basically has List of Order objects
Response.java
public class Response
{
private List<OrderDetail> orderInfo;
}
I am getting response as expected.But right now in this format
orderInfo:
0: {orderNum: "162293591",...}
productInfo:
0: {servicetag_info: ["7LSMW33", "49SMW33"]}
1: {servicetag_info: ["JF6XN33", "CQ5XN33"]}
2: {servicetag_info: ["5VRR523", "13LR523"]}
Here I am trying to merge productInfo List to be like this
productInfo:
0: {servicetag_info: ["7LSMW33", "49SMW33","JF6XN33", "CQ5XN33","5VRR523", "13LR523"]}
Just add all strings into one main property.
Here is my code
List<String> serviceTagList = new ArrayList<>();
for (OrderDetail orderDetail : arInvoiceOrderResponseBody.getOrders()) { //Here i am getting orders from external service
if (orderDetail != null) {
if (orderDetail.getProductInfo() != null && orderDetail.getProductInfo().size() > 0) {
for (ProductInfo productInfoDetail : orderDetail.getProductInfo()) {
if (productInfoDetail != null) {
if (productInfoDetail.getServicetagInfo() != null) {
for (String serviceTag : productInfoDetail.getServicetagInfo()) {
serviceTagList.add(serviceTag);
}
}
}
}
}
}
ProductInfo productInfo = new ProductInfo();
productInfo.setServicetagInfo(serviceTagList);
orderDetail.setProductInfo(Arrays.asList(productInfo));
}
Can anyone suggest how can i achieve same using streams in java so that it will be readable.
Try this:
Set<String> tags = order.stream()
.flatMap(order -> order.getProductInfo().stream())
.map(ProductInfo::getServicetagInfo)
.collect(Collectors.toSet());
Full implementation:
for (OrderDetail orderDetail : arInvoiceOrderResponseBody.getOrders()) {
if (orderDetail != null && orderDetail.getProductInfo() != null) {
orderDetail.getProductInfo().removeAll(null); // Remove any null elems
Set<String> tags = orderDetail.getProductInfo().stream()
.flatMap(product -> (product.getServicetagInfo() == null) ? null : product.getServicetagInfo().stream())
.collect(Collectors.toSet());
tags.remove(null); // Remove null if exists
}
ProductInfo productInfo = new ProductInfo();
productInfo.setServicetagInfo(tags);
orderDetail.setProductInfo(Arrays.asList(productInfo));
}
With streams your code could be like this:
arInvoiceOrderResponseBody.getOrders().stream()
.filter(Objects::nonNull)
.forEach(YourClassName::mergeProductInfo);
The method mergeProductInfo would be:
private static void mergeProductInfo(OrderDetail orderDetail) {
List<String> serviceTagList = new ArrayList<>();
if (orderDetail.getProductInfo() != null) {
serviceTagList = orderDetail.getProductInfo().stream()
.filter(Objects::nonNull)
.map(ProductInfo::getServicetagInfo)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
ProductInfo productInfo = new ProductInfo();
productInfo.setServicetagInfo(serviceTagList);
orderDetail.setProductInfo(Arrays.asList(productInfo));
}
It could be simplified if you could be sure that you are not going to receive null lists or elements.

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

WebFlux Conditional FlatMap

I'm relatively new to webflux and i want to find solution to avoid nested flatMap when there are conditionals:
I have 3 simple entities:
Item, Brand, Category.
Item basically contains: brandId and categoryId
public Mono<Item> patch(String itemId, PatchSpecs specs) {
return itemRepository.findById(itemId)
.switchIfEmpty(Mono.error(..)))
.flatMap(item -> {
final String brandId = specs.getBrandId();
if (brandId != null) {
return brandService.getById(brandId)
.switchIfEmpty(Mono.error(..)))
.flatMap(brand -> {
final String categoryId = specs.getCategoryId();
if (categoryId != null) {
return categoryService.getById(categoryId)... -> return updateAndSave(..)
}
return updateAndSave(specs, item, brand, null);
});
}
else {
final String categoryId = specs.getCategoryId();
if (categoryId != null) {
return categoryService.getById(categoryId)... -> return updateAndSave(..)
}
return updateAndSave(specs, item, null, null);
}
});
}
How do I prevent this branching conditional flatMaps mess? I cannot imagine if i have another entity inside Item. There will be more nested flatMaps?
If I understand your code well category and brand are optional attributes of the item. In this case I'd recommend the following:
public Mono<Item> patch(String itemId, PatchSpecs specs)
{
return itemRepository.findById(itemId)
.switchIfEmpty(Mono.error(new RuntimeException("Can not find item.")))
.flatMap(item -> updateAndSave(specs, item));
}
private Mono<? extends Item> updateAndSave(PatchSpecs specs, Item item)
{
Mono<Optional<Brand>> brand = getBrand(specs.getBrandId());
Mono<Optional<Category>> category = getCategory(specs.getCategoryId());
return Mono.zip(Mono.just(item), brand, category)
.flatMap(tuple -> updateAndSave(specs, tuple));
}
private Mono<Optional<Brand>> getBrand(String inputBrandId)
{
return Mono.justOrEmpty(inputBrandId)
.flatMap(brandId -> brandService.getById(brandId)
.switchIfEmpty(Mono.error(new RuntimeException("Can not find brand."))))
.map(Optional::of)
.defaultIfEmpty(Optional.empty());
}
private Mono<Optional<Category>> getCategory(String inputCategoryId)
{
return Mono.justOrEmpty(inputCategoryId)
.flatMap(brandId -> categoryService.getById(brandId)
.switchIfEmpty(Mono.error(new RuntimeException("Can not find brand."))))
.map(Optional::of)
.defaultIfEmpty(Optional.empty());
}
private Mono<Item> updateAndSave(PatchSpecs specs, Tuple3<Item, Optional<Brand>, Optional<Category>> tuple)
{
Item item = tuple.getT1();
Brand brand = tuple.getT2().orElse(null);
Category category = tuple.getT3().orElse(null);
// TODO do update and save here
return null;
}
It will be more extensible this way and no need for duplication and nested conditionals. Please, verify it works as you expect.

Chaining async methods in a loop with RxJava

I have this code and I want to know if this is possible with RxJava:
Function queries for a User from a server (async)
The server returns a User JSON object with a list of ID's of associated UserProfile(s)
Then for each of this ID, it needs to fetch the UserProfile given the ID (async also)
For each asynchronously fetched UserProfile append it to the User object, below is my pseudo-code.
I cannot use any blocking code, all request should be async.
Here's the code:
#Override
public Single<User> retrieve(String entityId) {
BaasUser baasUser = new BaasUser();
baasUser.setEntityId(entityId);
baasUser.setIncludes(Arrays.asList("userProfile"));
return baasUser.retrieve().map(user -> {
String username = user.getUsername();
String dateCreated = user.getDateCreated();
String dateUpdated = user.getDateUpdated();
List<UserProfile> userProfiles = new LinkedList<>();
BaasLink userProfileLink = user.getFirstLink();
userProfileLink.getEntities().forEach(stubEntity -> {
Single<UserProfile> observable = stubEntity.retrieve().map(entity -> {
UserProfile userProfile = new UserProfile();
userProfile.setEntityId(entity.getStringProperty("entityId"));
userProfile.setName(entity.getStringProperty("name"));
return userProfile;
});
observable.subscribe(userProfile -> {
// until all UserProfile is fetched this 'retrieve' "callback" should not return
userProfiles.add(userProfile);
}, error -> {
// err
});
});
User user1 = new User();
user1.setDateCreated(dateCreated);
user1.setDateUpdated(dateUpdated);
user1.setUsername(username);
user1.setUserProfiles(userProfiles);
return user1;
});
}
Here you have en example how to do your jobe maybe there is any typeerror becouse i dont have your objects
Single.just("user")
.observeOn(Schedulers.io())
.flatMap(user -> Observable.zip(getCurrentUserData(user),getUserProfiles(user),(t1, t2) -> {
//first func will return user eith some data next function wll return List<UserProfile> userProfiles
return newuser;
}))
.subscribeOn(Schedulers.io())
}
private Single<List<UserProfile>> getUserProfiles(User user) {
Observable.fromIterable( user.getFirstLink().getEntities())
.flatMap(stubEntity ->stubEntity.retrieve())
.map(o -> {
//set user profile data
return userprofile
})
.toList();
}
private Single<User> getCurrentUserData(User user) {
Observable.just(user)
.map(s -> {
//set data
return user;
})
}

Categories