Enum getting next code in Java

Asked

Viewed 318 times

0

I have a class in java that one of its attributes is an Enum and has the following values:

    HIPERTROFIA(1, "Hipertrofia"),
    DEFINICAO(2, "Definição"),
    RESISTENCIA(3, "Resistência"),
    OUTROS(4, "Outros");

When I send the value 0, saved in the bank the value 1. When I send the value 1, saved in the bank the value 2 and so on.

package br.com.academia.domain;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;

import org.hibernate.validator.constraints.Length;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import br.com.academia.domain.enums.StatusSerie;
import br.com.academia.domain.enums.TipoSerie;

@Entity
public class Solicitacao implements Serializable{
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator = 
"seqSolicitacao")
@SequenceGenerator(name = "seqSolicitacao", sequenceName = 
"seq_id_solicitacao")
private Integer id;

private Integer tipoSerie;
//definição de todos os atributos

public Solicitacao() {
    super();
}


public Solicitacao((...), TipoSerie tipoSerie, (...)) {
    super();
    (...)
    this.tipoSerie = (tipoSerie == null) ? null : tipoSerie.getCodigo();
    (...)

}


//get e set

public TipoSerie getTipoSerie() {
    return TipoSerie.toEnum(tipoSerie);
}


public void setTipoSerie(TipoSerie tipoSerie) {
    this.tipoSerie = tipoSerie.getCodigo();
}

//get e set

}

The Enum class:

package br.com.academia.domain.enums;

public enum TipoSerie {

HIPERTROFIA(1, "Hipertrofia"),
DEFINICAO(2, "Definição"),
RESISTENCIA(3, "Resistência"),
OUTROS(4, "Outros");

private int codigo;
private String descricao;

private TipoSerie(int codigo, String descricao) {
    this.codigo = codigo;
    this.descricao = descricao;
}

public int getCodigo() {
    return codigo;
}

public String getDescricao() {
    return descricao;
}

public static TipoSerie toEnum(Integer codigo)
{
    if(codigo == null)
    {
        return null;
    }

    for (TipoSerie serie: TipoSerie.values())
    {
        if(codigo.equals(serie.getCodigo()))
        {
            return serie;
        }
    }
    throw new IllegalArgumentException("Código inválido" + codigo);
}


}

Images that demonstrate the problem:

imagem 1

imagem 2

Note that in Postman I send 0 and in the bank saved 1.

My Source, where I receive the request

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<Solicitacao> create(@Valid @RequestBody Solicitacao solicitacao)
    {
    solicitacao = solicitacaoService.save(solicitacao);

    URI uri = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("/{id}").buildAndExpand(solicitacao.getId()).toUri();
    return ResponseEntity.created(uri).build();
    }

In the service I only make some sets in some values, but I do not make any changes in typeSerie, which in turn, already arrives in the service with wrong value.

  • You said that "When I send value 0, save in bank value 1. When I send value 1, save in bank value 2 and so on. " Wouldn’t it be the other way around (sends value 1, saves value 0)? Since there is no Enum with value 0 in your example. Also, your attributes/getters are not annotated?

  • That’s right, friend, when I send the 0 it saves the 1. I noticed this after trying to save the 1 and it insistently save the value 2. What I don’t understand is that I set the code to be assigned to each value. The typSerie attribute is not annotated yes, only annotated in the other attributes I did not cite in the post.

  • Why you are sending the value 0 if the smallest code of your Enum is 1? It should not launch an exception in this case with the message "Invalid code"?

  • Brother, I want to understand this. It was to blow the exception! It’s like java ignores the code that I set and takes the count from scratch. I know this happens when the code is not defined, for example HYPERTROPHY("Hypertrophy") if I put like this, the hypertrophy would be zero. Take a look at these images.

  • I believe the problem occurs when you convert JSON to your object. Maybe it is using the ordinal instead of the method toEnum to create the TipoDeSerie and inject it into the Setter. You edit your question and place the code where you receive the request?

  • I’ve already added there, Felipe.

Show 1 more comment

2 answers

2


When Jackson will deserialize a JSON and the properties AUTO_DETECT_GETTERS and AUTO_DETECT_SETTERS are at the value true (standard value), the setters/getters of its object are used to populate it (if any).

