SpringData/Hibernate: Sort enum by it's field - java

I have small problem with sorting enums by custom rule with springdata/hibernate.
A have an enum called let's say: DeviceState, which has its own priority field.
public enum DeviceState {
ON(4), OFF(3), UPDATE(2), CRITICAL(0), WARNING(1);
private final int priority;
private DeviceState(int priority) {
this.priority = priority;
}
public int getPriority() {
return priority;
}
}
The enum is used by DeviceEntity:
#Entity
public class Device implements Serializable {
// .... other fields
#Enumerated(EnumType.STRING)
private DeviceState state;
// ....
}
I use SpringData/Repository to get entities from database, I have used findAll(specification, pageable) method from org.springframework.data.repository.Respository class.
Page<Device> pagedDevices = deviceRepository.findAll(DeviceSpecifications.findByFilter(filter), pageable);
And if my request cointains sort=state,asc it will return the Devices sorted by state - alphabetically, but I would like to use priority field as sort criteria instead.
What have i tried:
change #Enumerated(STRING) to #Enumerated(ORIDINAL) - but this cannot happen this is production working system with existing data, there are several devices which are using by external system and my project needs String values.
Control sort order of Hibernate EnumType.STRING properties but i cannot add any new column since I already have production data and all enum values are persisted as String.
I also have tried some "workarounds" with wrapping Page result - but these are just workarounds not pure solutions :)
Any clue, help? idea?
Please keep in mind that this is production working system and I cannot do any "fireworks".

order by case
when state = 'CRITICAL' then 0
when state = 'WARNING' then 1
when state = 'UPDATE' then 2
when state = 'OFF' then 3
when state = 'ON' then 4
end

Related

Hints about OptaPlanner configuration to solve Vehicle Routing (real-time)

I would be glad for any kind of opinion on this setup for a Vehicle Routing Problem.
First of all, these are my first steps with this tool, so please forgive me if I'm totally out of scope :-)
I've made an algorithm without optaplanner, to test a basic rules setup.
It worked for a single Vehicle, but optaplanner looked very similar to my original idea to assign points to each satisfied rule, then select the vehicle with the higest score.
It's also configurable, and for sure it's better than what I made before.
Let's begin
Looking at the docs, and some videos, seems that VRP problems are solved when all the pickup points have been already defined in a dataset feeded into the Solver.
What about finding a Vehicle for each request in real-time, refusing it in case no vehicle can satisfy the constraints? Consider that there are calls to external (paid) Maps services, that are making the process slower, and have a cost. It's better to avoid redundancy in these calls.
Pickup requests can be done for any future date, but not the current day.
Planning Entity - the pickup request
#PlanningEntity
public class FindBestVehicleRequest
{
#PlanningId
private Long id;
// Shadow variable candidate
private Double requiredSpace;
// Shadow variable candidate
private int requiredAutonomy;
private String pickupAddress;
// Shadow variable candidate
private LatLng pickupPosition;
private LocalDateTime pickupDateTime;
#PlanningVariable(valueRangeProviderRefs = "vehicle")
private Vehicle vehicle;
...
}
Calculations involved in each request
I've read into the docs about shadow variables, I'm still far from understanding how to define them, but I suppose that are useful in my case: as stated before, for each request I need to call the Maps Service(Google, OpenStreetMaps, ...) in order to calculate the distance to reach the pickup address from vhere the vehicle is located.
Obtain the Vehicle origin position,
some pseudo code of the logic:
if (vehicle.hasOrdersBefore(pickupDateTime) {
LatLng origin = vehicle.lastOrderBefore(pickupDateTime).getPosition();
String destination = pickupAddress;
Integer distance = mapsServer.getDistance(origin, destination);
return distance;
}
There are more calculations like this one involved, but there's no need to list all of them, they're similar.
I'm studying all the available algorithm types to find the one that's more indicated for this problem.
ConstraintProvider implementation
public class BestVehicleConstraintProvider implements ConstraintProvider {
#Override public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
vehicleHasAutonomy(constraintFactory)
};
}
// One HARD constraint
private Constraint vehicleHasAutonomy(ConstraintFactory constraintFactory) {
return constraintFactory.from(FindBestVehicleRequest.class)
.groupBy(FindBestVehicleRequest::getVehicle, sum(FindBestVehicleRequest::getRequiredAutonomy))
.filter((vehicle, requiredAutonomy) -> requiredAutonomy > vehicle.getVehicleTypeProperties().getMaxKmAutonomy())
.penalize("vehicleHasAutonomy", HardSoftScore.ONE_HARD,
((vehicle, requiredSpace) -> vehicle.getVehicleTypeProperties().getMaxKmAutonomy() - requiredSpace));
}
}
And the final part,
the Solution class
#PlanningSolution
public class FindBestVehicleSolution
{
#PlanningEntityCollectionProperty
private List<FindBestVehicleRequest> processes;
#ProblemFactCollectionProperty
#ValueRangeProvider(id = "vehicle")
private List<Vehicle> vehicles; // <----- I'm fetching a list of active Vehicles in
// the requested pickupDate, and passing it here
#ProblemFactProperty
private String pickupAddress;
// private LatLng pickupAddressPosition; // shadow variable ? how to call the map server
// and populate this field ?
#PlanningScore
private HardSoftScore score;
...
}
Ok, so I think that all the code is here. I'm looking for suggestions on proper ways to:
- call the maps server to get diestances in an efficient way
- avoid repeating the same calculations
- (IMPORTANT!) if a Vehicle satisfy certain rules, like if it has no assigned orders in the selected day, end the evaluation process directly (vehicle found!)
Yes I'm asking too much maybe, but the documentations is a bit hard to adapt to this situation, I think that with time I will get better, but I'd like to make some simulations with Optaplanner Workbench soon :-)
Thanks for anyone that will give any kind of suggestion!

