"ChoiceField" (or: a better JPA Enum) in Play? - java

I'm very much used to the Django 'choices' option for a model field:
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
That allows, for a model field, to easily match a database value with a (representation) string. And works with whatever sql type is chosen (Char, Int...)
In JPA that's not possible; you can make an Enum: but only use its ordinal or string. I find that overly limiting and complicated.
Is there in Play! something similar to the Django choices, in particular to be used with the CRUD?
Or at least a pattern for the CRUD than just declare a simple String or int for the model field?
Related:
How to use enums with JPA
Map enum in JPA with fixed values?
play! framework ENUM and Groovy problem

I don't understand your problem with Enum, you can map enum with JPA like this
#Enumerated(EnumType.STRING)
public GenderChoice genderChoice;
an use the enum in your code. Is this not enough ?

Based on #SebCesbron comment this is the kind of pattern I'm using now...
#Required
#Enumerated(EnumType.STRING)
public MyEnumType myenum;
public enum MyEnumType {
ENUMONE ("label one", 1),
ENUMTWO ("label two", 2);
String label;
Int value;
CastType(String label, Int value) {
this.value = value;
this.label = label;
}
#Override
public String toString() {
return this.label;
}
public Int getValue() {
return this.value;
}
public String getLabel()
{
return label;
}
}
Using EnumType.STRING the database will contain the enum item name - overriding toString does not affect that, because JPA uses name() which is final.
So wrt to my precise usecase and question:
#Required
#Enumerated(EnumType.STRING)
public GenderEnum myenum;
public enum GenderEnum {
M ("Male"),
F ("Female");
String label;
CastType(String label) {
this.value = value;
}
#Override
public String toString() {
return this.label;
}
}

Related

Adding properties to enums in JOOQ

We have been moving away from hibernate to jooq for months now. Pattern we would often do in Hibernate would be writing a custom enum like this...
public enum HBMCapacityType {
Accepting("Accepting until end of day", true),
Limited("Limited until end of day", true),
AtCapacity("At Capacity until further notice",false);
private final String description;
private final boolean userOverridable;
HBMCapacityType(String description, boolean userOverridable) {
this.description = description;
this.userOverridable = userOverridable;
}
public String getDescription() {
return this.description;
}
public boolean isUserOverridable() {
return this.userOverridable;
}
}
Then we are able to use this a column in our hibernate domain objects
#Enumerated(EnumType.STRING)
#Type(type = "pg_enum")
#Column(name = "capacity_type", nullable = false, columnDefinition = "capacity_type")
private HBMCapacityType capacityType;
This is convenient as we can call capacityType.getDescription() when rendering to to the GUI. We have this nice 1:1 mapping of enum types to a description we can use in the GUI. Sometimes we have multiple getters like this say one for description and a few booleans which characterize behaviour associated with that enum. The property isUserOverrideable would be an example of this.
Now looking at JOOQ: since this was defined as an enum in postgres JOOQ automatically generates a type like this...
/**
* This class is generated by jOOQ.
*/
#SuppressWarnings({ "all", "unchecked", "rawtypes" })
public enum CapacityType implements EnumType {
Accepting("Accepting"),
Limited("Limited"),
AtCapacity("AtCapacity");
private final String literal;
private CapacityType(String literal) {
this.literal = literal;
}
#Override
public Catalog getCatalog() {
return getSchema().getCatalog();
}
#Override
public Schema getSchema() {
return Wastecoordinator.WASTECOORDINATOR;
}
#Override
public String getName() {
return "capacity_type";
}
#Override
public String getLiteral() {
return literal;
}
/**
* Lookup a value of this EnumType by its literal
*/
public static CapacityType lookupLiteral(String literal) {
return EnumType.lookupLiteral(CapacityType.class, literal);
}
}
I guess your question is about how to add custom properties to jOOQ generated enums? There are multiple approaches:
Using a custom code section
One way to achieve the same functionality is by adding a custom code section as described here. You'll add a generateEnumClassFooter() method to your own JavaGenerator subclass, and generate the necessary code there. Unlike in your original code, you can't modify the properties of enum values, so you'll just have to switch over this, instead.
Move the logic outside of the enum
You can always just write a static utility of the form
public static boolean isUserOverridable(HBMCapacityType type) {
return switch (type) { ... };
}
It's less object oriented, but maybe, that doesn't matter all that much?
Keep 2 separate enums
You don't have to use jOOQ's enum. You can translate it to your own hand-written enum and attach an EnumConverter to your generated code to map between the jOOQ enum type and yours.

Is it bad practice to return Enums in Java?

