Showing posts with label enum. Show all posts
Showing posts with label enum. Show all posts

4 November 2013

JPA : choose your enum ordinals (Updated)

Ordinal enums are brittle with JPA.
If you change the order of the enum constants,
the ordinal number that is saved to the database changes.

You can solve that by assigning your own code in the enum (10,20,30 in the example).
In the entity, make the enum transient.
Before saving the entity put your code in an int attribute, which will be stored in the database.
After loading convert the int back to the enum.

 Update: In JPA 2.1 you can also do this with @Converter


public enum Status{
  OPEN(10), CLOSED(20), CANCELLED(30);
  private int code;  
  
  private Status(int code) {  
    this.code = code;  
  }  
       
  public int getCode() {  
    return code;  
  }
     
  public static  Status getStatus(int code){
    for (Status stat : Status.values()) {
      if (stat.getCode()==code) return stat;
    }
    return null; // not found: invalid code
  }
}
       
@Entity
public class Auction{
  private int status;
  @Transient private Status statEnum;

  public int getStatus(){
    return status;
  }

   public void setStatus(int status){
    this.status = status;
  }

  @PostLoad private void int2enum(){
    statenum = Status.getStatus(status);
  }

  @PrePersist private void enum2int(){
    status = statEnum.getCode();
  }
  //...
}


NB: kudos to Guy for moving conversion to callbacks.

15 April 2012

Java enum demo

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

public enum EnumDemo {
  // For this enum each value has an identification code
  // other than the internal ordinal value
  DEMANDED(10),
  SPECIFIED(20),
  EVALUATED(30),
  DUPLICATE(33),
  ESTIMATED(35),
  WORKAROUND (36),
  ACCEPTED(37),
  PLANNED(40),
  BUSY(50),
  IMPLEMENTED(60),
  REJECTED(100);

  private int code;

  /**
   * @param code  constructor takes code to  be associated with enum
   */
  EnumDemo(int code) {
    this.code = code;
  }

  /**
   * Helper map for reverse lookup in getByCode method
   */
  private static Map<Integer,EnumDemo> enumByCode;
  static{
    enumByCode = new HashMap<Integer, EnumDemo>();
    for (EnumDemo val: EnumDemo.values()) {
      enumByCode.put(val.getCode(),val);
    }
  }

  public int getCode() {
    return code;
  }

  /**
   * reverse lookup
   * @param code the code of the enum
   * @return the enum or null if the code does not exist
   */
  public static EnumDemo getByCode(int code){
    return enumByCode.get(code);
  }

  /**
   * test if a code exists
   * @param code
   * @return  true if the code exists
   */
  public static boolean isCodeValid(int code){
    return enumByCode.containsKey(code);
  }

  /**
   * enumToString: EnumDemo.DEMANDED to "DEMANDED"
   * @return enum value name as a String
   */
  public String getName(){
    return toString();
  }

  /**
   * StringToEnum: "  demanded" to EnumDemo.DEMANDED
   * @param name case insensitive and leading and trailing spaces are ignored
   * @return  the enum constant
   */
  public static EnumDemo getByName(String name){
    return EnumDemo.valueOf(name.trim().toUpperCase());
  }
 
  /**
   * convert between enum and internal ordinal number
   * not recommended to expose this, just here for demo purposes
   * @return internal ordinal number of the constant
   */
  public int getOrdinal(){
    return getOrdinal();
  }
 
  /**
   * convert from ordinal number to enum. Not recommended.
   * @param ordinal
   * @return  the enum
   */
  public static EnumDemo getByOrdinal(int ordinal){
    return EnumDemo.values()[ordinal];
  } 
 
  // some tests and subsets
  // alternate methods can be written using the codes
    
  /**
   *
   * @return  is this enum in a range?
   */
  public boolean isOK(){
    return ACCEPTED.compareTo(this) <= 0 && IMPLEMENTED.compareTo(this)  >= 0;
  }

  /**
   * @return  an enum subrange
   */
  public static EnumSet<EnumDemo> getOK(){
    return EnumSet.range(ACCEPTED,IMPLEMENTED);
  }

  /**
   * @return  is this enum in a subset?
   */
  public boolean isNOK(){
    return this == DUPLICATE
      || this == WORKAROUND
      || this == REJECTED;
  }

  /**
   *
   * @return  get an enum subset
   */
  public static EnumSet<EnumDemo> getNOK(){
    return EnumSet.of(DUPLICATE,WORKAROUND,REJECTED);
  }
}