Result from query with join loses order when converted to POJO list

I'm using JOOQ to create a native query to an Oracle 12 database.
When I convert the result to my POJO the order from the order by clause is lost.
Basically I'm joining two tables, Tasks and Variables. A Task can have many Variables.
I'm converting the results to my POJO this way:
select.fetchGroups(Task.class, Variable.class);
public class Task{
private String id;
private String name;
private String formKey;
private List<Variable> variables = new ArrayList<Variable>();
getters()/setters()
...
hashCode()
...
equals()
}
public class Variable{
private String vId;
private String vName;
private String vValue;
private String vType;
getters()/setters()
...
hashCode()
...
equals()
}
I'm trying to "plug" my order clause at the end of the select manually (as didn't find a way to build it up with the jooq sintax, some insights on that will be appreciated):
The jooq dslcontext
#Autowired
private DSLContext dsl;
The list that is populated
Map<Task, List<Variable>> response;
The plain order by sentence.
String orderClause = " case when V.NAME_ = '"+sortColumn+"' then V.TEXT_ end "+sortDirection;
(If I log the plain SQL of this, the result is correctly ordered)
SelectSeekStep1<Record, Object> selectOrdered = select.orderBy(DSL.field(orderClause));
response = selectOrdered.fetchGroups(Task.class, Variable.class);
At this point my response variable is already a map, but the order is lost.
There's no error messages or exceptions, the Map is returned as expected, but the order is lost.
Please tell me if I'm missing some critical detail, and I'll add it.
The various fetchGroups() methods use a LinkedHashMap behind the scenes, which they return to you, and they iterate the records in fetch order, so any order you provide the jOOQ query with is stable with respect to these methods.
From what you've shown, you're ordering by some V columns, which probably stands for Variable. You cannot possibly expect that order to be retained when grouping by Task in the client, although the ordering is stable per Task. I'll illustrate. If this is what you're getting from the database (because you order by variable):
TASK VARIABLE
1 A
2 B
3 C
1 D
2 E
Then, these fetchGroups() methods will produce the following client side grouping:
TASK = 1
VARIABLE = [A, D]
TASK = 2
VARIABLE = [B, E]
TASK = 3
VARIABLE = [C]

