Java 8 : How to have a condition check in-between multiple comparisons - java

I need to sort a collection with multiple conditions. But, in between these conditions I need to modify the stream data.
Customer.class
import java.math.BigDecimal;
import java.util.Date;
public class Customer {
private int id;
private String name;
private long quantity;
private BigDecimal cost;
private Date lastPurchasedDate;
public Customer(int id, String name, long quantity, BigDecimal cost) {
this.id = id;
this.name = name;
this.quantity = quantity;
this.cost = cost;
}
// setters & getters are omitted for brevity.
}
Main.class
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
List<Customer> customers = Arrays.asList(new Customer(1, "A", 10, BigDecimal.valueOf(673.89)),new Customer(2, "B", 10, BigDecimal.valueOf(673.89)));
getCustomer(customers).ifPresent(c -> System.out.println(c.getId()));
}
private static Optional<Customer> getCustomer(List<Customer> customers) {
// #formatter:off
return customers.stream()
.max(
Comparator.comparing(Customer::getQuantity)
.thenComparing(Customer::getCost)
// here I need to check, If multiple customers are after
// comparing the cost, I have to update the lastPurchasedDate l
// attribute value & then sort it
.thenComparing(Customer::getLastPurchasedDate)
);
// #formatter:on
}
}
If there are multiple customers available after sorting by cost then I need to populate the lastPurchasedDate attribute value and then sort it by lastPurchasedDate.
Why I'm not populating the lastPurchasedDate data before ?
To get that information, I need run a query on db (To get the information, we are going to use a Table which will have millions of records). So it is a performance constraint & I want to avoid it. A very rare scenario of comparing by lastPurchasedDate needed. So I don't want to unnecessarily run this query for all the customers.

This is the solution I came up with...
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
List<Customer> customers = Arrays.asList(new Customer(1, "A", 10, BigDecimal.valueOf(673.89)),new Customer(2, "B", 10, BigDecimal.valueOf(673.89)));
getCustomer(customers).ifPresent(c -> System.out.println(c.getId()));
}
private static Optional<Customer> getCustomer(List<Customer> customers) {
// #formatter:off
return customers.stream()
.max(
Comparator.comparing(Customer::getQuantity)
.thenComparing(Customer::getCost)
.thenComparing(Main::updateLastPurchasedDate)
);
// #formatter:on
}
private static Date updateLastPurchasedDate(Customer c) {
Date lastPurchasedDate = customerRepository.getLastPurchasedDateById(c.getId());
if(lastPurchasedDate == null) {
return new Date(10000);
}
return lastPurchasedDate;
}
}

Related

Opraplanner uniform employees shifts planning

