Dúvida sobre persistência de objetos de uma classe Moeda

Pessoal, estou com uma dúvida aqui que deve ser idiota, mas eu não consegui encontrar informações suficientes no Google para resolvê-la. A dúvida surgiu após ler o artigo do Phillip na Mundo Java 17.

Imaginem que eu tenha uma classe Moeda, por exemplo, que tem dois atributos: um do tipo long e seria o número de centavos de uma determinada quantia em dinheiro (para evitar problemas de operações com ponto flutuante), o outro seria um objeto java.util.Currency que serveria para dizer em qual moeda aquela quantia está (Real, Dólar, Euro, etc.). Imaginem ainda uma classe Pedido que tem como um de seus atributos o valorTotal do pedido, do tipo Moeda.

A dúvida é: como se daria a persistência dessa moeda em um banco de dados? Na tabela pedido eu deveria ter uma coluna para a quantidade de centavos e outra para a moeda? Se sim e eu estiver usando o hibernate, como seria feito o mapeamento de Pedido? Se não, como eu deveria fazer? Desculpem se não fui claro.

Para finalizar, alguém já viu ou usou isso aqui? http://www.jvalue.org/

Editado: Não gostei do título, mas não consegui imaginar nada muito bom. Se alguém tiver alguma sugestão…

David,

Para não ter problemas de ponto flutuante você pode usar o BigDecimal. Sobre o mapeamento no hibernate:

http://www.hibernate.org/hib_docs/v3/reference/en/html/components.html

[]s

Oi,

Depende bastante. Como sua empresa(ou cliente) quer guardar valores monetários no banco de dados? Centavos e inteiro separados? Só centavos? um double?

Seja como for, acredito que um mapeamento de componente (linkado pelo Rodrigo) seja o que você procura. Value objects como moedas ou datas dificilmente vão ter uma tabela própria ou coisa do tipo, eles geralmente ficam na mesma tupla da Entity que o referencia.

No momento eu não estou fazendo nada no momento que precise disso, foi apenas uma dúvida que surgiu após a leitura do artigo. E eu acredito que mapeamento de componentes faça o que eu quero mesmo! Muito obrigado! :slight_smile:

Uma dúvida.
Se você trabalha com apenas duas casas é necessário usar o BigDecimal;
Já tive problemas com o double em operações de comparação.
Mas resolvi isto.
É recomendável usar double ?

Double não deve ser usado para matemática exata, não foi feito para isso. Até hoje 99% dos sistemas que trabalhei (excluindo apenas um fortemente baseado em estatística) não deveriam usar double. Provavelmente é o seu caso.

Use Bigdecimal ou uma classe de Moeda, como sugerido no artigo em questão.

quote=pcalcado não deveriam usar double. (…)[/quote]Não deveriam mas foi usado? Ou você realmente não usou? Se usou, o que fez para resolver os problemas?

Eu estou trabalhando num sistema em que o double não deveria ter sido usado, mas por falta de experiência nossa no início (o projeto tem mais de dois anos), ele foi. Estou tendo uns problemas chatos agora, pois uns relatórios financeiros ficam dando diferenças de 1 centavo para mais ou para menos, mas infelizmente estamos sem tempo para fazer um refactoring.

Foram usados. Se você olhar meu curriculum vai ver que nos últimos anos eu tenho praticamente refatorado sistemas de todos os tamanhos.

Refatorar de double pra Bigdecimal/Moeda não é fácil. É normal quando se falta experiência mas depois que se aprende uma vez é apra nunca mais usar mesmo. Lembro de um sistema de gerência de empréstimos que fiz (nad aa ver com o exemplo da revista, por acaso) que foi onde descobri sobre doubles. Simplesmente a cotna nunca batia com a do sistema que eu estava substituindo, eram poucos mas imperdoáveis centavos de diferença.

No meu projeto atual, além de refatorar dois sistemas eu estou criando um novo, que lida basicamente com vendas, e este não usa double, apenas Moeda.

Uma solução adotada em um dos projetos da minha empresa foi fazer instrumentação do código (bytecode enhancement). Desta forma foram utilizadas operações com double em código-fonte, mas na execução, as contas eram feitas com BigDecimal. Esta solução foi escolhida para não poluir o código, pois pode-se usar os operadores matemáticos em expressões complexas.

i.e. bytecode enhancement porque Java não tem sobrecarga de operador.

Bom, que tal especificar fórmulas complexas em Groovy? Na verdade a idéia pode evoluir para uma DSL.

Como isso seria feito, Phillip? Poderia dar um exemplo?

Essa classe Moeda seria uma classe personalizada ?
Alguém tem um exemplo de como fazer um mapeamento no hibernate para
uma classe Moeda personalizada ?

Obs: Sei qque posso encontrar no Google, mas gostaria de ouvir a opinião pessoal e a experiência de vocês.

Que ferramenta você usou para isto ?
Fiquei muito interessado.