Like your Setter receives a Enum of the kind TipoSerie, Jackson tries to use the number entered in his JSON to identify which is the corresponding element in Enum. The problem is Jackson doesn’t know to use the method toEnum since you didn’t inform him. Then he uses the default form in this conversion, which is through the ordinal (when the value is a number). Therefore, when you enter the value 0, it returns the element HIPERTROFIA (index 0, code 1).

So he can use the method toEnum to deserialize JSON, you should note such a method with @JsonCreator, so he will make the conversion in the expected way.

Just as you need to tell you how to deserialize JSON, you need to tell it how to serialize the object. For this, you must use the annotation @JsonValue in his method getCodigo.

Therefore, your Enum TipoSerie would look that way:

public enum TipoSerie {

    HIPERTROFIA(1, "Hipertrofia"),
    DEFINICAO(2, "Definição"),
    RESISTENCIA(3, "Resistência"),
    OUTROS(4, "Outros");

    private int codigo;
    private String descricao;

    private TipoSerie(int codigo, String descricao) {
        this.codigo = codigo;
        this.descricao = descricao;
    }

    @JsonValue
    public int getCodigo() {
        return codigo;
    }

    public String getDescricao() {
        return descricao;
    }

    @JsonCreator
    public static TipoSerie toEnum(Integer codigo) {
        if(codigo == null) {
            return null;
        }

        for (TipoSerie serie: TipoSerie.values()) {
            if(codigo.equals(serie.getCodigo())) {
                return serie;
            }
        }
        throw new IllegalArgumentException("Código inválido" + codigo);
    }
}
  • Master, simply fantastic! Very good explanation. These 2 notes solved this problem. Thank you very much, Felipe :)

0

You can try using one Converter/AttributeConverter:

package br.com.academia.domain;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;

import org.hibernate.validator.constraints.Length;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import br.com.academia.domain.enums.StatusSerie;
import br.com.academia.domain.enums.TipoSerie;

@Entity
public class Solicitacao implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqSolicitacao")
    @SequenceGenerator(name = "seqSolicitacao", sequenceName = "seq_id_solicitacao")
    private Integer id;

    private TipoSerie tipoSerie;

    // Definição de outros atributos.

    public Solicitacao() {
        super();
    }

    public Solicitacao(/*...,*/ TipoSerie tipoSerie /*, ...*/) {
        super();
        // ...
        this.tipoSerie = tipoSerie;
        // ...
    }

    // Getters e setters.

    public TipoSerie getTipoSerie() {
        return tipoSerie;
    }

    public void setTipoSerie(TipoSerie tipoSerie) {
        this.tipoSerie = tipoSerie;
    }
}
public enum TipoSerie {
    HIPERTROFIA("Hipertrofia"),
    DEFINICAO("Definição"),
    RESISTENCIA("Resistência"),
    OUTROS("Outros");

    private final String descricao;

    private TipoSerie(String descricao) {
        this.descricao = descricao;
    }

    public int getCodigo() {
        return ordinal() + 1;
    }

    public String getDescricao() {
        return descricao;
    }

    public static TipoSerie toEnum(Integer codigo) {
        if (codigo == null) return null;
        try {
            return values()[codigo - 1];
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new IllegalArgumentException("Código inválido" + codigo, e);
        }
    }
}
import javax.persistence.Converter;
import javax.persistence.AttributeConverter;

@Converter(autoApply = true)
public class MapeadorTipoSerie implements AttributeConverter<TipoSerie, Integer> {

    @Override
    public Integer convertToDatabaseColumn(TipoSerie x) {
        return x.getCodigo();
    }

    @Override
    public TipoSerie convertToEntityAttribute(Integer y) {
        return TipoSerie.toEnum(y);
    }
}

Also note that you can take advantage of the method ordinal() that all enum has to derive the code.

  • Master, but in this example you gave, I lose control of the Enum identifier. Let’s say that another inattentive programmer arrives and adds a value above HYPERTROPHY ("Hypertrophy"), this new value would not assume the value of hypertrophy?

  • @Marcelonascimento In this case, then you would need the getCodigo() just the way it was.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.