Using Android & Google App Engine on Android Studio

I'm developing an app with backend and I decided to try using Google App Engine for my backend. Since I'm really new on Google App Engine, I'm little bit confused with the logic.
Basically, I have a couple of model classes to represent my object types. Lets say one of them is User and another is Item. Users have items and an item can belong more than one user. So User X can have 25 items including Item A, and User Y can have totally different 20 items and also the Item A.
Right now my User class looks like this:
#Entity
public class User {
#Id private Long id;
private String name;
private String emailAddress;
private String photoURL;
//All getters and setters...
}
And my Item class is approximately same. One of my questions is, where should I add some kind of list, like a list of Items into User. And which annotation should I use? What will that annotation provide me as a result (a reference, an id or a complete object)?
Another question related to this is, in my endpoint class, how can I get a list of Items that a specific User has (or list of Users that owns a specific Item)?
One last totally unrelated question, should I do anything to make id auto increment or will it be automatic if I won't provide any id while inserting an item?
You can search in the datastore for 2 things: keys and indexed properties.
class Thing {
#Id Long id;
#Index String property;
}
At some point you save some entities
Thing thing1 = new Thing();
thing1.property = "yes";
Thing thing2 = new Thing();
thing2.property = "no";
ofy().save().entities(thing1, thing2).now();
Now you can search for all entities based on their indexed properties. E.g. for all things with property == "yes".
List<Thing> things = ofy().load().type(Thing.class).filter("property", "yes").list();
Would return exactly thing1.
The same works with Lists of properties. And it works with lists of references/keys to other properties.
class User {
#Id Long id;
#Index List<Key<Item>> items;
}
class Item {
#Id
Long id;
}
List<User> searchUsersWithItem(long itemId) {
Key<Item> itemKey = Key.create(Item.class, itemId);
return ofy().load().type(User.class).filter("items", itemKey).list();
}
List<User> searchUsersWithItem(Item item) {
return ofy().load().type(User.class).filter("items", item).list();
}
// just loads all the referenced items in the owner
List<Item> searchItemsWithOwner(User owner) {
return new ArrayList<Item>(ofy().load().<Item>values(owner.items).values());
}
filter works with refs, keys and entitiy instances.
To be found things must be indexed https://cloud.google.com/datastore/docs/concepts/indexes / https://github.com/objectify/objectify/wiki/Queries
What's left for you to decide is how you model your relation. There are multiple ways. A user that owns a set of items which can be owned by set of users is actually a many-to-many relation. You could represent it like
class User { List<Key<Item>> items; }
class Item { }
or
class User { }
class Item { List<Key<User>> owners; }
or
class User { List<Key<Item>> items; }
class Item { List<Key<User>> owners; }
or even
class User { }
class Item { }
class Ownership { Key<Item> item; Key<User> user; }
Each approach has it's ups and downs with respect to data consistency and searchability / performance. In the initial example it's trivial to search for all items of a user since all you have to to is to load that one user and you have the list of items. The other direction requires the query approach.
So with respect to search performance you benefit from having the list of owners in the items as well as the list of items in the user because that way you don't need queries at all. The big downside becomes data consistency. If you fail to update both user and item at the same time you can have items that believe to be owned by a user where the user thinks different.
The last approach, using an explicit "Ownership" entity is essentially the traditional pivot / junction table https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29 that is the result of transforming a many-many relation into 2 one-many relations. Using that would result in easy consistency, but the worst query performance.
Parent relations can sometimes be useful but only if there is an actual 1 to many relation where the parent needs to exist.
Also note how keys are not foreign keys like in traditional SQL databases as they can exist without an entity. So you'll have to take care of consistency regardless of what you do.

Enum saving in Hibernate