Lets say I have a class to model an item in a game like so:
public class Item {
private final EnumItem type;
public Item(EnumItem type) {
this.type = type;
}
public Item(String name) {
this.type = EnumItem.fromName(name);
}
}
public enum EnumItem {
MACHINE_GUN("machine_gun"),
SWORD("sword"),
BAT("bat"),
DEFAULT("default");
private final String name;
public EnumItem(name) {
this.name = name;
}
public String getName() { return name; }
public static EnumItem fromName(String name) {
for(EnumItem i: EnumItem.values()) {
if(i.name.equals(name)) {
return i;
} else {
return EnumItem.DEFAULT;
}
}
}
}
Assume that .equals() and .hashCode() of Item are overridden correctly to compare the internal Enum.
Now I want a way to distinguish these items with a getter in Item: should I return an Enum or the String name? Is it good practice to return an Enum in general? Or is there a better way to distinguish these Items? Because returning the enum kind of looks like exposing the rep to me and I don't want my colleagues to use EnumItem directly to compare Items.
The approaches I thought of are the following:
string getName() to do something like item1.getName().equals("machine_gun");
EnumItem getEnum() to do item1.getEnum().equals(EnumItem.MACHINE_GUN);
item1.equals(new Item("machine_gun"));
static name(String name) { new Item(name) } to do item1.equals(Item.name("machine_gun"));
I don't know what should I do, I'd appreciate some insight from experienced programmers.
I know they look like they would from context, but in my use case these items have no special functionality that would justify extending from the base Item class.
Is this good practice? Sure, you're using aggregation since Item doesn't depend on EnumItem, which is fine. That being said, could it be done better? Sure. Is the alternative I provide the only solution? No.
Alternative
If you want this to be extensible, consider using an interface to represent an item. Then allow the interface to extend this interface to provide some standard types. Alternatively you could use composition or aggregation to define a type inside EnumItem that implements the Item interface to ensure that equals/hashcode for the Item are always override and adhere to some contract.
interface Item {
String key();
}
enum EnumItem implement Item {
private final String key;
EnumItem(String key) {
this.key = key;
}
#Override
public String key() {
return key;
}
}
class AbstractItem implements Item {
// constructor, override name()
}
Item item = EnumItem.FOO_BAR;
Item item2 = new AbstractItem("FooBar");
Item item3 = () -> "FooBar";

Table as an enum. Is it possible?

