Hibernate Synchronizer - pozor na UserType

23. April 2007

Při čtení článku Schéma databáze – používáme hibernate mě napadlo, abych napsal o jednom problému, který souvisí s generováním POJOs z mapovacích souborů pomocí Hibernate Synchronizeru v případě používání vlastních typů (UserTypes).

Hibernate umožňuje psaní vlastních typů – takzvaných UserTypes.Mohou být buď jednoduché (skalární – s jednou vlastností) – typ UserType nebo kompozitní – CompositeUserType (více vlastností).

Ukažme si na příkladu použití kompozitního UserTypu:

package cz.vavru.sample.hibernate;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Currency;

import net.sf.hibernate.CompositeUserType;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.engine.SessionImplementor;
import net.sf.hibernate.type.Type;
import cz.vavru.sample.hibernate.MonetaryAmount;

public class MonetaryAmountType implements CompositeUserType {

        public Class returnedClass() {
                return MonetaryAmount.class;
        }

        ...

        public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session,
                        Object owner) throws SQLException {

                double value = resultSet.getDouble(names[0]);
                if (resultSet.wasNull())
                        return null;
                Currency currency = Currency.getInstance(resultSet.getString(names[1]));
                return new MonetaryAmount(new Double(value), currency);
        }

        public void nullSafeSet(PreparedStatement statement, Object value, int index,
                        SessionImplementor session) throws SQLException {

                if (value == null) {
                        statement.setNull(index, Hibernate.BIG_DECIMAL.sqlType());
                        statement.setNull(index + 1, Hibernate.CURRENCY.sqlType());
                } else {
                        MonetaryAmount amount = (MonetaryAmount) value;
                        String currencyCode = amount.getCurrency().getCurrencyCode();
                        statement.setDouble(index, amount.getValue().doubleValue());
                        statement.setString(index + 1, currencyCode);
                }
        }

        ...
}

Příslušný doménový objekt MonetaryAmount vypadá následovně:

package cz.vavru.sample.hibernate;

import java.io.Serializable;
import java.util.Currency;
import java.util.Iterator;

public class MonetaryAmount implements Serializable {
        private static final long serialVersionUID = Long.MIN_VALUE * 34;
        private Double value;
        private final Currency currency;

        public MonetaryAmount(Currency currency) {
                value = new Double(0d);
                this.currency = currency;
        }

        public MonetaryAmount(BigDecimal bigDecimal, Currency currency) {
                this.value = new Double(bigDecimal.doubleValue());
                this.currency = currency;
        }

        public MonetaryAmount(Double value, Currency currency) {
                this.value = value;
                this.currency = currency;
        }

        public Currency getCurrency() {
                return currency;
        }

        public Double getValue() {
                return value;
        }

        public void setValue(Double value) {
                this.value = value;
        }

        public boolean equals(Object o) {
                if (this == o)
                        return true;
                if (!(o instanceof MonetaryAmount))
                        return false;

                final MonetaryAmount monetaryAmount = (MonetaryAmount) o;

                if (!currency.equals(monetaryAmount.currency))
                        return false;
                if (value != monetaryAmount.value)
                        return false;

                return true;
        }

        public int hashCode() {
                int result;
                result = 29 * value.intValue() + currency.hashCode();
                return result;
        }

        public String toString() {
                return "Value: '" + getValue() + "', " + "Currency: '" + getCurrency() + "'";
        }
}

A ukázka využití v mapovacím souboru jiného doménového objektu, který MonetaryAmount používá:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >

<hibernate-mapping>
        <class name="cz.vavru.sample.hibernate.MyEntity"
                table="ccg_pg_yearly_settings" >

                <id name="id" type="java.lang.Integer" column="id">
                        <generator class="increment" />
                </id>

                ...

                <property name="budget" not-null="false"
                        type="cz.vavru.sample.hibernate.MonetaryAmountType">
                        <column name="budget_amount" />
                        <column name="budget_currency" />
                </property>

        </class>
</hibernate-mapping>

Kód, který Hibernate Synchronizer vygeneruje nesprávně používá pro vlastnost budget typ MonetaryAmountU­serType místo správného MonetaryAmount.

package cz.vavru.sample.hibernate;

public class MyEntity implements Serializable {
        ...
        private cz.vavru.sample.hibernate.MonetaryAmountType budget;
        public cz.vavru.sample.hibernate.MonetaryAmountType getBudget () {
                return budget;
        }

        public void setBudget (cz.vavru.sample.hibernate.MonetaryAmountType budget) {
                this.budget = budget;
        }
        ...
}

Správný kód je tedy:

package cz.vavru.sample.hibernate;

public class MyEntity implements Serializable {
        ...
        private cz.vavru.sample.hibernate.MonetaryAmount budget;
        public cz.vavru.sample.hibernate.MonetaryAmount getBudget () {
                return budget;
        }

        public void setBudget (cz.vavru.sample.hibernate.MonetaryAmount budget) {
                this.budget = budget;
        }
        ...
}

Přiznám se, že mi odhlaní tohoto problému zabralo asi hodinu.

Odkazy

Článek patří do kategorie: Java

2 Komentářů Přidat komentář

  • 1. Ladislav Thon  |  24. April 2007 v 7.51

    K tomu jednu otázku, zcela mimo zaměření textu: jistě je vhodné ukládat peněžní obnos jako double? Víme přece, jak je to s jeho přesností už u základních početních úkonů typu násobení či dělení…

  • 2. Vlasta  |  24. April 2007 v 8.22

    [1]Správné je použit typ BigDecimal, ale zde jsem si jej dovolil nahradit Doublem.

Přidat komentář

Povinné

Povinné, skryté

Security Image Povinné
Opište text z obrázku

Povolené HTML značky:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>

Odkazovat na tento článek  |  Přihlásit se k odběru těchto komentářů přes RSS Feed


Kalendář

November 2024
M T W T F S S
« Jan    
 123
45678910
11121314151617
18192021222324
252627282930  

Poslední články

Locations of visitors to this page