Adding properties to enums in JOOQ - java

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.

Related

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";

Adding subcategories to a java Enum

Suppose I have a simple Java Enum:
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
}
I would now like to introduce a "category" to this enum, for example to make the distinction between liquid items and solid items. I found two ways of doing this within the enum class, see below. However, both suffer from the same anti-pattern: if the amount of categories or amount of items ever increases/decreases (imagine 100 item types with 10 categories!), I've got a lot of updating to do. What patterns can I use to design this enum as cleanly and re-usable as possible?
First approach: Add additional properties to the enum
public Enum itemType
{
FRUITS("fru",false),
VEGETABLES("veg",false),
LIQUOURS("liq",true),
SODAS("sod",true);
private String dbCode;
private boolean liquid;
public ItemType(String dbCode, boolean liquid){
this.dbCode = dbCode;
this.liquid = liquid;
}
public String getDbCode(){
return this.dbCode;
}
public boolean isLiquid(){
return this.liquid;
}
}
Second approach: Use static methods to ask about subcategories
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
public static boolean isLiquid(ItemType type){
switch(t){
case SODA:
case LIQOURS: return true;
default: return false;
}
}
How about using an EnumSet for that?
public enum ItemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
public static final EnumSet<ItemType> LIQUIDS = EnumSet.of(LIQUOURS, SODAS);
// ...
}
Then you can use ItemType.LIQUIDS.contains(someItemType) to check if someItemType is a "liquid".
I would do something like:
enum Category {
LIQUID, SOLID;
}
enum ItemType {
FRUITS("fru", SOLID),
VEGETABLES("veg", SOLID),
LIQUOURS("liq", LIQUID),
SODAS("sod", LIQUID);
private String dbCode;
private Category category;
public ItemType(String dbCode, Category category){
this.dbCode = dbCode;
this.category = category;
}
/* getters / setters */
}
That would allow, for example, that you can add new products and categories (e.g. BUTANE("but", GAS)) without having to modify the existing code (as would happen in Approach 2).
On the other hand, if the number of categories and items is long and changing, I would consider to use a SQL database.
Since you are modeling something that has no logic that can be encoded in an algorithmic way (i.e. there's no algorithm that would figure out that "sod" is liquid and "veg" is not) there is no way around enumerating all related pairs of (item, category) in one way or the other.
There are three approaches to implementing it:
Enumerate categories on item's side - this is what your code does in both cases, or
Enumerate items on category's side - this would build an enum of categories, and attach a full list of items to each of them, or
Enumerate item+category pairs independently - this approach may be useful when storing item/category mapping in the database or in a configuration file.
I would recommend taking the third approach as it is the most "symmetric" one. Make a table for categories with category codes, and add a "cross-table" (or a cross-file) that has all pairs of categories and their corresponding items. Read the cross table/file at startup, and set up the dependencies on both sides.
public Enum ItemType {
FRUITS("fru")
, VEGETABLES("veg")
, LIQUOURS("liq")
, SODAS("sod");
public void addCategory(ItemCategory category) ...;
public EnumSet<ItemCategory> getItemCategories() ...;
}
public Enum ItemCategory {
LIQUIDS("liq")
, SNACKS("snk")
, FAST("fst");
public void addItem(ItemType type) ...;
public EnumSet<ItemType> getItemTypes() ...;
}
Cross-file or cross-table may look like this:
liq liq
sod liq
fru snk
fru fst
sod fst
You process it by enumerating pairs, and calling addCategory on the pair's item side, and calling addItem on the pair's category side.
These were three excellent answers, but I think I can combine all three in one nice package:
public enum ItemType {
FRUITS("fru",PERISHABLE),
VEGETABLES("veg",PERISHABLE),
LIQUOURS("liq",LIQUIDS),
SODAS("sod",LIQUIDS),
FRESH_SQUEEZED_ORANGE_JUICE("orgj",LIQUIDS,PERISHABLE);
private final String dbCode;
private final EnumSet<ItemCategory> categories;
private static final Map<ItemCategory,Set<ItemType>> INDEX_BY_CATEGORY = new EnumMap<>(ItemCategory.class);
ItemType(String dbcode,ItemCategory... categories) {
this.dbCode = dbcode;
this.categories = EnumSet.copyOf(Arrays.asList(categories));
//for (ItemCategory c:categories) {
// // Illegal Reference to Static Field!
// INDEX_BY_CATEGORY.put(c, this);
//}
}
static {
for (ItemCategory c:ItemCategory.values()) {
INDEX_BY_CATEGORY.put(c, EnumSet.noneOf(ItemType.class));
}
for (ItemType t:values()) {
for (ItemCategory c:t.categories) {
INDEX_BY_CATEGORY.get(c).add(t);
}
}
}
public boolean is(ItemCategory c) {
return INDEX_BY_CATEGORY.get(c).contains(this);
}
public Set<ItemType> getAll(ItemCategory c) {
return EnumSet.copyOf(INDEX_BY_CATEGORY.get(c));
}
public String getDbCode() {
return dbCode;
}
}
Now,
we can easily ask about additional subcategories without writing the code for it: boolean isVegetableLiquid = VEGETABLES.is(LIQUIDS);
we can easily assign not only one, but multiple categories to an item as you can see for FRESH_SQUEEZED_ORANGE_JUICE.
we are using EnumSet and EnumMap for performance, including their methods like contains.
we absolutely are minimizing the amount of code required to add an additional item. This could be further minimized by setting this up by database or configuration. However, in that case we would have to avoid the use of Enum as well.

Replacing Enum constants with static variable fetched from table

Dealing with an old enum class which contains group of constants. A function inside enum class is defined to check if a passed constant is present in the enum or not.
Now there can be some constants that may be added to enum. If so it has to go through separate release process.
So we want to get rid of enum and get data from db or from a property file so that any new addition need not go through release.
But there is a problem with it. There are too many modules referring to the enum, hence demising enum will cause too many file changes.
Initial Enum class
Class enum Planets{EARTH,MARS,SATURN};
private static final Planets[] codes={EARTH,MARS,SATURN};
public static boolean isPlanetPresent(String str){//static Edited 19-09-2013
for(Palnets planet : codes){
if(planet.equals(str)){
return true;
}
return false;
}
With the above if we need to check whether NEPTUNE is part of Planets then we need to use Planets.isPlanetPresent("NEPTUNE") .
In order to minimize changes, I decided to retain the enum class but as a ordinary class.
A static array is declared inside the class and is loaded during runtime which reads data from a table in db.
Static alternative I thought of:
Class Planets{
private static String[] codes;
public Planets{
Spring IOC to connect to db and get the data and place it into codes array( EARTH,MARS,SATURN)
}
public static boolean isPlanetPresent(String str){
for(String planet : codes){
if(planet.equals(str)){
return true;
}
return false;
}
}
With the above implementation Planets.isPlanetPresent("NEPTUNE") need not be changed in other files.
My question is:
Is my implementation correct?
Are there any better alternative implementations?
Way 1
You could declare an entity (if you use an ORM) with static constant, something like this (with hibernate):
public class Planet {
public static final Planet SATURN = new Planet(1L, "SATURN");
public static final Planet MARS = new Planet(2L, "MARS");
// ...
private static final Planet[] VALUES = { SATURN, MARS /*, ...*/ }
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name", nullable = false)
private String name;
private Planet(final Long id, final String name) {
this.id = id;
this.name = name;
}
/**
* Simulates enum#values().
*
* #return list of declared values.
*/
public static Planet[] values() {
return VALUES.clone();
}
/**
* Simulates enum#valueOf(String).
*
* #return found {#link Planet} or null.
*/
#Nullable
public static Planet valueOf(final String name)
{
Planet result = null;
for (final Planet val : values()) {
if (val.getName().equals(name)) {
result = val;
break;
}
}
return result;
}
}
Way 2
Stick to an enum, and use DB "enumerated" datatype to ensure a db change when an enum value is added.

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

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

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;
}
}

Categories

Resources