How can I a map static table (which is not expandable) to an enum in Java and bind this table to another one?
For example, I have simple calculator web app (spring web MVC + hibernate) with one table (results of user's calculations) which has the following fields: id (PK), leftOperand, operation, rightOperand, result.
I would like to create a new static table (I mean for basic arithmetic operations like PLUS, MINUS, DIVIDE, MULTIPLY) with 2 fields: id(PK) and operation, and map this table to enum in Java.
So how can I bind these two tables (using operation field)?
Some pseudo-code is greatly appreciated.
Note that I do not need to create a hibernate entity for the static table. Just enum.
As with classes, you can add properties to enums like so:
public enum MyEnum {
PLUS(1, "something"),
MINUS(2, "something");
private final int id;
private final String string;
private MyEnum(int id, String string){
this.id = id;
this.string = string;
}
public int getId(){
return id;
}
public String getString(){
return string;
}
}
Assuming the 'opreration' field matches the enum's name, you can do the following:
MyEnum enumValue = MyEnum.valueOf(map.get("operation"));
I think you don't need static table for operation values. Just change operation field type to enum and use #Enumerated.
enum Operation {
PLUS, MINUS;
}
#Entity
public class Calculation {
private String leftOperand;
#Enumerated(EnumType.STRING)
private Operation operation;
private String rightOperand;
}

get() methods in Java enum type

I have an enum type (say for arguments sake CarModel), used throughout an application (numerous classes).
public enum CarModel {
DIABLO,
P911,
DB7;
}
I have various methods that use this CarModel enum type in different ways, and each has a switch statement to set some String variable depending on the enum type, before going on to do other stuff. (e.g. set the Manufacturer of some model, or set the country of origin etc. These results are static at runtime)
The issue is, if I want to add a new model to the enum, I'd need to go to each method, and extend/modify the switch statement to handle its existence. This could easily lead to human error, and/or code duplication (if various methods use the same switch statements).
Rather than using switch statements all-over, I would like to have static methods, that could be edited in a single location, and would allow for behaviour similar to the following:
String country = CarModel.DIABLO.getCountry() // returns Italy
String manufacturer = CarModel.P911.getManufacturer() // returns Porsche
Is this possible with an enum, (and is an enum even the 'correct' way to do this?
You can do something like this.
public enum CarModel {
DIABLO("Lamborghini", "Italy"),
P911("Porsche", "Germany");
private String manufacturer;
private String country;
private CarModel(String manufacturer, String country) {
this.manufacturer = manufacturer;
this.country = country;
}
public String getManufacturer() {
return manufacturer;
}
public String getCountry() {
return country;
}
}
Yes, absolutely. Enums can have their own methods, and those methods can be value-specific. It looks like this:
enum CarModel {
P911 {
public String getManufacturer() { return "Porsche"; }
},
DB7 {
public String getManufacturer() { return "Aston Martin"; }
},
...
public abstract String getManufacturer();
}
You can add more methods, of course.
If you're going to use enums, I would suggest an abstract method declared in the enum, and then a provided implementation for each enum instance.
That way you don't have switch statements everywhere (from which you can easily omit cases) and you have a more reliable and OO-styled polymorphic approach.
abstract public int getEngineSize();
DIABLO {
public int getEngineSize() {
return 6.3; // whatever it really is...
}
}
See here for more examples/discussions etc.
I would suggest adding this information directly into your enum.
Like this:
public enum CarModel {
DIABLO("Lambo"),
P911 ("Porsche");
private String manufacturer;
private CarModel(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getManufacturer() {
return manufacturer;
}
}
and in the class you'd only have to use the getManufacturer method
Moreover enums can implement an interface. You can add some get() methods like getMaxSpeed() or getWeight().
Interface can look like
interface car{
public int getMaxSpeed();
public int getWeight();
}
Yes, this is quite easy to do:
public enum CarModel {
DIABLO("rod"),
P911("jane"),
DB7("freddy");
private String name;
CarModel(String name){
this.name = name;
}
public String getName() {
return name;
}
}
Haha, I recommend you to use "Factory" Design Pattern.
you can make a CarFactory(), to produce new model car.
http://en.wikipedia.org/wiki/Factory_method_pattern

How should I store this data in a Java enum?

What's the best way to store this data in a Java enum?
<select>
<option></option>
<option>Recommend eDelivery</option>
<option>Require eDelivery</option>
<option>Require eDelivery unless justification provided</option>
</select>
I'm new to java and have tried things like
public enum Paperless {
"None" = null,
"Recommend eDelivery" = "Recommend eDelivery",
"Require eDelivery" = "Require eDelivery",
"Require eDelivery unless justification provided" = "Require eDelivery w/out justification"
}
But this doesn't work. I'm considering the possibility of storing a text value that summarizes the option that the user sees on this web page.
Take a look at the enum tutorial, more specifically the Planet example. You can do the same, e.g.
public enum Paperless{
NONE( null ),
RECOMMENDED_DELIVERY( "Recommended delivery" ),
...//put here the other values
REQUIRED_DELIVERY( "Required delivery" );
private String name;
Paperless( String name ){
this.name = name;
}
public String getName(){
return this.name;
}
}
Something like this can work for your case:
public enum PaperLess {
NONE("none"),
RECOMMEND("Recommend eDelivery"),
REQUIRE("Require eDelivery"),
REQUIRE_JUSTIFIED("Require eDelivery unless justification provided");
private String value;
private PaperLess(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
You can't assign strings to enum values in Java in the way that you are trying.
The way to do it would be:
public enum Paperless {
None(null),
RecommendedDelivery("Recommended Delivery"),
RequireEDelivery("Require eDelivery"),
RequireEDeliveryUnlessJustification("Require eDelivery unless justification provided");
private final String value;
Paperless(String value) {
this.value = value;
}
private String enumValue() { return value; }
public static void main(String[] args) {
for (Paperless p : Paperless.values())
System.out.println("Enum:" + p + "; Value:" + p.enumValue());
}
}
You can't have spaces in the names of members and you can't assign enum values, they are objects, not constants.
The name of the enum must be an identifier (e.g. one-word, not a string)
public enum Paperless {
None,
RecommendEDelivery,
...
}
You can associate string values with them if you want (although you can get the default too that equals to the identifier name, usign the name() method) by associating a String member with the enum type and providing a custom constructor.
public enum Paperless {
None("None"),
RecommendEDelivery("Recommend eDelivery"),
...;
private String myValue;
private Paperless(String name) {myValue=name;)
}
To access that associated string, you need to provide a public accessor method as well.
Java enums aren't constructed in that way. Check out
Java Tutorials: Enum Types
Java - Convert String to enum: #2
Yours might look something like this:
public enum Paperless {
NONE(""),
RECOMMEND("Recommend eDelivery"),
REQUIRE("Require eDelivery"),
REQUIRE_UNLESS("Require eDelivery unless justification provided");
private String text;
Paperless(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
}
public enum Paperless {
NONE("None"),
RECOMMEND("Recommend eDelivery"),
REQUIRE("Require eDelivery"),
REQUIRE_UNLESS("Require eDelivery unless justification provided"),;
private String value;
private Paperless(String value){
this.value=value;
}
public String getValue(){
return this.value;
}
}

Categories

Resources