Java 8 Streams Nested ForEach with different conditions - java

i'm totally new to java 8 streams. just want know how to write the below code using java stream api. Not sure on how to write nested loops with filters to map the data.
public AccountByCustomerDto getAccountDetails(int customerId, HttpServletRequest request) throws Exception {
List<Accountowner> accountOwnerList = repo.getAccountOwners(customerId);
List<AccountByCustomerDto.AccountDto> aDtoList = new ArrayList<AccountByCustomerDto.AccountDto>();
for (Accountowner accountOwner : accountOwnerList) {
String currency = accountOwner.getAccount1().getAccountCurrency();
if(accountOwner != null && currency.startsWith("USD")) {
List<Accountbalance> accountBalanceList = accountOwner.getAccount1().getAccountbalances();
List<AccountByCustomerDto.BalancesDto> balanceDtoList = new ArrayList<AccountByCustomerDto.BalancesDto>();
for (Accountbalance balance : accountBalanceList) {
String creditInclude = balance.getCreditLimitIncluded();
if(balance != null && creditInclude.equals("Y")) {
AccountByCustomerDto.BalancesDto balanceDto = AccountByCustomerDto.BalancesDto.builder()
.balanceType(balance.getBalanceType()).baDto(null)
.referenceDate(
balance.getReferenceDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
.build();
balanceDtoList.add(balanceDto);
}
}
String accountId = Integer.toString(accountOwner.getAccount1().getAccountId()) + ":"
+ accountOwner.getAccount1().getAccountCurrency();
AccountByCustomerDto.AccountDto adto = AccountByCustomerDto.AccountDto.builder()
.accountId(Utility.encrypt(accountId))
.accountNumberDisplay(accountOwner.getAccount1().getAccountDisplay())
.balances(balanceDtoList).accountLink(null).build();
aDtoList.add(adto);
}
}
return AccountByCustomerDto.builder().accounts(aDtoList).build();
}

I think just switching the code to Stream would make the code even less readable. So when one method gets very long it's a good idea to split it into smaller methods.
When the code is split up into smaller methods streams provide a real benefit when it comes to readability compared to traditional loops.
Element in stream remains in stream if accountOwnerCurrencyIsUSD is true. The remaining elements are mapped to a AccountDto using createAccountDto and the result is collected into a AccountDto list. Is much easier to read than create list, loop through other list, get currency, check currency, create another list...
public AccountByCustomerDto getAccountDetails(int customerId, HttpServletRequest request) throws Exception {
List<AccountByCustomerDto.AccountDto> aDtoList = accountOwnerList.stream()
.filter(this::accountOwnerCurrencyIsUSD)
.map(this::createAccountDto)
.collect(Collectors.toList());
return AccountByCustomerDto.builder().accounts(aDtoList).build();
}
private AccountCustomerDto.AccountDto createAccountDto(Accountowner owner) {
String accountId = accountOwner.getAccount1().getAccountId() + ":" + accountOwner.getAccount1().getAccountCurrency();
List<AccountByCustomerDto.BalancesDto> balanceDtoList = accountOwner.getAccount1().getAccountbalances()
.stream()
.filter(this::includesCredit)
.map(this::createBalanceDto)
.collect(Collectors.toList());
return AccountByCustomerDto.AccountDto.builder()
.accountId(Utility.encrypt(accountId))
.accountNumberDisplay(accountOwner.getAccount1().getAccountDisplay())
.balances(balanceDtoList)
.accountLink(null)
.build();
}
private AccountByCustomerDto.BalancesDto createBalanceDto(Accountbalance balance) {
return AccountByCustomerDto.BalancesDto.builder()
.balanceType(balance.getBalanceType())
.baDto(null)
.referenceDate(alance.getReferenceDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
.build();
}
private boolean accountOwnerCurrencyIsUSD(Accountowner owner) {
return accountOwner != null && "USD".eqauls(accountOwner.getAccount1().getAccountCurrency());
}
private boolean includesCredit(Accountbalance balance) {
return balance != null && "Y".equals(balance.getCreditLimitIncluded());
}
I have not way to test the code, so take it with a grain of salt.

Related

Apache Camel Reducing Number though each Split Iteration

I have a directory of three files in the body of a Camel exchange, and I'm going to iterate through those 3 files using a Split. What I would like to do, is for each iteration, update a property with the total of all files (3 for this example) minus the current iteration. So at the some point, the goal is to have a property set to 1, so on another part of the code I can do logic based on this. I've tried a couple of different approaches but failed, and here I am. Here's a snippet of something I tried (simplified):
`private void test() {
from("timer:test?fixedRate=true&period=10000")
.process(exchange -> {
Path pathD = FileSystems.getDefault().getPath("Lmao").toAbsolutePath();
File folder = new File(pathD.toString());
String[] fileList = folder.list();
if (fileList != null) {
exchange.setProperty("PROPERTY", fileList.length);
}
exchange.getIn().setBody(fileList);
})
.split(body(), new AggregationStrategy() {
#Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
if (oldExchange == null) {
Integer iterationsLeft = newExchange.getProperty("PROPERTY", Integer.class);
Integer iterationsLeftMinusOne = iterationsLeft - 1;
newExchange.setProperty("PROPERTY", iterationsLeftMinusOne);
return newExchange;
} else {
Integer iterationsLeft = oldExchange.getProperty("PROPERTY", Integer.class);
oldExchange.setProperty("PROPERTY", iterationsLeft - 1);
return oldExchange;
}
}
})
.process(exchange -> {
Integer test = exchange.getProperty("PROPERTY", Integer.class);
System.out.println(test);
})
.end();
}
`
This code keeps printing 3 all the time and I wanted 3,2,1

Unit test for SpringBoot Service PUT method

I'm writing Unit tests for my SpringBoot application and I'm completely lost in the PUT method. I'm getting Expecting code to raise a throwable but I guess the complete test is wrong.
Here's my code:
the PUT method in the Service
public void updateCar(String id, String carModel, Integer HP, Integer year, String designer) {
Garage garage = garageRepository.findById(id)
.orElseThrow(() -> new IllegalStateException(
"A car with the id " + id + " is not in our Garage."));
if(carModel != null && carModel.length() > 0 && !Objects.equals(garage.getCarModel(), carModel)) {
garage.setCarModel(carModel);
}
if(HP != null && !Objects.equals(garage.getHP(), HP)) {
garage.setHP(HP);
}
if(year != null && !Objects.equals(garage.getYear(), year)) {
garage.setYear(year);
}
if(designer != null && designer.length() > 0 && !Objects.equals(garage.getDesigner(), designer)) {
garage.setDesigner(designer);
}
garageRepository.save(garage);
}
My Repository:
#org.springframework.stereotype.Repository
public interface GarageRepository extends MongoRepository<Garage, String> {
Optional<Garage> findByCarModel(String carModel); }
And here's a test:
#Test
#DisplayName("Update Car Test")
void testUpdateCar() {
String id = "630ca281f12905d5f5249f08";
String carModel = "Shelby Cobra";
int HP = 485;
int year = 1964;
String designer = "Carroll Shelby";
Garage testGarage = new Garage();
testGarage.setId(id);
given(garageRepository.findById(testGarage.getId()))
.willReturn(Optional.of(testGarage));
assertThatThrownBy(() -> garageService.updateCar(id,carModel,HP,year,designer))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("A car with the id " + id + " is not in our Garage.");
verify(garageRepository.findById(id).get().getId());
}
Other tests are fine (Create, Read, and Delete), but just this one confuses the hell out of me. I don't think the Controller is necessary, so I skipped adding it.
You're setting up your test so that findById is returning an Optional containing testGarage. That is, something is actually being found.
The updateCar method only throws the IllegalStateException if findById returns an empty Optional - in other words, there's no object in the repository matching the search criterion.
So in this case, the exception is never thrown, and that's what makes the test fail.

How to validate certain input parameters through a Validation API?

I have an API in which I am doing my own validation for certain input parameters. For example -
public Builder(int clientId) {
TestUtils.assertNonNegative(clientId, "ClientId");
this.clientId = clientId;
}
public Builder setUserId(String userid) {
TestUtils.assertNotNull(userid, "UserId");
this.userid = userid;
return this;
}
And my assertNonNegative and assertNotNull method in TestUtils class is like this -
public static void assertNonNegative(int val, String attr) {
if (val <= 0) {
s_logger.logError("Attribute = ", attr, " Value = ", val, " error=", attr, " cannot be negative or zero");
throw new IllegalArgumentException(attr + " cannot be negative or zero");
}
}
public static void assertNotNull(String value, String key) {
if (value == null || value.isEmpty()) {
s_logger.logError("Key = ", key, " Value = ", value, " error=", key,
" cannot be NULL or empty String");
throw new IllegalArgumentException(key + " cannot be null OR empty");
}
}
I am wondering is there any validation API available in any open source projects which I can use to substitute the above internal method I have? If yes, can anyone provide an example how would I achieve this? I still need to throw the same message back as an IllegalArgumentException
I dont understand why would you use an external API tu achieve a nullOrEmpty or a non-negative number validation but...
If you would like to verifiy an id of a user in a database directly in you Java app
This might interest you to learn:
http://www.mkyong.com/java/how-to-send-http-request-getpost-in-java/
Use a bit of PHP and verify if the user is in the database.
if(isset($_GET['idcmd']))
{
switch($_GET['idcmd'])
{
case 1:
if(isset($_POST['iduser']))
{
$sql= "SELECT idUser FROM users WHERE idUser=:iduser ";
$result = $db_conn->prepare($sql);
$result->bindParam(":iduser" ,$_POST['iduser']);
$result->execute();
$num=$result->fetchColumn();
if($num > 0){
echo "cool";
}else{
echo "nocool";
}
}
break;
}
}
Now if you make a POST request to the url www.mydomain.com/myapi.php?idcmd=1 and get the response cool, it means that the user is in database.
I hope it helps.

how to cache collection or array parameters with simple-spring-memcache

My project use a simple-spring-memcache to caching a service method, but it does't work, as follows:
#ReadThroughSingleCache(namespace = "AdvServiceImpl.findByIdList", expiration = 60)
public List<Adv> findByIdList(#ParameterValueKeyProvider(order = 0) List<String> idList, #ParameterValueKeyProvider(order = 1) String deviceType, #ParameterValueKeyProvider(order = 2) String sourceId) throws Exception {
But I write so it can work:
#ReadThroughSingleCache(namespace = "AdvServiceImpl.findByIdList", expiration = 60)
public List<Adv> findByIdList(List<String> idList, #ParameterValueKeyProvider(order = 0) String deviceType, #ParameterValueKeyProvider(order = 1) String sourceId) throws Exception {
#ReadThroughSingleCache shouldn't be used if one of the method's arguments annotated with ParameterValueKeyProvider is of type List. Instead of #ReadThroughSingleCache try to use #ReadThroughMultiCache

GreenDao whereOr and conditions adding in a loop

I need to add some or clauses to query. I need to do it in a loop.
StringTokenizer st = new StringTokenizer(symptoms, ",");
while(st.hasMoreTokens()){
qb.whereOr(Properties.Symptom.like("%" + st.nextToken() + "%"));
}
How I can add those or conditions properly, because this above is not working as expected. I want to add or for every symptom.
If you look at the documentation, you'll see that whereOr() takes an unbounded number of conditions. What you want to do is add them all at once in an array:
StringTokenizer st = new StringTokenizer(symptoms, ",");
ArrayList<WhereCondition> whereConditions = new ArrayList<WhereCondition>();
while(st.hasMoreTokens()){
whereConditions.add(Properties.Symptom.like("%" + st.nextToken() + "%"));
}
// Give the ArrayList an already allocated array to place its contents in.
WhereCondition[] conditionsArray = new WhereCondition[whereConditions.size()];
conditionsArray = whereConditions.toArray(conditionsArray);
qb.whereOr(conditionsArray);
It looks like the method call in the documentation takes two non-array WhereConditions and then an ellipsized argument, which accepts an array or an additional comma-separated list of objects. So you might have to do something like this to get it to work properly:
qb.whereOr(conditionsArray[0], conditionsArray[1], Arrays.copyOfRange(conditionsArray, 2, conditionsArray.length));
ADDENDUM: It looks like you're using APIs that don't match the documentation, possibly an older version of greenDAO. I wrote this solution based off the current documentation. I can't guarantee that it will work for you. I recommend updating if possible.
Try this:
StringTokenizer st = new StringTokenizer(symptoms, ",");
WhereCondition where = null;
while(st.hasMoreTokens()){
if (where != null) {
where = qb.or(where, Properties.Symptom.like("%" + st.nextToken() + "%"));
} else {
where = Properties.Symptom.like("%" + st.nextToken() + "%");
}
}
qb.where(where).list();
I had the same problem so I added my own method in an Util class to perform the same behavior when I have one or several WhereCondition in an array.
Here is my gateway method :
public static QueryBuilder whereOr(QueryBuilder queryBuilder, WhereCondition[] whereConditions){
if(whereConditions == null) return queryBuilder.where(null);
else if(whereConditions.length == 1) return queryBuilder.where(whereConditions[0]);
else return queryBuilder.whereOr(whereConditions[0], whereConditions[1], Arrays.copyOfRange(whereConditions, 2, whereConditions.length));
}
Use : Util.whereOr(queryBuilder, whereConditionsArray);
Default : Can't use the Builder Pattern from the QueryBuilder with this approach
(More later) Here, I share you some code which could spare you time when developping DAO methods.
public class QueryBuilderUtil {
public static final String EQ = "=?";
public static final String NOTEQ = "<>?";
public static final String LIKE = " LIKE ?";
public static final String GE = ">=?";
public static final String LE = "<=?";
public static final String GT = ">?";
public static final String LT = "<?";
public static QueryBuilder whereOrOnSamePropertyWithDifferentValues(QueryBuilder queryBuilder, Property property, String operation, String values, String separator) {
return whereOrOnSamePropertyWithDifferentValues(queryBuilder, property, operation, values.split(separator));
}
public static QueryBuilder whereOrOnSamePropertyWithDifferentValues(QueryBuilder queryBuilder, Property property, String operation, String[] values) {
WhereCondition[] whereConditions = new WhereCondition[values.length];
int i = 0;
for (String value : values) {
whereConditions[i++] = new WhereCondition.PropertyCondition(property, operation, value);
}
return whereOr(queryBuilder, whereConditions);
}
public static QueryBuilder whereOr(QueryBuilder queryBuilder, WhereCondition[] whereConditions) {
if (whereConditions == null) return queryBuilder.where(null);
else if (whereConditions.length == 1) return queryBuilder.where(whereConditions[0]);
else return queryBuilder.whereOr(whereConditions[0], whereConditions[1], Arrays.copyOfRange(whereConditions, 2, whereConditions.length));
}
}
With this class, you can perform a whereOr with the same property on multiples "values string" in one line. It was necessary to clean my code :). However you can only do simple operations like variables declared in the class.
Example :
public List<Block> loadAllByModId(String mods_id) {
synchronized (this) {
QueryBuilder<Block> queryBuilder = queryBuilder();
QueryBuilderUtil.whereOrOnSamePropertyWithDifferentValues(queryBuilder, Properties.ModId, QueryBuilderUtil.EQ, mods_id, ";");
query_list = queryBuilder.build();
}
Query<Block> query = query_list.forCurrentThread();
return query.list();
}
Hope it helps

Categories

Resources