I have a problem with planning employees shifts where employees are distributed uniformly (randomly) across the shifts.
In my minimal example I use Spring boot, Lombock and Optaplanner spring boot starter (8.15.0.Final) package.
My minimal example in one file:
package com.example.planner;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.lookup.PlanningId;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.domain.variable.PlanningVariable;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.score.stream.Constraint;
import org.optaplanner.core.api.score.stream.ConstraintFactory;
import org.optaplanner.core.api.score.stream.ConstraintProvider;
import org.optaplanner.core.api.solver.SolverManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.ArrayList;
import java.util.List;
#SpringBootApplication
public class PlannerApplication implements CommandLineRunner {
#Autowired
private SolverManager<Problem, Long> solverManager;
public static void main(String[] args) {
SpringApplication.run(PlannerApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
final var problem = new Problem(
List.of(new Employee(1L), new Employee(2L), new Employee(3L)),
List.of(new Shift(1L), new Shift(2L), new Shift(3L), new Shift(4L), new Shift(5L), new Shift(6L))
);
final var job = solverManager.solveAndListen(1L, id -> problem, bestSolution -> {
for (final var shift : bestSolution.shifts) {
System.err.println("Shift " + shift.id + ": Employee " + shift.employee.id);
}
});
}
#NoArgsConstructor
public static class PlannerConstraintProvider implements ConstraintProvider {
#Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{};
}
}
#PlanningSolution
#Data #NoArgsConstructor #AllArgsConstructor
public static class Problem {
#ValueRangeProvider(id = "employeeRange")
#ProblemFactCollectionProperty
private List<Employee> employees;
#PlanningEntityCollectionProperty
private List<Shift> shifts = new ArrayList<>(0);
#PlanningScore
private HardSoftScore score;
public Problem(List<Employee> employees, List<Shift> shifts) {
this.employees = employees;
this.shifts = shifts;
}
}
#Data #NoArgsConstructor #AllArgsConstructor
public static class Employee {
#PlanningId
private Long id;
}
#PlanningEntity
#Data #NoArgsConstructor #AllArgsConstructor
public static class Shift {
#PlanningId
private Long id;
#PlanningVariable(valueRangeProviderRefs = "employeeRange")
private Employee employee;
public Shift(Long id) {
this.id = id;
}
}
}
Output of this example is:
Shift 1: Employee 1
Shift 2: Employee 1
Shift 3: Employee 1
Shift 4: Employee 1
Shift 5: Employee 1
Shift 6: Employee 1
Desired output is:
Shift 1: Employee 1
Shift 2: Employee 2
Shift 3: Employee 3
Shift 4: Employee 1
Shift 5: Employee 2
Shift 6: Employee 3
(or another uniform combinations)
You haven't defined any constraints, therefore OptaPlaner has no reason to come up with a better solution. You are not telling it what is better.
OptaPlanner "thinks" this solution is the best possible because (I guess) it has a score of 0hard/0soft (you can check it in the console), which is an ideal score.
To achieve the desired output you should define a fair workload distribution constraint that will penalize each employee with a square of its workload. Probably something like this should work in your case:
#Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[] {
fairWorkloadDistribution(constraintFactory)
};
}
...
Constraint fairWorkloadDistribution(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Shift.class)
.groupBy(Shift::getEmployee, ConstraintCollectors.count())
.penalize(
"Employee workload squared",
HardSoftScore.ONE_SOFT,
(employee, shifts) -> shifts * shifts);
}
You have no constraints. You have not told OptaPlanner what to optimize for, and therefore all solutions are equally favorable to OptaPlanner.
(In fact, I am quite surprised that this code does not fail. A situation with no constraints should have thrown an exception.)

CQEngine In Clause with MultiValueNullableAttribute

I have a class Object1 which has a list of longs called tags. I have another list of longs called tagsToSearch. How can I construct a query using CQEngine that is the following:
Select * from Object1 Where tags in (tagsToSearch)
If anyone knows how this would look using CQEngine please let me know.
This should do the trick:
package com.googlecode.cqengine;
import com.googlecode.cqengine.attribute.*;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.query.option.QueryOptions;
import com.googlecode.cqengine.query.parser.sql.SQLParser;
import java.util.*;
import static com.googlecode.cqengine.codegen.AttributeBytecodeGenerator.*;
import static com.googlecode.cqengine.query.QueryFactory.*;
import static java.util.Arrays.asList;
public class TagsExample {
static class MyObject {
final String name;
final List<Long> tags;
public MyObject(String name, List<Long> tags) {
this.name = name;
this.tags = tags;
}
static final Attribute<MyObject, Long> TAGS = new MultiValueAttribute<MyObject, Long>("tags") {
public Iterable<Long> getValues(MyObject object, QueryOptions queryOptions) { return object.tags; }
};
}
public static void main(String[] args) {
IndexedCollection<MyObject> collection = new ConcurrentIndexedCollection<>();
collection.add(new MyObject("foo", asList(1L, 2L, 3L)));
collection.add(new MyObject("bar", asList(4L, 5L, 6L)));
collection.add(new MyObject("baz", asList(7L, 8L, 9L)));
// Search via a programmatic query...
Query<MyObject> nativeQuery = in(MyObject.TAGS, asList(3L, 9L));
collection.retrieve(nativeQuery)
.forEach(object -> System.out.println(object.name));
// ..prints: foo, baz
// Search via an SQL query...
String sqlQuery = "SELECT * FROM collection WHERE tags IN (3, 9)";
SQLParser<MyObject> parser = SQLParser.forPojoWithAttributes(MyObject.class, createAttributes(MyObject.class));
parser.retrieve(collection, sqlQuery)
.forEach(object -> System.out.println(object.name));
// ..prints: foo, baz
}
}

