There is a class Movie which has two properties, id and type.
static class Movie implements Comparable<Movie> {
Integer id;
Type type;
Movie(Integer id, Type type) {
this.id = id;
this.type = type;
}
#Override
public int compareTo(final Movie other) {
return 0;
}
public Integer getId() {
return id;
}
public Type getType() {
return type;
}
}
enum Type {
TYPE_A,
TYPE_B
}
Let's say that this is the input:
// MOVIE_ID MOVIE_TYPE
// 1 TYPE_A
// 2 TYPE_B
// 3 TYPE_A
// 4 TYPE_B
and the output I want to achieve is this:
// MOVIE_ID MOVIE_TYPE
// 1 TYPE_A
// 3 TYPE_A
// 2 TYPE_B
// 4 TYPE_B
Also I want to be sure that null is greater. Are there any libraries or do I need to implement it by my own, if so, what's the best way to achieve that?
So you want to compare by type first, and then by ID? In that case, you need some compareTo logic like this:
if (type != other.type) {
return type.compareTo(other.type);
} else {
return id.compareTo(other.id);
}
To account for null, you could add more ifs.
Related
i'm using java, for example, i have 2 tables staff(id, name, status_id, company_id) and company(id, name), the corresponding entity looks like:
public class Staff {
private Integer id;
private String name;
private Integer statusId;
private Integer companyId;
private Company company;
}
public class Company {
private Integer id;
private String name;
private List<Staff> staffList;
}
for status_id of table staff, 0 means New, 1 represents Active and 2 stands for Inactive.
I need to show New, Active or Inactive on html page/excel when describe a staff status rather than 0, 1 or 2.
And I have a StaffDto:
public class StaffDto {
private Integer id;
private String name;
private Integer statusId;
private String companyName;
}
my questions are:
the statusName(New/Active/Inactive) should be in StaffDto, such that there is no need to calculate status name according to statusId on each client, right?
what is a good practice to get statusName base on statusId?
I should write code like
public class StaffDto {
private Integer statusId;
private String statusName;
public String getStatusName() {
switch(statusId) {
case 0: return "New";
case 1: return "Active";
case 2: return "Inactive";
}
}
}
is this a good practice? or something else(e.g. enum) is better?
if the logic of getting status name is added in StaffDto, what if there is another dtoj(e.g. ADto) also need to show status name, then I have to re-write this logic in ADto?
what if one client need to show New, Active or Inactive, while another client need to show A, B or C or something else, then what should I return in StaffDto? do I still return New, Active or Inactive in StaffDto, and other client need to calculate N, A or I base on statusId on their client? or should I return something else to client instead of xxxDto?
I too would go for enum as you mentioned, bind the status code to the name
then, you do not have to rewrite the logic in DTOs, Make your model have the enum rather than code or name
enum can have its own methods like getShortName for different representations
enum Status {
NEW(0), Active(1), InActive(2);
private final int code;
Status(int code) {
this.code = code;
}
public String getShortName() {
return this.name().substring(0, 1).toUpperCase();
}
public int getCode() {
return code;
}
}
So, for example consider these two objects:
class MyObject {
Integer Id;
MyObjectInner myObjectInner;
}
class MyObjectInner {
Integer Id:
String attributeToGroupBy;
}
If I wanted to group the a list of MyObjects by Id, I would do something like:
Map<Integer, List<MyObject>> myObjectsPerId =
myObjects.stream().collect(Collectors.groupingBy(MyObject::getId));
But what if I needed to group by MyObject::MyObjectInner::AttributeToGroupBy, how would I do that?
You would need to add a getId() method in you MyObject class for this to work.
class MyObject {
Integer Id;
MyObjectInner myObjectInner;
public MyObject(){}
public Integer getId() { return this.Id; }
}
Then, you could create your map with :
Map<Object, List<MyObject>> myObjectsPerId =
myObjects.stream().collect(Collectors.groupingBy(o -> o.getId()));
I am trying to have a class that has a certain list of objects (specified by another class) persisted in the database as a string (use JPA Converter - all good).
And then I want to use Specification to search inside that string.
What is the best way to create the predicates? I don't seem to understand the connection bettween the AttributeConverter and the Expression in the Specification.
The parent class:
#Entity #Table
public class A {
#Column #Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column
private String name;
#Enumerated(EnumType.STRING)
private SomeType type;
#Column(length=1000) #Convert(converter = BConverter.class)
private List<B> bList;
private Integer no;
}
The listed object class:
public class B{
private String type;
private Integer quantity;
}
The converter:
#Converter
public class BConverter implements AttributeConverter<List<B>, String> {
private static final String SEPARATOR = ":";
private static final String LIST_SEPARATOR = ";";
#Override public String convertToDatabaseColumn(List<B> bList) {
return bList.stream().map(e->convertToString(e)).collect(Collectors.joining(LIST_SEPARATOR));
}
#Override public List<B> convertToEntityAttribute(String str) {
if(str==null || str.isEmpty() ) return null;
return Arrays.stream(str.split(LIST_SEPARATOR)).map(e->convertFromString(e)).collect(Collectors.toList());
}
private String convertToString(B b){
if(entity==null) return null;
return b.getType().toString() +SEPARATOR+ b.getQuantity().toString();
}
private B convertFromString(String subStr){
if(subStr==null || subStr.isEmpty() ) return null;
String[] pair = subStr.split(SEPARATOR);
return new B(pair[0],Integer.valueOf(pair[1]));
}
}
In the database should look something like:
Table A:
id: 1;
name: "Some Name";
type: "THISTYPE";
blist: "TYPE1:11;TYPE2:22";
no: 0;
id: 2;
name: "Other Name";
type: "THISTYPE";
blist: "TYPE1:45;TYPE2:56";
no: 12;
I would then like to create Specifications to search over this table for the attributes inside the bList.
For example, search by an entity that contains a B object where type=TYPE1 and a quantity>=30.
public static Specification<A> customSpecification(String type, Integer value) {
return (root, query, builder) -> ///?????????
}
Is there a way to use such specifications where the DB attribute is a String but JAVA only sees the objects?
I have integer column as "status" in my db.
My enum class:
public enum MemberStatus {
PASSIVE(0),ACTIVE(1);
private int value;
private MemberStatus(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
My entity field:
#Column(name = "status", nullable = false)
#Enumerated(EnumType.ORDINAL)
private MemberStatus status;
Hibernate Log:
org.postgresql.util.PSQLException: ERROR: column "status" is of type integer but expression is of type bytea.
Hint: You will need to rewrite or cast the expression.bytea
I use PostgreSQL. How to solve this problem? Any idea?
I suggest you use a converter.
It's the cleanest solution i came to because:
you no longer have an issue with the order in which you add values to
the enum or if you refactor the enum elements name
you have more flexibility on what database type your column has
You can define the field as:
#Column(name = "status", nullable = false)
#Convert(converter = MemberStatusEnumConverter.class)
private MemberStatus status;
The enum becomes simpler:
public enum MemberStatus {
PASSIVE,
ACTIVE;
}
And your converter class MemberStatusEnumConverter:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter
public class MemberStatusEnumConverter implements
AttributeConverter<MemberStatus,Integer>{
#Override
public Integer convertToDatabaseColumn(MemberStatus attribute) {
switch (attribute) {
case PASSIVE:
return new Integer(0);
case COUNTYLEVEL:
return new Integer(1);
default:
throw new IllegalArgumentException("Unknown" + attribute);
}
}
#Override
public MemberStatus convertToEntityAttribute(Integer dbData) {
if (dbData == 0){
return MemberStatus.PASSIVE;
} else if (dbData == 1){
return MemberStatus.ACTIVE;
}
else{
throw new IllegalArgumentException("Unknown" + dbData);
}
}
}
This article describes the solution i implemented for your example.
I tried to fill DB tables with random data and through Hibernate.
But my code fill incompatible data into tables (not exactly incompatible it is index of this element declared at enum, for ex: at ApartmentState - FREE is first element it set to appropriate column it index - 0. But I want to put or FREE as enum or as string).
I couldn't figure out why exactly this happen.
Here is code snippet:
private List<Apartment> generateApartments() {
for (int i = 1; i <= 3; i++) {
for (int j = 2; j <= 5; j++) {
Apartment apartment = new Apartment();
// fill apartment
apartment.setRoomName(generateRoomName());
apartment.setPricePerHour(generatePrice());
apartment.setApartmentState(FREE);
apartment.setRating(getDesiredRating(j));
apartment.setSleepPlaces(getDesiredPlaces(i));
apartment.setActive(true);
// save apartment
apartments.add(apartment);
}
}
return apartments;
}
private Apartment.SleepPlaces getDesiredPlaces(int i) {
switch (i) {
case 1:
return ONE_PLACE;
case 2:
return TWO_PLACES;
case 3:
return THREE_PLACES;
}
return ONE_PLACE;
}
private Apartment.StarRating getDesiredRating(int j) {
switch (j) {
case 2:
return TWO_STARS;
case 3:
return THREE_STARS;
case 4:
return FOUR_STARS;
case 5:
return FIVE_STARS;
}
return TWO_STARS;
}
I need to fill at table some enum values, as rating (2, 3, 4) and sleep places (1, 2..).
But this puts some wrong data into table.
Here is content at workbench:
Why it puts only index, not as string or as enum.
How can I recovering this at desired value, in the future.
Here is snippet from Apartment class:
#Entity
#Table(name = "Apartments")
public class Apartment extends AbstractEntity implements Comparable<Apartment> {
private Integer id;
private String roomName;
private Double pricePerHour;
private ApartmentState apartmentState;
private StarRating rating;
private SleepPlaces sleepPlaces;
private Boolean active;
public enum ApartmentState {
FREE, REQUESTED, BOOKED, LIVED, CLEANING, PROCESSING
}
public enum StarRating {
TWO_STARS("2 stars"), THREE_STARS("3 stars"), FOUR_STARS("4 stars"), FIVE_STARS("5 stars");
private String description;
private StarRating(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
public enum SleepPlaces {
ONE_PLACE("1 person"), TWO_PLACES("2 persons"), THREE_PLACES("3 persons"), FOUR_PLACES("4 persons");
private String count;
private SleepPlaces(String count) {
this.count = count;
}
public String getCount() {
return count;
}
}
For me the best is to put enum as enum (possibly at MySql workbench) or as a string (and use name() and valueOf() from Enum class).
But how to implement it with hibernate.
How to solve this trouble?
You can add following enumeration, to indicate you want the String representation to be persisted :
#Enumerated(EnumType.STRING)
private ApartmentState apartmentState;
Use this annotation at field level:
#Enumerated(EnumType.STRING)
I also had to add
#Embeddable to the java enum
#Embeddable
public enum ApartmentState {
FREE, REQUESTED, BOOKED, LIVED, CLEANING, PROCESSING
}