I am trying to save an Enum field to database but I am having a problem mapping the field to database. The code I have is as follows:
public enum InvoiceStatus {
PAID,
UNPAID;
}
and I am using this enum in one of my application classes as follows:
public class Invoice {
Enumerated(EnumType.ORDINAL)
#Column(name="INVOICE_STATUS", nullable = false, unique=false)
private InvoiceStatus invoiceStatus;
}
finally I let the app user select the Invoice Status from the view (JSP) using a drop down menu.
But I am not sure how to map the value received from the drop down menu selection to the Invoice Status field
I tried mapping the value received to short as follows, but it won't compile
invoice.setInvoiceStatus(Short.parseShort(request.getParameter("inbStatus")));
can someone please tell me how to map the data received from the view to the enum field?
Enum ordinal values are zero based indexes. In your case:
PAID = 0
UNPAID = 1
So the following code will return PAID:
int invoiceStatus = 0;
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
And the following code will return UNPAID:
int invoiceStatus = 1;
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
That means you should be able to do this way:
short invoiceStatus = Short.parseShort(request.getParameter("inbStatus"));
invoice.setInvoiceStatus(InvoiceStatus.values()[invoiceStatus]);
But only if inbStatus is 0 or 1. You should always validate user input for null and invalid values.
I see that u are using
Enumerated(EnumType.ORDINAL)
however after a while it could be quite difficult to troubleshoot if your enum will grow. Another issue with the ordinal is that you could refactor your code and change the order of the enum values and after that you could be in trouble. Mainly if it is a shared codebase and someone just decides to cleanup the code and "group the relevant enum constants together". If you'll use:
Enumerated(EnumType.STRING)
Directly the enum "name" will be inserted into the database. (Therefore you need Varchar type). If you want to present more user friendly version of your enum you could probably have:
public enum InvoiceStatus {
PAID(0, "Paid"), UNPAID(1, "Unpaid"), FAILED(2, "Failed"), PENDING(3, "Pending");
private int st;
private in uiLabel;
private InvoiceStatus(int st, String uiLabel){
this.st = st;
this.uiLabel = uiLabel;
}
private Map<String, InvoiceStatus> uiLabelMap = new HashMap<String, InvoiceStatus> ();
static {
for(InvoiceStatus status : values()) {
uiLableMap.put(status.getUiLabel(), status);
}
}
/** Returns the appropriate enum based on the String representation used in ui forms */
public InvoiceStatus fromUiLabel(String uiLabel) {
return uiLableMap.get(uiLabel); // plus some tweaks (null check or whatever)
}
//
// Same logic for the ORDINAL if you are keen to use it
//
}
Probably this could be also a solution for your problem, however i would really not use the ORDINAL based mapping. But just personal feeling.

Persist HashMap in ORMLite

Im using ORMLite in my Android app. I need to persist this class, which has a HashMap. What is a good way of persisting it? Its my first time trying to persist a HashMap, also first time with ORMLite so any advice would be greatly appreciated!
*Edit*
If that makes any difference, the Exercise class is simply a String (that also works as id in the database), and the Set class has an int id (which is also id in database), int weight and int reps.
#DatabaseTable
public class Workout {
#DatabaseField(generatedId = true)
int id;
#DatabaseField(canBeNull = false)
Date created;
/*
* The hashmap needs to be persisted somehow
*/
HashMap<Exercise, ArrayList<Set>> workoutMap;
public Workout() {
}
public Workout(HashMap<Exercise, ArrayList<Set>> workoutMap, Date created){
this.workoutMap = workoutMap;
this.created = created;
}
public void addExercise(Exercise e, ArrayList<Set> setList) {
workoutMap.put(e, setList);
}
...
}
Wow. Persisting a HashMap whose value is a List of Sets. Impressive.
So in ORMLite you can persist any Serializable field. Here's the documentation about the type and how you have to configure it:
http://ormlite.com/docs/serializable
So your field would look something like:
#DatabaseField(dataType = DataType.SERIALIZABLE)
Map<Exercise, List<Set>> workoutMap;
Please note that if the map is at all large then this will most likely not be very performant. Also, your Exercise class (and the List and Set classes) need to implement Serializable.
If you need to search this map, you might consider storing the values in the Set in another table in which case you might want to take a look at how ORMLite persists "foreign objects".

Categories