Join two tables and send as one record

Suppose, that we have such tables:
Table Users
iduser | password
Table Marks
id | iduser | mark | idtest
Table Tests
idtest | title
Query looks like this:
#GET
#Path("/{id}/marks")
#Produces(MediaType.APPLICATION_JSON)
public Object funkcja(#PathParam("id") Integer iduser) {
Query query = em.createQuery("select m,t from Marks m, Tests t where m.idusers=:iduser and m.idtest = t.idtests");
query.setParameter("iduser", id);
List<Object> results = (List<Object>)query.getResultList();
return results;
}
I have entity classes:
Marks , Users, Tests
What I should to do in order to join tables and send JSON type on web service and how to convert JSON to entity class because I would like to show in TableView.
Perhaps there are other simple ways?
Maybe map or JsonObject?
You seem to have multiple questions here; I think you need to break these down into separate questions.
For the "main" question, which is about JPA and how to join the entities, I would do that at the entity level, not at the query level. I.e. I think I would have entity classes like:
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="Tests")
public class Test {
#Id
#Column(name="idtest")
private int id ;
private String title ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Override
public boolean equals(Object other) {
if (other instanceof Test) {
return Objects.equals(title, ((Test)other).title);
} else return false ;
}
#Override
public int hashCode() {
return Objects.hash(title);
}
}
and then the Mark entity can use a #ManyToOne annotation to reference the actual Test object (not its id):
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="Marks")
public class Mark {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id ;
#ManyToOne
#JoinColumn(name="idtest")
private Test test ;
// You probably don't want a reference to User here, as the User class
// encapsulates the password, which you don't want to throw back and
// forward across the network unnecessarily. But if the User class
// instead had a user name etc you wanted, you could use the same
// #ManyToOne technique to reference a User object here if you needed.
#Column(name="iduser")
private int userId ;
private int mark ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof Mark) {
Mark other = (Mark)obj ;
return Objects.equals(userId, other.userId)
&& Objects.equals(test, other.test)
&& mark == other.mark ;
} else return false ;
}
#Override
public int hashCode() {
return Objects.hash(userId, test, mark);
}
}
Now your query looks like
TypedQuery<Mark> query = em.createQuery("select m from Mark m where m.userId=:userid");
query.setParameter("userid", iduser);
List<Mark> results = query.getResultList();
and the Mark instances in the list already have all the data you need:
for (Mark mark : results) {
System.out.println(mark.getTest().getTitle() + ": " + mark.getMark());
}
For the remaining questions:
Assuming you have a server set up with a JAX-RS implementation (e.g. Jersey) the code snippet you showed (modified with the new query) should generate JSON output. (You can use a developer tool such as Firefox REST client to view the JSON output by specifying the appropriate URL and request headers, and viewing the response.)
On the client (JavaFX) side you can use Jersey's client library (or maybe Apache HttpComponent library) to create the web requests easily, and a library such as GSON or Jackson for mapping the JSON content you get back to a Java object for display in the TableView.
I recommend trying this and asking specific questions about the remaining pieces if you get stuck.

Cannot create TypedQuery for query with more than one return using requested result type

