I have am working a project which uses enums as columns to a database
public enum MyEnum{
a1(1,"act1"),
a2(2,"act2"),
a3(3,"act3"),
b1(4,"bob1"),
c1(10,"cake1"),
c2(11,"cake2"),
}
#Entity
public class MyEntity{
..
private Integer myEnumIntValue;
...
}
(NOTE: myEnumIntValue is not mapped as enum in the entity)
I have a hibernate query to query MyEntity. The query orders the result based on the int value of the enum
createCriteria(MyEntity.class).addOrder("myEnumIntValue");
This was all good until the enum was changed to
Public enum MyEnum{
a1(1,"act1"),
a2(2,"act2"),
a3(3,"act3"),
a4(7,"act4"),
b1(4,"bob1"),
c1(10,"cake1"),
c2(11,"cake2")
}
It now becomes a problem because the order of the result is i am getting is (a1,a2,a3,**b1,a4**,c1,c2) but sorting of the Entities should be (a1,a2,a3,**a4,b1**,c1,c2)
What is strategy can i use to resolve this?
If you don't want / can't manipulate the query to order the enums while fetching, remember that all enums implement the Comparable interface.
You can override the compareTo() method and do the sorting direclty from your java code.
To get specific:
public enum MyEnum{
a1(1,"act1"),
a2(2,"act2"),
a3(3,"act3"),
b1(4,"bob1"),
c1(10,"cake1"),
c2(11,"cake2");
private number;
private name;
public MyEnum(Integer number, String name){
this.number = number;
this.name = name;
}
//getters and setters
#Override
public int compareTo(MyEnum otherEnum){
return this.getName().compareTo(otherEnum.getName());
}
}
As soon as you have your List<MyEnum> myList fetched from the DB, you only have to call:
Collections.sort(myList);
to get your enums sorted via name.
Related
public enum CameraType {
CAMERA(false, false, "External lens ", ""),
CameraType{
boolean collector,
boolean hidden,
String description
) {
this.collector = collector;
this.granular = hidden;
this.description = description;
} // end ctor
public void setHide(boolean hidden) {
this.hide = hidden;
}
} // end enum
I have few Instance of CameraType.
I have a setter for "hidden" property which on certain condition is set to true or false.
Now I serialize CameraType with few other fields inside SecurityEntity.
```
#Entity
#Table
public class Security {
Few more fields...
#Enumerated(EnumType.STRING)
#Column(nullable = false)
private CameraType cameraType
And other fields...
}
```
When I deserialize the value of "hidden" field is always false. If I understand correctly, during deserialization ctor is called and default is assigned.
Is there a way I can retain the value of "hidden" field(true or false) after deserialization per instance of CameraType.
I am using Postgres DB 10.
enter code here
Please Please help. I am out of clues.
By definition, enums are immutable elements of a fixed set. Because of this, to represent an enum value you just need its name. That's exactly what JPA does during serialization/deserialization.
You are trying to violate the rules. While Java allows you to treat enums almost as plain objects, JPA treats them according to what they are supposed to be. That's why your code is not working.
You can either:
make CameraType into a class and serialize it as such, or
split CameraType into two parts, for example enum CameraType (immutable) and class CameraConfig (with all the mutable fields)
The former answer is correct : enums must be immutable and dividing parts into immutable and mutable data is a good choice.
One addition here: using the enum values for database storage is often not a good choice, because when another developer decides to refactor the enum names and you are after this reading old entries from database, you got a crashing application...
So I would suggest to use javax.persistence.AttributeConverter to deserialize/serialize an enum in a specific and rename save way.
Here a very simple example with an enum called MyDefinition:
enum MyDefinition{
ONE("def_one"),
TWO"def_two"),
THREE("def_three"),
;
private String id;
private MyDefinition(String id){
this.id=id;
}
public String getId(){
return id;
}
public static MyDefinition fromId(String id) {
for (MyDefinition definition : MyDefinition.values()) {
if (definition.id.equals(id)) {
return definition;
}
}
return null;
}
}
Here the converter:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class MyDefinitionAttributeConverter implements AttributeConverter<MyDefinition, String> {
#Override
public String convertToDatabaseColumn(MyDefinition attribute) {
if (attribute == null){
return null;}
}
return attribute.getId();
}
#Override
public MyDefinition convertToEntityAttribute(String dbData) {
return MyDefinition.fromId(dbData);
}
So we can use the ids for database. A renaming of the enum names will no longer lead to a crashing application when reading old data.
There's a table in mysql sakila.film which has field rating of type enum('G','PG','PG-13','R','NC-17').
I'm trying to map this in JPA entity using type enum and #Enumerated(EnumType.STRING) but not able to, as java enum doesn't allow hyphen.
Can't alter or update table also as it has lots of data and gives error -"Data truncated for column 'rating'"
How to proceed?
This sort of thing is what JPA Attribute Converters are designed to help achieve.
First you'll want to map your Java enum elements to the strings found in the database, and provide a lookup method for converting a string to an element:
public enum Rating {
G("G"), // the string arguments should exactly match what's in the database.
PG("PG"),
PG13("PG-13"),
R("R"),
NC17("NC-17");
private static final Map<String, Rating> LOOKUP = Arrays.stream(values())
.collect(Collectors.toMap(Rating::getRating, Function.identity()));
private final String rating;
Rating(final String rating) {
this.rating = rating;
}
public String getRating() {
return rating;
}
public Rating fromString(final String rating) {
// You may want to include handling for the case where the given string
// doesn't map to anything - implementation is up to you.
return LOOKUP.get(rating);
}
}
Next you're going to want a class that implements javax.persistence.AttributeConverter:
public static class RatingConverter implements AttributeConverter<Rating, String> {
#Override
public String convertToDatabaseColumn(final Rating attribute) {
return attribute.getRating();
}
#Override
public Rating convertToEntityAttribute(final String dbData) {
return Rating.fromString(dbData);
}
}
From here you need to decide whether this converter should always be applied (it sounds like it probably should be), or not.
If you want it to always be used with no further configuration from you, then annotate your converter class with #javax.persistence.Converter(autoApply = true).
If you want to choose when you use the converter, then you will need to add the annotation #javax.persistence.Convert(converter = RatingConverter.class) to the Rating attribute of each JPA entity that needs it.
Personally I usually nest the converters as a static class inside the class that they convert, but you don't have to if you'd rather keep them separate.
Can I use Hibernate like this?
public class Employee{
private String name;
private String age;
//public getters and setters
}
public class EmployeeDao{
public List<Employee> getByFilter(Employee filter){
//what whould I write here?
}
}
public class Test{
public static void main(String[] args){
Employee employeeFilter = new Employee();
employeeFilter.setAge(15);
List<Employee> employeesOf15 = new EmployeeDao().getByFilter(employeeFilter);
}
}
If yes then how?
Yes, in your getByFilter() method, you could write a straightforward HQL or native SQL query to find all the employees who have an age equal to the filter's age, or use Hibernate's Criteria Queries like for example:
return getSession().createCriteria(Employee.class).add(Restrictions.eq("age", filter.getAge())).list();
It might be a good idea to add another parameter to your all-purpose getByFilter() method so you can specify filtering by any field (name, age, and so on), and then act accordingly.
The ratings column in the (MySQL) database has type ENUM('G','PG','PG-13','R','NC-17') (notice the dashes). This:
#Entity
#Table(name = "movies")
public class Movie implements Serializable {
#Enumerated(EnumType.STRING)
private Rating rating;
public static enum Rating {
G("G"), NC_17("NC-17"), R("R"), PG("PG"), PG_13(
"PG-13");
private String label;
private Rating(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
#Override
public String toString() { // added but still
return label;
}
}
}
causes a Data truncated for column 'rating' at row 1 exception for NC_17("NC-17") (and PG_13("PG-13"), works for the other values) - nevermind the exception should read Data rejected for enum ENUM('G','PG','PG-13','R','NC-17') for column 'rating' at row 1.
This happens because it tries to insert the string NC_17 - I thought that adding toString() as above would fix this but apparently calls rating.name() to produce the string to insert (instead of rating.toString()).
Are there any workarounds or do I have to change the type of ratings to ENUM('G','PG','PG_13','R','NC_17') in the DB ?
Edit: from the comments - what I ask is why JPA is not calling toString() for an #Enumerated(EnumType.STRING) entity field on update ? So I can't put in the db arbitrary chars ? It seemed like an oversight to me but apparently it is not - so off for mysql
JPA uses the name() of the enum, and not what is returned by toString() which could be overridden and return something different every time it's called. name() doesn't have this problem, because it's final and is thus guaranteed to always return the same thing.
So, your simplest option is to rename the values in your database so as they match with the enum names.
If that's really not an option and you're using Hibernate, then you could define your own type, that would be used to transform the enum to a String and vice-versa by Hibernate.
I wonder if anyone can advise me here; I have an application which has classes like this:
Code:
public class Order implements Serializable {
private int orderNo;
private int customerNo;
private date orderDate;
public int getOrderNo () {
return orderNo;
}
public int getCustomerNo () {
return customerNo;
}
public date getOrderDate () {
return orderDate;
}
// And so on with set methods.
}
public class OrderLine implements Serializable {
private int orderNo;
private int lineNo;
private int qty;
private int prodID;
// Get and set methods for each of the above.
public int getOrderNo () {
return orderNo;
}
public int getLineNo () {
return lineNo;
}
public int getQty () {
return qty;
}
public int prodID () {
return prodID;
}
// And so on with set methods.
}
This translates directly into relational table:
Order: orderno, customerNo, orderDate
OrderLine: orderno, lineNo, qty, prodID
So each class directly translates into a database table with get and set pairs for each attribute.
Now what I want to know is, if in an Java web application, should the classes be as they are above or more like this where the gets return objects:
Code:
public class Order implements Serializable {
private int orderNo;
private Customer;
private date orderDate;
private ArrayList<OrderLine>lineItems;
public int getOrderNo () {
return orderNo;
}
public Customer getCustomer () {
return Customer;
}
public date getOrderDate () {
return orderDate;
}
public ArrayList<OrderLine> getOrderLines () {
return lineItems;
}
public OrderLine[] getOrderLines () {
return lineItems;
}
// And so on with set methods.
}
public class OrderLine implements Serializable {
private int orderNo;
private int lineNo;
private int qty;
private Product;
// Get and set methods for each of the above.
public int getOrderNo () {
return orderNo;
}
public int getLineNo () {
return lineNo;
}
public int getQty () {
return qty;
}
public int getProduct () {
return Product;
}
}
Which is the better approach? Or does it really matter which approach is taken as long as the classes processing the data do so correctly and the system operates efficiently?
Thanks
Mr Morgan
There is absolutely no reason to use the first form - it will be much harder to work with in the Java code, and there will be a lot of bugs resulting from that.
All decent O/R mappers (Hibernate being the most popular one) will translate from the DB schema into proper OO object graphs with no problems.
However, the downside is that having all this done for you can result in very bad performance when you leave everything to the OR mapper and it fetches too much or too little data from the DB, so you have to pay some attention to caching and whether your collections are lazy or eager loading.
If your business objects are as clean as this, I'd suggest maybe looking at an ORM mapper such as Hibernate or ActiveObjects.
If you'd rather build your own data access layer, I'd suggest making these classes as light-weight as possible. For instance in Order, I would have a CustomerID integer field which would represent the foreign key, instead of having a getCustomer() returning a Customer object in the Order business class.
Fetch methods such as getCustomer() would probably be better placed in a different class such as CustomerDAO etc which contains your data access functionality.
It won't really matter from an efficiency point of view which approach you go with, but it's always good practice to have loosely-coupled objects.
You should look at some Object Relational Mapping (ORM) mapping tool, like Hibernate or TopLink, or perhaps a simpler query mapping tool like iBatis.
I think you'd end up with something more like your second suggestion if you go down the ORM route.
The common way to do it is by using an Object-Relational Mapping library (ORM). The common ones are:
Hibernate
EclipseLink (based on the open source part of TopLink)
OpenJPA
All these are open source libraries and implement the JPA (Java Persistence Architecture) standard.
I would also encourage you to look at an Object-Relational Mapping tool like Hibernate, or a simpler SQL mapping tool like iBatis. (I've had very good experience with iBatis.)
On the question of modelling the master-detail relationship between an Order and its OrderLines, at the Java object level they certainly are part of the same conceptual hierarchy, so I would choose to model them as an Order object that contains a List. That way I could pass complete Orders around my code and never worry about losing parts of them.
If you're using an SQL mapper like iBatis, you'd set up an Order table and an OrderLine table in your database, and give iBatis an XML map that tells it how to assemble your compound Java Order/OrderLine object hierarchy by joining the two SQL tables together. ORMs operate in reverse, generating the SQL tables based on your Java data model.