8

I've never really made use of Java enum classes before for constant values, I've typically used the "public final" approach in the past. I've started using an enum now and I'm overriding the toString() method to return a different value than the enum name.

I have some JPA code in which I'm creating a TypedQuery with named parameters, one of which is a String representation of the enum value. If I merely set the parameter using Status.ACTIVE, I get the proper "A" value, but an exception is thrown because it's type is actually Status rather than String. It only works if I explicitly call the toString() method. I thought that simply overriding the toString() method would result in a String type being returned, no matter what the class type was.

This is the enum:

public enum Status {
    ACTIVE ("A"),
    PENDING ("P"),
    FINISHED ("F");

    private final String value;

    Status(String value) {
        this.value = value;
    }

    public String toString() {
        return value;
    }
};

This is the TypedQuery:

    TypedQuery<MechanicTimeEvent> query = entityManager().createQuery("SELECT o FROM MechanicTimeEvent o WHERE o.id.mechanicNumber = :mechanicNumber AND o.id.status = :status", MechanicTimeEvent.class);
    query.setParameter("mechanicNumber", mechanicNumber);
    query.setParameter("status", Status.ACTIVE.toString());
user85421
  • 28,957
  • 10
  • 64
  • 87
Patrick Grimard
  • 7,033
  • 8
  • 51
  • 68
  • Can add the mapping of that field to the question? – Augusto Oct 13 '11 at 17:58
  • 1
    If you're looking for a more concise way to write it, `query.setParameter("status", Status.ACTIVE+"");` will work. – Eric Oct 13 '11 at 17:59
  • 1
    Is the field in your `@Entity` of type `Status` and is it annotated with `@Enumerated(EnumType.STRING)`? If so you should be able to use enums in queries just fine. – Tomasz Nurkiewicz Oct 13 '11 at 18:04
  • [Here](http://stackoverflow.com/questions/352586/how-to-use-enums-with-jpa) and [here](http://stackoverflow.com/questions/1979176/how-to-use-enum-with-jpa-as-a-data-member-of-persisted-entity) similar problems and answers have been discussed already. – A.H. Oct 13 '11 at 18:07
  • The field in my @Entity is merely a String, but it will only ever be a value of A, P or F, so I created an enum for those values so that in the code, I could use something like Status.ACTIVE and it would be more descriptive. I figured overriding the toString() method to return the actual value would return a String type without explicitly calling toString(). – Patrick Grimard Oct 13 '11 at 18:11

6 Answers6

5
public enum Status {
    ACTIVE,
    PENDING,
    FINISHED;

    @Override
    public String toString() {
        String name = "";
        switch (ordinal()) {
        case 0:
            name = "A";
            break;
        case 1:
            name = "P";
            break;
        case 2:
            name = "F";
            break;
        default:
            name = "";
            break;
        }
        return name;
    }
};
Lazy Beard
  • 305
  • 3
  • 10
  • 8
    This toString() implementation requires too much maintenance especially if the enum names will ever be added to in the future. it is best to assign a string value on instanciation and return that instead. – initialZero Aug 01 '13 at 17:31
  • ... as the Question solution provides. That code is more readable and the locality between the "String mapping" (`ACTIVE ("A")`), also avoids running code with typos (ie: `case 3: name = "E"` instead of `case 2: name = "F"`). Also, the switch default value is meaningless. – José Andias Jan 14 '15 at 12:04
3

If I understand your question correctly, You should do the enum mapping other way around. In this way states are stored as state and JPA would process the enum based on its name A,P, F).

public enum Status {
    A("ACTIVE"),
    P("PENDING"),
    F("FINISHED");

In this way you can just pass Status without invoking the toString() method to JPA. The .name() method on the ENUM will be invoked automatically to get the status code for persistence.

java_mouse
  • 2,069
  • 4
  • 21
  • 30
  • 1
    You shouldn't cut down field names to one letter just because that's the form your database/whatever uses. Can't you just override `.name()`? – Eric Oct 13 '11 at 18:15
  • 2
    Enum.name() method is final so can't be overridden. – java_mouse Oct 13 '11 at 18:17
  • 2
    The purpose of doing it the way I've got it is so if someone else reads the code, Status.ACTIVE is more obvious than Status.A. – Patrick Grimard Oct 13 '11 at 19:08
  • As the [Java API docs](http://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html#name%28%29) points out in bold: _most programmers should use the toString()_. Overriding `toString()` is the right path. – José Andias Jan 14 '15 at 12:13
3

Is the field status of the MechanicTimeEvent bean an enum type? If not, I would suggest to change it to the enum type Status.

You can annotate it with @Enumerated(EnumType.STRING)

Furthermore I would suggest to remove the value part of your enum and just use the names like:

public enum Status {
   ACTIVE,
   PENDING,
   FINISHED;
}
lauwie
  • 301
  • 1
  • 5
  • It wasn't an enum type, so I changed it to type Status and added the annotation you suggested. I think that did the trick. Weird thing now though is if no record are found by my query, then it works fine, but if it finds a record, then OpenJPA throws an ArgumentException and tells me to check the syntax of my query, not sure why. – Patrick Grimard Oct 13 '11 at 19:10
  • Did you use the enum value instead of string?: ie query.setParameter("status", Status.ACTIVE) – lauwie Oct 13 '11 at 19:42
  • Actually, the problem is that since the database only stores the values, ie: A, P or F, then when creating a select statement, OpenJPA passes the value A returned from the query to the valueOf method of the enum. Since it doesn't find A in the enum, it throws the exception. The valueOf method is final, so I can't override it. – Patrick Grimard Oct 13 '11 at 19:47
  • Ah, yeah. Perhaps a downside, but when using enums in combination with JPA only the names count, not the values. Personally I would just remove the **value** property and only use the key names of the enum. See my bjoetified answer ;-) – lauwie Oct 13 '11 at 20:10
  • Hey lauwie, I figured out how to deal with it. I ended up switching from OpenJPA to EclipseLink. With EclipseLink I'm able to define my enum as simple as you have it without a constructor, then in on my entity field, I add a couple EclipseLink annotations for taking care of the conversion. Check the Gist I just posted. https://gist.github.com/1287713 – Patrick Grimard Oct 14 '11 at 17:25
0

This or you simply implement a getter for the value:

public String getValue()

And you call it in your code:

query.setParameter("status", Status.ACTIVE.getValue());
falsarella
  • 12,217
  • 9
  • 69
  • 115
Cygnusx1
  • 5,329
  • 2
  • 27
  • 39
0

toString is just an ordinary method in Object that is explicitly called by some methods like PrintStream.println (remember System.out.println) or considered during concatenation using the + operator. Not every method needs to implement this behavior.

I'd suggest you use a more descriptive method name like getValue and call it explicitly instead of override toString

Jagat
  • 1,392
  • 2
  • 15
  • 25
-1
java.lang.Enum said clearly:
 /**
 * Returns the name of this enum constant, exactly as declared in its
 * enum declaration.
 * 
 * <b>Most programmers should use the {@link #toString} method in
 * preference to this one, as the toString method may return
 * a more user-friendly name.</b>  This method is designed primarily for
 * use in specialized situations where correctness depends on getting the
 * exact name, which will not vary from release to release.
 *
 * @return the name of this enum constant
 */
 public final String name()

just like the loft say ,you can use "name" method to get the name. also you can use toString() method.

of course it is just name of this enum constant.

zg_spring
  • 349
  • 1
  • 10