I want to map Enum's value with hibernate for fetching data from DB.
In DB column **EASE_RATING** is number[1]. I'm able to save data in DB as Numeric.
But When I retrieve data by Criteria.list() I get easeRating=Two instead of easeRating=2.
My question is How can I get Data in form of Ordinal Or Enums's Value.
public enum Rating {
Zero(0), // [No use] Taken Zero B'Coz Ordinal starts with 0
One(1),
...
Five(5);
private final int value;
Rating(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
public static Rating getRating(int x) {
switch(x) {
case 1: return One; ...
case 5: return Five;
}
return One;
}
}
POJO:
#Enumerated(EnumType.ORDINAL)
#Column(name = "EASE_RATING", nullable = false)
private Rating easeRating;
UPDATE
I want this value in int[ordinal()] in Hibernate List().
I'm hitting DB through hibernate.
List<CustomerFeedback> result = criteria.list();
I can achieve value throgh **getValue()**
System.out.println(Rating.Five.getValue()); // 5
System.out.println(Rating.Five); // Five
System.out.println(Rating.Five.name()); // Five
But How would I get this in Hibernate list()
Hibernate is working fine. you question is about Enum.
toString() default implementation returns the name of the enum. The name is the literal of the enum: "One", "Two", etc...
If you want to obtain the ordinal of a enum stating with 1, you have to create a new method, sincel ordinal() is final:
/**
* One-starting index enumeration
*/
public enum Rating {
One, Two, Three, Four;
public int position() {
return ordinal() + 1;
}
public static Rating getRating(int x) {
return Rating.values()[x - 1];
}
public static void main(String args[]) {
Rating one = Rating.One;
System.out.println("ToString() (name): " + one);
System.out.println("Ordinal position stating in 1: " + one.position());
}
}
Answer to update 1:
Why don't just map Rating list to a value list?
List<Rating> ratings = Arrays.asList(Rating.values());
List<Integer> ints = ratings.stream()
.mapToInt(Rating::position)
.boxed()
.collect(Collectors.toList());
I am not sure why you need raw database data; my recommendation is that you should always use Rating enum and take the ordinal/value from its instances in use cases where you need it.
However, one of the solutions to meet your requirements could be to map another entity to the same table with the following mapping:
#Column(name = "EASE_RATING", nullable = false)
private Integer easeRating;
Also include any other desired columns in this entity (columns which you use in this use case). Then use this entity in the query.
Related
In my assignment I have to use an enum to make an EnumSet of elements that fit the criteria given. So, the code needs to be as flexible as possible and allow any criteria that could be applied to the object declared in the enum.
I've been testing things out, my code involves taking the finals of the enum into the static context, so that a boolean can be applied to them, and then looping through each declared object in the enum to see if they fit the criteria. For some reason though, the state of the boolean doesn't change to true, when the value of the static fields fit the criteria.
Here is my code:
public class test {
// enumeration of persons by age and sex
enum Name {
Adam("Male", 17),
Amy("Female", 24),
Boris("Male", 12),
Bella("Female", 16);
final String _sex;
final int _age;
// static variants to "turn" values to the static context
volatile static String sex = "";
volatile static int age = 0;
Name(String sex, int age) {
_sex = sex;
_age = age;
}
}
public static void main(String[] args) {
// creating a set of people older than 17
EnumSet<Name> set = makeSet(Name.age >= 17);
System.out.print(set.toString());
}
static EnumSet<Name> makeSet(boolean query) {
EnumSet<Name> set = EnumSet.noneOf(Name.class);
for (Name element : Name.values()) {
// this is the "turning" of the values to the static context
Name.sex = element._sex;
Name.age = element._age;
// PROBLEM LIES HERE
// the query remains false, even when age is above 17
if (query) {
set.addAll(EnumSet.of(element));
}
}
return set;
}
}
Changing the static fields to volatile doesn't fix the problem either, so I don't think it's a caching issue.
So, why does the boolean not update? Is there a work-around?
The problem you are facing is that the predicate (Name.age >= 17) is checked at the moment of the function call:
When you are calling makeSet(Name.age >= 17), what actually happens is, the predicate returns a boolean, which at the time of checking returns false, and false is therefore parsed into the function.
Answered in comments by #Turing85
The problem occurred because the boolean is being passed by value.
Using a Predicate instead of just a boolean fixed the problem, because the object reference would be passed on instead, allowing the value to change after the makeSet() method was invoked.
Furthermore, this eliminates the need to take the finals of the enum into the static context.
Here is my code now:
public class test {
// enumeration of persons by age and sex
enum Name {
Adam("Male", 17),
Amy("Female", 24),
Boris("Male", 12),
Bella("Female", 16);
final String sex;
final int age;
Name(String sex, int age) {
this.sex = sex;
this.age = age;
}
}
public static void main(String[] args) {
// creating a set of people older than 17
EnumSet<Name> set = makeSet(query -> query.age >= 17);
System.out.print(set.toString());
}
static EnumSet<Name> makeSet(Predicate<Name> query) {
EnumSet<Name> set = EnumSet.noneOf(Name.class);
for (Name element : Name.values()) {
// PROBLEM FIXED
if (query.test(element)) {
set.addAll(EnumSet.of(element));
}
}
return set;
}
}
I need to create an enum with about 300 values and have the ability to get its value by id (int). I currently have this:
public enum Country {
DE(1), US(2), UK(3);
private int id;
private static Map<Integer, Country> idToCountry = new HashMap<>();
static {
for (Country c : Country.values()) {
idToCountry.put(c.id, c);
}
}
Country(int id) {
this.id = id;
}
public static Country getById(int id) {
return idToCountry.get(id);
}
}
That enum is going to be used a lot, so I'm wondering if this is the best possible solution performance-wise.
I've read http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html over and over again, but couldn't find the part that describes at what time the
static {
}
block is called, and if it is guaranted that this will be called only once. So - is it?
In case if the first country id is 0 and ids are incremented by 1, you may use the next approach:
Cache enum values in array. Enum.values() returns elements in the same order as they declared in enum. But it should be cached, as it creates new array every time it is invoked.
Get values from cached array by id, which will be array index.
Please, see the code below:
enum Country {
A, B, C, D, E;
private static final Country[] values = Country.values();
public static Country getById(int id) {
return values[id];
}
}
UPDATE: To get Country's id, ordinal() method should be used. And to make getting id code clearer, the next method can be added to the enum:
public int getId() {
return ordinal();
}
Static initializer blocks are called once when the class is initialized. It's not guaranteed to be called once, but it will be unless you're doing something exotic with class loaders.
So, your approach is probably fine from a performance perspective. The only changes I'd propose would be to make your fields final.
An alternative way to represent the mapping could be to store elements in an array (or a list):
Country[] countries = new Countries[maxId + 1];
for (Country country : Country.values()) {
countries[country.id] = country;
}
You could then look them up by element index:
System.out.println(countries[1]); // DE.
This avoids the performance penalty of having to box the id in order to call idToCountry.get(Integer).
This of course requires you to have non-negative IDs (and ideally the IDs would be reasonably contiguous, to avoid having to store large runs of null between countries).
First you don't need to have a static block to create the map. You can just add your code to constructor where each component adds itself to your map. Enum is ALWAYS a sigleton so your constructor is guaranteed to be called only once (per a enum value) Also you don't need to even have ID as Enum has method public final int ordinal() that returns its zero-based sequential number in the enum. In your case ordinals would be 0 for DE, 1 forUS and 2 UK.
Here is an example:
public enum Country {
DE, US, UK;
private static Map<Integer, Country> idToCountry = new HashMap<>();
Country() {
idToCountry.put(this.ordinal(), this);
}
public static Country getById(int id) {
return idToCountry.get(id);
}
}
You can try this one too. Simple as it shows.
enum Country {
DE(1), US(2), UK(3);
public int id;
Country(int id) {
this.id = id;
}
public static Country getCountry(int id) {
Country[] c = new Country[Country.values().length];
c = Country.values();
return c[id];
}
}
Thanks a lot.
I am newbie to java, I have a scenario, where i need to list the organisation types from the table:
Requirement : Just listing, no add or removing the elements,
As i understand the difference between set and list:
Set:
Set is Unique collection of Objects.
Set is Un-ordered collection of Objects.
List:
List is non-unique collection of Objects.
List is ordered collection of Objects.
In my table i am having columns like:
id name is_active
1 Lab 1
2 Pharmacy 2
3 Hospital 3
Maximum 10 rows
**Controller**:
List<OrgType> orgTypeList = organizationService.getAllOrgTypes(true);
OrgTypeResponse response = new OrgTypeResponse();
List<EntityDetail> orgTypeDetailList = new ArrayList<>();
EntityDetail orgTypeDetail;
for(OrgType orgType : orgTypeList) {
orgTypeDetail = new EntityDetail();
orgTypeDetail.setId(orgType.getId());
orgTypeDetail.setName(orgType.getName());
orgTypeDetailList.add(orgTypeDetail);
}
response.setStatus(ResponseStatusCode.SUCCESS);
response.setTotalOrgTypes((long)orgTypeDetailList.size());
response.setOrgTypes(orgTypeDetailList);
return response;
**Service** Implementaion:
List<OrgType> orgTypeList = orgTypeRepository.findByActive(active);
return orgTypeList;
This is my EntityDetail class:
public class EntityDetail {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My question here is, can i use the Set instead of List
If Set is used, can i use TreeSet, because i need to show in the asc order of id
Or Leave the code, as it is
i just want the clarification,
Thanks
You can use any of them but things to be kept in consideration:
Set although provides unique data, but that also has a cost.
In case, you are sure that table has unique names of organizations then you should opt for list.
It seems like you are using Spring with JPA, if that is the case, then you can use SORT interface(org.springframework.data.domain.Sort) to get sorted data.
My question here is, can i use the Set instead of List
Yes, without problem, just implement methods equals and hashCode.
If Set is used, can i use TreeSet, because i need to show in the asc order of id
You can if class EntityDetail implements interface Comparable<EntityDetail>. This is necessary because TreeSet must know what is the natural order of the various EntityDetail objects.
For more details please see Oracle docs on object ordering and Javadoc for Comparable
yes u can use SET instead of List in this scenario because SET will ensure that duplicate entries are eliminated. But making use of SET make sure that you have overridden "equals" and "hashcode" appropriately.
This is how you need to override equals and hashcode methods and for sorting purpose you need to implement Comparable and implement compareTo method as follows:
class EntityDetail implements Comparable<EntityDetail>{
#Override
public int hashcode(){
int result = 17;
result = 31 * result + name.hashCode();
result = 31 * result + id;
return result;
}
#Override
public boolean equals(Object o){
if (o == this) return true;
if (!(o instanceof EntityDetail)) {
return false;
}
EntityDetail ed = (EntityDetail) o;
return ed.name.equals(name) &&
ed.id == id ;
}
#Override
public int compareTo(EntityDetail ed) {
int compareId = ((EntityDetail) ed).getId();
//ascending order
return this.id - compareId;
//descending order
//return compareId - this.id;
}
}
You can use List if you can make sure in your code that the details are added in it in the order that you want. If you are not sure of the order in which you add then you can use the Collections.sort method. For this you will also want to make your OrgType implement the Comparable interface to provide a strategy to order the OrgType objects. In your case it is by id.
If you use TreeSet, the sorting is done automatically whenever you insert into the set thereby eliminating the use of Collections.sortbut you will still have to provide an ordering strategy.
Have a look at this
There are costs of using a Set because it maintains unique elements but because you have a maximum of 10 rows that won't be a problem.
I was looking arround for this, but I couldn´t find a right answer.
I´m trying to get any field from an entity based on it´s column name.
Like a generic get, in which it receives a String columnName and returns an Object field that represents the Class field which is mapped by that columnName by Hibernate.
For example
#Table(name="ENTITY_EXAMPLE")
public class EntityExample{
#Column(name="COL_NAME")
private String name;
#Column(name="COL_SURNAME")
private String surname;
public EntityExample(String name, String surname){
this.name=name;
this.surname=surname;
}
//getters and setters
public Object getField(String columnName){
Object field=//some way to map the columnName with the field;
return field;
}
}
public main(String[] args){
EntityExample example=new EntityExample("John", "Doe");
String exampleName=(String) example.getField("COL_NAME");
String exampleSurname=(String) example.getField("COL_SURNAME");
System.out.println("NAME: "+ exampleName+ ", SURNAME: "+exampleSurname);
}
and that main when runs should print:
NAME: John, SURNAME: Doe
The way I´m doing now is with ifs that checks if the parameter is equal to each annotated column and inside returns the field if equals, but it should be a propper way to do that.
way I'm doing now:
public Object getField(String columnName){
if(columnName.equals("COL_NAME")){
return name;
}
if(columnName.equals("COL_SURNAME")){
return surname;
}
}
Thanks in advance.
As far as I know, the way you're doing it, is the only way. With the exception being, to use a switch statement instead of multiple if statements:
switch (columnName) {
case 1: columnName = "COL_NAME";
return this.name;
break;
case 2: columnName= "COL_SURNAME";
return this.surname;
break;
default: columnName= "COL_BLAHBLAH";
return this.blahblhblah;
break;
}
The only possible way to what you want (and this is a big stretch), is if you did something along the lines of:
public Object getField(String columnName){
Object field= (Object)columnName;
return field;
}
Note: You would need to pass in the object name (aka: name,surname) and not the column name.
But I honestly don't think this is going to work. Regardless, you would need to cast a string as an object in such a way that the compiler would know how to handle the casting properly (don't think it's possible).
Good luck either way. Maybe someone else will have more ideas.
You can , of course, use reflection to go through the fields of your entity, looking for which one has the #Column annotation with the corresponding name. However, many people will tell you that Reflection is slow.
What we did to accomplish this was to create a set of public static integer constants for each of the columns. Thus in your entity you would have the following:
public static final int COL_NAME = 1;
public static final int COL_SURNAME = 2;
and in the Entity you also have a getFieldValue method as follows:
public Object getFieldValue(int fieldNo) {
switch (fieldNo) {
case COL_NAME:
return this.name;
case COL_SURNAME:
return this.surname;
default:
throw IllegalArgumentException("Invalid Field Number: " + fieldNo);
}
}
and you would use these to get fields values as follows:
String name = entityExample.getFieldValue(EntityExample.COL_NAME);
Of course, the problem you have now is maintaining both the list of constants and the switch cases when columns are added/removed/renamed. We get round this by using a script to generate both the constants and the method. You could also use Java's annotation processing to generate the same code.
In Java I can do something like this:
enum Country {
IRELAND("Europe"),
FRANCE("Europe"),
NIGERIA("Africa"),
THAILAND("Asia");
private String continent;
Country(String continent) {
this.continent = continent;
}
public String getContinent() {
return continent;
}
}
which allows me to do something like:
Country country1 = getCountryFromSomewhere();
Country country2 = Country.FRANCE;
System.out.print("country1 is in " + country1.getContinent());
System.out.print("country2 is in " + country2.getContinent());
Is it possible to do the same thing in VB.NET i.e. add the continent attribute to the country enum?
(Apologies for using C# throughout - I believe the concepts are more about .NET than the language you happen to use; hopefully you're better at reading C# than I am at writing VB.)
Not directly - enums in .NET are just integer types with names for some of the values.
The closest you can come in .NET is to create a type with a fixed set of values. For example, in your case:
public sealed class Country
{
public static readonly Country Ireland = new Country("Europe");
public static readonly Country France = new Country("Europe");
public static readonly Country Nigeria = new Country("Africa");
public static readonly Country Thailand = new Country("Asia");
private readonly string continent;
public string Continent { get { return continent; } }
private Country(string continent)
{
this.continent = continent;
}
}
(I assume the VB.NET would be very similar.)
Note that this doesn't let you switch on the enum values.
If you want polymorphism, you can create nested subclasses which can still call the private constructor, which prevents any other subclasses being created.
One alternative to this is to use attributes on normal enums:
[AttributeUsageAttribute(AttributeTargets.Field)]
public class ContinentAttribute : Attribute
{
// etc
}
public enum Country
{
[Continent("Europe")] Ireland = 1,
[Continent("Europe")] France = 2,
...
}
You'd then need to use reflection to get at the ContinentAttribute and retrieve the string.
Note that here there isn't really a fixed set of values - you could write:
Country country = (Country) 15;
At that point you can't get the continent for it, and if you pass it to any methods which expect it to be a real country, you've got problems. That isn't the case with the earlier solution, where you really are restricted to those few values (and null).
Here is the code:
Imports System.ComponentModel
Imports System.Reflection
Public Enum enumOrderStatus
<Description("None")>
None
<Description("Sent")>
Sent
<Description("Accepted")>
Accepted
<Description("Cancelled")>
Cancelled
<Description("Declined")>
Declined
End Enum
Public Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim aattr() As DescriptionAttribute = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If aattr.Length > 0 Then
Return aattr(0).Description
Else
Return EnumConstant.ToString()
End If
End Function
I used this solution instead:
Declare enum:
Private Enum Country
IRELAND
FRANCE
THAILAND
End Enum
Declare and initialise Dictionary (aka a map):
Dim countryContinentMap As IDictionary(Of Country, String) = New Dictionary(Of Country, String)
countryContinentMap.add(Country.IRELAND, "Europe")
countryContinentMap.add(Country.FRANCE, "Europe")
countryContinentMap.add(Country.THAILAND, "Asia")
which allows me to get the continent like this:
Dim franceContinent As String = countryContinentMap(Country.FRANCE)
Here is how I solved this in my application. Still looking for something even easier.
What do you think about it?
Public Sub Init()
Dim values() As Integer = CType([Enum].GetValues(GetType(MyEnum)), Integer())
For i As Integer = 0 To values.Count - 1
Me.contextMenuInGUI.Items.Add(Me.GetEnumDescription(i))
Next
End Sub
Private Function GetEnumDescription(ByVal i As Integer) As String
Select Case i
Case MyEnum.Comment
Return "Description for Comment"
Case MyEnum.SomeEnumValueInCamelCase
Return "Value without camel case (€)(%)(#)"
End Select
Return "Add a case in Class:GetEnumDescription"
End Function
Create an extension method for your Enum
Usage example:
dim description = TableTag.Important.GetDescription()
Definition example:
Imports System.ComponentModel
Imports System.Reflection
Imports System.Runtime.CompilerServices
Namespace Foo
Public Enum TableTag
<Description("Identifies tables that should be availible for writing as table or view to the model database")>
Important
<Description("Example for a table group that helps to select disctinct tables")>
CustomGroup
End Enum
Public Module TableTagExtensions
<Extension>
Public Function GetDescription(enumValue As TableTag) As String
Dim fieldInfo As FieldInfo = enumValue.GetType().GetField(enumValue.ToString())
Dim attributes = DirectCast(fieldInfo.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If attributes.Length > 0 Then
Return attributes(0).Description
Else
Return enumValue.ToString()
End If
End Function
End Module
End Namespace