I am getting error "Cannot create TypedQuery for query with more than one return using requested result type"
I tried with all columns value returning. That time the application hangs. I need to get list of Client, in arraylist. Please help, I am new to JPA.
#Override
public ArrayList<Client> findAllClients() {
EntityManager entity = this.emf.createEntityManager();
List<Client> clients = entity.createQuery("select clientID,clientName from Client", Client.class).getResultList();
return (ArrayList<Client>) clients;
}
Client class is
package com.springmaven.models;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="tblclient")
public class Client {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY) #Column(name="ntClientID")
private Long clientId;
#Column(name="vcClientName")
private String clientName;
#Column(name="vcLocation")
private String location;
#Column(name="ofstTimeZone")
private Date timeZone;
#Column(name="vcCommunicationMode")
private String communicationMode;
#Column(name="vcContact")
private String contact;
#OneToMany(targetEntity=Project.class,mappedBy="client",
cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Set<Project> projects = new HashSet<Project>();
public Set<Project> getProjects() {
return projects;
}
public void setProjects(Set<Project> projects) {
this.projects = projects;
}
public Long getClientId() {
return clientId;
}
public void setClientId(Long clientId) {
this.clientId = clientId;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Date getTimeZone() {
return timeZone;
}
public void setTimeZone(Date timeZone) {
this.timeZone = timeZone;
}
public String getCommunicationMode() {
return communicationMode;
}
public void setCommunicationMode(String communicationMode) {
this.communicationMode = communicationMode;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public Client(){
}
}
Usually on Hibernate you simply make selects of an specific entity, not necessarily defining what columns you want. Something like this:
List<Client> clients = entity.createQuery("select c from Client c", Client.class).getResultList();
You are getting the TypedQuery error because the EntityManager is waiting for a collection of Clients, but instead you are selecting two specific columns of it, which will make Hibernate unable to cast the results as a Client entity.
So in your case, use the query given above and everything should work fine.
You can cast to your result in (List< clients >)
List<Client> clients = (List<Client>) entity.createQuery("select clientID,clientName from Client", Client.class).getResultList();
That is a projection query on the "client" thay only return clientID and clientName, instead of loading the full object to memory. This approach can allow to reduce network traffic to the database server and save memory.
So, you can use the next one:
List<Object[]> results =
entity.createQuery("select clientID, clientName from Client").getResultList();
This result set contains a List of Object arrays and each array represents one set of properties, in this case clientID and clientName. Now you can retrieved this:
Object[] o = results.get(0); // for first element!
Long id = (Long) o[0]; // choose the correct type!
String name = (String) o[1];

map-like serialization w/ data values as keys

I need to serialize this:
List<Event>
where the Event class is:
public class Event {
public int id;
public String foo;
public String bar;
}
into JSON of this form:
{
"123":{"foo":"...","bar":"..."},
"345":{"foo":"...","bar":"..."}
}
Taking the "id" property out of Event and storing a Map would do the trick, but I need to support duplicate IDs.
Is there an annotation I can put on the "id" property to cause Jackson to treat it as a key, with the rest of the object as the associated value?
With your current structure of ID as the key, I'm not sure having duplicate IDs is possible in the JSON spec. Maybe if you had arrays with the IDs. I think you need to re-evaluate your desired JSON output.
You could use IdentityHashMap, so you could use different instances of string containing same value and have this result:
{"1":{"foo":"foo1","bar":"bar"},"2":{"foo":"foo2.1","bar":"bar"},"3":{"foo":"foo2","bar":"baz"},"2":{"foo":"foo2","bar":"baz"}}
that you can have executing this:
import java.io.IOException;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonTest {
public static void main(final String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper om = new ObjectMapper();
IdentityHashMap<String, Event> ihm = new IdentityHashMap<String, Event>();
List<Event> list = Arrays.asList( //
new Event(1, "foo1", "bar"), //
new Event(2, "foo2", "baz"), //
new Event(2, "foo2.1", "bar"), //
new Event(3, "foo2", "baz") //
);
for (Event e : list) {
ihm.put(String.valueOf(e.id), e);
}
System.out.println(om.writeValueAsString(ihm));
}
#JsonIgnoreProperties({ "id" })
public static class Event {
public int id;
public String foo;
public String bar;
public Event(final int id, final String foo, final String bar) {
super();
this.id = id;
this.foo = foo;
this.bar = bar;
}
}
}

Categories