2
\$\begingroup\$

I have a class Currency, where I want to store business logic for working with currencies:

public class Currency implements Comparable<Currency> {
    public static final int NAME_MAX_STRING_LENGTH = 50;
    public static final int ISO_CODE_STRING_LENGTH = 3;
    public static final int SYMBOL_STRING_LENGTH = 1;
    public static final int COMMENTS_MAX_STRING_LENGTH = 100;

    private String name;
    private String isoCode;
    private String symbol;
    private String comments;

    public Currency(String name, String isoCode, String symbol, String comments) {
        setName(name);
        setIsoCode(isoCode);
        setSymbol(symbol);
        setComments(comments);
    }

    public String getIsoCode() {
        return isoCode;
    }

    public String getName() {
        return name;
    }

    public String getSymbol() {
        return symbol;
    }

    public String getComments() {
        return comments;
    }

    public void setIsoCode(String isoCode) {
        if (isoCode == null) {
            throw new ArgumentIsNullException("isoCode");
        }
        if (isoCode.length() != ISO_CODE_STRING_LENGTH) {
            throw new IllegalArgumentException(
                    "ISO code should be consisted of 3 letters.");
        }
        if (!isoCode.matches(defineRegularExpressionForIsoCode())) {
            throw new IllegalArgumentException("ISO code is not valid.");
        }
        this.isoCode = isoCode;
    }

    public void setName(String name) {
        if (name == null) {
            throw new ArgumentIsNullException("name");
        }
        if (name.length() > NAME_MAX_STRING_LENGTH) {
            StringBuilder exceptionMessageBuilder = new StringBuilder();
            exceptionMessageBuilder
                    .append("Length of name's string can not be more than ");
            exceptionMessageBuilder.append(NAME_MAX_STRING_LENGTH);
            exceptionMessageBuilder.append(" symbols.");
            throw new IllegalArgumentException(
                    exceptionMessageBuilder.toString());
        }
        this.name = name;
    }

    public void setSymbol(String symbol) {
        if (symbol == null) {
            throw new ArgumentIsNullException("symbol");
        }
        if (symbol.length() != SYMBOL_STRING_LENGTH) {
            throw new IllegalArgumentException(
                    "Currency's symbol should be exactly one character.");
        }
        this.symbol = symbol;
    }

    public void setComments(String comments) {
        if (comments != null && comments.length() > COMMENTS_MAX_STRING_LENGTH) {
            StringBuilder exceptionMessageBuilder = new StringBuilder();
            exceptionMessageBuilder
                    .append("Length of comments' string can not be more than ");
            exceptionMessageBuilder.append(COMMENTS_MAX_STRING_LENGTH);
            exceptionMessageBuilder.append(" symbols.");
            throw new IllegalArgumentException(
                    exceptionMessageBuilder.toString());
        }
        this.comments = comments;
    }

    private String defineRegularExpressionForIsoCode() {
        return "^[A-Z][A-Z][A-Z]$";
    }

    @Override
    public int compareTo(Currency currency) {
        return this.getIsoCode().compareTo(currency.getIsoCode());
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result =
                prime * result + ((comments == null) ? 0 : comments.hashCode());
        result = prime * result + ((isoCode == null) ? 0 : isoCode.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Currency other = (Currency) obj;
        if (comments == null) {
            if (other.comments != null)
                return false;
        } else if (!comments.equals(other.comments))
            return false;
        if (isoCode == null) {
            if (other.isoCode != null)
                return false;
        } else if (!isoCode.equals(other.isoCode))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (symbol == null) {
            if (other.symbol != null)
                return false;
        } else if (!symbol.equals(other.symbol))
            return false;
        return true;
    }
}

CurrencyEntity interface:

public interface CurrencyEntity {
    void setName(String name);

    String getName();

    void setIsoCode(String isoCode);

    String getIsoCode();

    void setSymbol(String symbol);

    String getSymbol();

    void setComments(String comments);

    String getComments();
}

which CurrencyEntityAtJpa implements for working with Hibernate:

@Entity
@Table(name = "currencies")
public class CurrencyEntityAtJpa implements CurrencyEntity {
    public static final int NAME_MAX_STRING_LENGTH =
            Currency.NAME_MAX_STRING_LENGTH;
    public static final int ISO_CODE_STRING_LENGTH =
            Currency.ISO_CODE_STRING_LENGTH;
    public static final int SYMBOL_STRING_LENGTH =
            Currency.SYMBOL_STRING_LENGTH;
    public static final int COMMENTS_MAX_STRING_LENGTH =
            Currency.COMMENTS_MAX_STRING_LENGTH;

    private Currency currency;
    private long id;

    public CurrencyEntityAtJpa() {
        setCurrency(new Currency("Invalid currency", "INV", "I", null));
    }

    public CurrencyEntityAtJpa(String name, String isoCode, String symbol,
            String comments) {
        setCurrency(new Currency(name, isoCode, symbol, comments));
    }

    public CurrencyEntityAtJpa(Currency currency) {
        this(currency.getName(), currency.getIsoCode(), currency.getSymbol(),
                currency.getComments());
    }

    @Transient
    public Currency getCurrency() {
        return currency;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column
    public long getId() {
        return id;
    }

    @Override
    @Column(name = "iso_code", nullable = false, length = ISO_CODE_STRING_LENGTH)
    public String getIsoCode() {
        return getCurrency().getIsoCode();
    }

    @Override
    @Column(name = "name", nullable = false, length = NAME_MAX_STRING_LENGTH)
    public String getName() {
        return getCurrency().getName();
    }

    @Override
    @Column(name = "symbol", nullable = false, length = SYMBOL_STRING_LENGTH)
    public String getSymbol() {
        return getCurrency().getSymbol();
    }

    @Override
    @Column(name = "comments", nullable = true, length = COMMENTS_MAX_STRING_LENGTH)
    public String getComments() {
        return getCurrency().getComments();
    }

    private void setCurrency(Currency currency) {
        this.currency = currency;
    }

    public void setId(long id) {
        this.id = id;
    }

    @Override
    public void setIsoCode(String isoCode) {
        getCurrency().setIsoCode(isoCode);
    }

    @Override
    public void setName(String name) {
        getCurrency().setName(name);
    }

    @Override
    public void setSymbol(String symbol) {
        getCurrency().setSymbol(symbol);
    }

    @Override
    public void setComments(String comments) {
        getCurrency().setComments(comments);
    }

    @Override
    public int hashCode() {
        return getCurrency().hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof CurrencyEntityAtJpa)) {
            throw new IllegalArgumentException(obj.toString()
                    + " is not CurrencyEntityAtJPA object.");
        }
        return getCurrency().equals(((CurrencyEntityAtJpa) obj).getCurrency());
    }
}

Is this a good design for Hibernate entity where I want to separate business logic and classes which work with database?

\$\endgroup\$
2
\$\begingroup\$

I don't see too much of an advantage in separating like this. Especially when considering Basic Java Persistence API Best Practices, Access Fields Rather Than Properties:

I prefer to specify object-relational mapping by annotating entity fields directly, rather than annotating get/set methods (properties), for several reasons.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.