Em um dos clientes que atendo o modulo de calculo foi todo feito com Jython e provavelmente iremos migrar para Groovy.

]['s

Pessoal!
Qual o número da revista com o Artigo?

Poderiam postar o código fonte da classe Moeda?
Algum exemplo com bigDecimal? :wink:

Sim, ver abaixo.

Veja o post acima do rodrigoy sobre componentes no hibernate.

Não estou com a revista aqui mas foi essa última, cuja matéria de capa é a de TV Digital. A classe moeda seria algo assim:

public class Moeda {
    private long centavos;

    public Moeda(long centavos) {
        this.centavos = centavos;
    }

    public Moeda(long inteiro, int centavos) {
        this.centavos = inteiro * 100 + centavos;
    }

    public Moeda somarCom(Moeda m) {
        return new Moeda(this.centavos + m.centavos);
    }    

    // ... e por ai vai.

}

Eu pensei ainda em colocar um atributo java.util.Currency para dizer em qual moeda aquele valor estava, pois as vezes eu preciso trabalhar com moedas diferentes.

E sobre o BigDecimal: http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html

Este é um ótimo exemplo para demonstrar que um value object possui comportamentos (operações de negócio). Este pattern é confundido com o DTO.

Neste caso é iteressante armazenar o simbolo monetário u alguma informação dizendo qual a moeda utilizada ?
Como vocês fazem isto ?
Nunca trabalhei em um projeto que necessitasse disto.

Se você for trabalhar com moedas diferentes é necessário sim você dizer qual a moeda que vai usar. Eu pensei em um atributo do tipo java.util.Currency, como falei antes, mas não sei se seria viável ou como ele seria armazenado no banco.

Galera vcs que trabalham com BigDecimal, um colega meu disse que o ejb nao dava “mapeamento” para BigDecimal e sim apenas para Double, ainda é assim ou ja tem suporte para BigDecimal tbem ? senão so da pra usar Double mesmo é isso ?

Geralmente eu uso um código + ou - assim(pego em koders.com):

[code]
import java.math.BigDecimal;

/**

  • Object to store and manipulate money.
    /
    public class Currency extends BigDecimal {
    /
    *

    • Construct a Currency object of value zero.
      */
      public Currency() {
      super(0);
      }

    /**

    • Construct a Currency object from a BigDecimal or Currency object.
    • A null value result in an object containing zero.
      */
      public Currency(BigDecimal value) {
      super((value == null) ? 0.0 : value.doubleValue());
      }

    /**

    • Construct a Currency object from a double.
      */
      public Currency(double value) {
      super(value);
      }

    /**

    • Construct a Currency object from a float.
      */
      public Currency(float value) {
      super((double)value);
      }

    /**

    • Construct a Currency object from a String.
      */
      public Currency(String value) {
      this(Double.valueOf(value).doubleValue());
      }

    /**

    • Check if equal to a float value.
      */
      public boolean equals(float value) {
      return compareTo(new Currency(value)) == 0;
      }

    /**

    • Check if equal to a double value.
      */
      public boolean equals(double value) {
      return compareTo(new Currency(value)) == 0;
      }

    /**

    • Returns a Currency whose value is (this + val).
      */
      public Currency add(Currency val) {
      return new Currency(super.add(val));
      }

    /**

    • Returns a Currency whose value is (this - val).
      */
      public Currency subtract(Currency val) {
      return new Currency(super.subtract(val));
      }

    /**

    • Returns a Currency whose value is (this * val)
      */
      public Currency multiply(Currency val){
      return new Currency(super.multiply(val));
      }

    /**

    • Returns a Currency whose value is (this * val)
      */
      public Currency multiply(int val){
      return multiply(new Currency(val));
      }

    /**

    • Returns a Currency whose value is (this / val).
      */
      public Currency divide(Currency val)
      throws ArithmeticException, IllegalArgumentException {
      return new Currency(super.divide(val, 2, ROUND_HALF_UP));
      }

    /**

    • Returns a Currency whose value is (this / val).
      */
      public Currency divide(int val)
      throws ArithmeticException, IllegalArgumentException {
      return divide(new Currency((double)val));
      }

    /**

    • Returns a Currency whose value is the absolute value of this
    • number.
      */
      public Currency absCurrency() {
      return (signum() < 0 ? negateCurrency() : this);
      }

    /**

    • Returns a Currency whose value is -1 * this.
      */
      public Currency negateCurrency() {
      return new Currency(negate());
      }

    /**

    • Convert to string with two decimals.
      */
      public String toString() {
      if (scale() == 2) {
      return super.toString();
      } else {
      return setScale(2, ROUND_HALF_UP).toString();
      }
      }
      }[/code]
      Mas não tem muito jeito se vc precisar de muuitas casas decimais e/ou mexer com double para alguma coisa…Uso sempre BigDecimal, mas custa em velocidade…
      Eu também tenho problemas quando eu penso no que(que tipo Monetário ou Numérico) inserir no banco quando o assunto é dinheiro.