2012-12-28 72 views
0

我是Hibernate的新手。我使用Hibernate 4.0,特別是JPA註釋。休眠 - 堅持一個地圖

我有一個訂單類。一個訂單可以包含多個Skus('sku'是一個庫存單位 - 見Wikipedia)以及一個或多個sku。 Order和Sku類在下面。我想在相同的表格中將訂單與其關聯的skus保持一致,但如果需要,我可以使用兩個表格。我無法弄清楚如何保持HashMap - 運行時異常拋出狀態javax.persistence.PersistenceException: org.hibernate.exception.DataException: Data truncation: Data too long for column 'skusInOrder' at row 1

我已閱讀[this] [2] Stackoverflow問題: - 使用@Lob註釋的建議在我的場景中是否有意義?我是否需要使用@Embedded或@Embeddable和/或@ElementCollection?

package com.newco.drinks.data; 

import java.io.Serializable; 
import java.math.RoundingMode; 
import java.util.ConcurrentModificationException; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import org.joda.money.CurrencyUnit; 
import org.joda.money.Money; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.MapKey; 
import javax.persistence.OneToMany; 
import javax.persistence.SecondaryTable; 
import javax.persistence.Table; 
import javax.persistence.Temporal; 
import javax.persistence.TemporalType; 
import javax.persistence.Transient; 

@Entity 
@Table(name="Orders") 
public class Order implements Serializable { 

    private static final long serialVersionUID = 5915005362337363656L; 
    private List<Sku> availableSkus; 
    private String barId; 
    private java.util.Date collectionNotificationTime; 
    private CurrencyUnit currencyUnit; 
    private String customerId; 
    private String orderId; 
    private Money orderTotal; 
    private String paymentProcessorRef; 
    private Boolean paymentSuccessful; 
    private HashMap<Sku, Integer> skusInOrder = new HashMap<Sku, Integer>(); // skuId and quantity thereof 
    private java.util.Date timeReceived; 

    /** 
    * 
    * @param sku 
    * @param quantity 
    * @throws IllegalArgumentException 
    *    if quantity is zero or negative, or if the Order does not 
    *    contain the Sku 
    * @throws ConcurrentModificationException 
    */ 
    void add(Sku sku, int quantity) throws IllegalArgumentException, ConcurrentModificationException { 

     if (quantity <= 0) { 
      throw new IllegalArgumentException("Quantity must be greater than zero, but was " + quantity); 
     } 

     if (skusInOrder.isEmpty() || !skusInOrder.containsKey(sku)) { 
      skusInOrder.put(sku, quantity); 
     } else { 
      if (skusInOrder.containsKey(sku)) { 
       int i = skusInOrder.get(sku); 
       skusInOrder.put(sku, i + quantity); 
      } else { 
       throw new IllegalArgumentException("Order " + getOrderId() + " does not contain SKU " + sku.getSkuId()); 
      } 
     } 
    } 

    private Money calculateOrderTotal() { 

     int decimalPlaces = currencyUnit.getDecimalPlaces(); 
     String zeroString = null; 

     switch (decimalPlaces) { 
     case 1: 
      zeroString = "0.0"; 
      break; 
     case 2: 
      zeroString = "0.00"; 
      break; 
     case 3: 
      zeroString = "0.000"; 
      break; 
     case 4: 
      zeroString = "0.0000"; 
      break; 
     } 

     Money total = Money.of(currencyUnit, new Double(zeroString)); 

     for (Sku sku : skusInOrder.keySet()) { 

      int quantity = skusInOrder.get(sku); 
      Money totalExTax = sku.getPrice().multipliedBy(quantity, RoundingMode.HALF_UP); 

      double taxRate = sku.getTaxRate(); 
      if (taxRate > 0) { 
       Money tax = totalExTax.multipliedBy(taxRate/100, RoundingMode.HALF_UP); 
       total = total.plus(totalExTax.plus(tax)); 
      } else { 
       total = total.plus(totalExTax); 
      } 
     } 

     return total; 
    } 

    /** 
    * 
    * @return a List of Sku objects available to be added to this order. 
    *   Different bars will have different Skus available. 
    */ 
    @Transient 
    @OneToMany 
    public List<Sku> getAvailableSkus() { 
     return availableSkus; 
    } 

    @Column(name="barid", nullable=false) 
    public String getBarId() { 
     return barId; 
    } 

    @Temporal(TemporalType.TIMESTAMP) 
    @Column(name = "collectionnotificationtime") 
    public java.util.Date getCollectionNotificationTime() { 
     return collectionNotificationTime; 
    } 

    @Column(name="currencyunit", nullable=false) 
    public CurrencyUnit getCurrencyUnit() { 
     return currencyUnit; 
    } 

    @Column(name="customerid", nullable=false) 
    public String getCustomerId() { 
     return customerId; 
    } 

    @Id 
    @Column(name="orderid", nullable=false) 
    public String getOrderId() { 
     return orderId; 
    } 

    //@Column(name="ordertotal", nullable=false) 
    @Transient 
    public Money getOrderTotal() { 
     return calculateOrderTotal(); 
    } 

    @Column(name="paymentprocessorref", nullable=true) 
    public String getPaymentProcessorRef() { 
     return paymentProcessorRef; 
    } 

    @Column(name="paymentsuccess", nullable=true) 
    public Boolean getPaymentSuccessful() { 
     return paymentSuccessful; 
    } 

    @Column(name="skusinorder", nullable=false) 
    public HashMap<Sku, Integer> getSkusInOrder() { 
     return skusInOrder; 
    } 

    @Temporal(TemporalType.TIMESTAMP) 
    @Column(name = "timereceived") 
    public java.util.Date getTimeReceived() { 
     return timeReceived; 
    } 

    /** 
    * 
    * @param sku 
    * @param quantity 
    * @throws IllegalArgumentException 
    *    if quantity is zero or negative, or if the Sku does not form 
    *    part of the Order 
    * @throws ConcurrentModificationException 
    */ 
    void remove(Sku sku, int quantity) throws IllegalArgumentException, ConcurrentModificationException { 

     if (quantity <= 0) { 
      throw new IllegalArgumentException("Quantity to remove must be greater than zero for order " + getOrderId() + ", but was " + quantity); 
     } 

     if (skusInOrder.isEmpty() || !skusInOrder.containsKey(sku)) { 
      throw new IllegalArgumentException("Cannot remove sku " + sku.getSkuId() + " which doesn't form part of order " + getOrderId()); 
     } else { 
      int i = skusInOrder.get(sku); 
      if (quantity <= i) { // fine, this is expected 
       skusInOrder.put(sku, i - quantity); 
      } else if (quantity == i) { //okay, remove that sku altogether as its quantity is now zero 
       skusInOrder.remove(sku); 
      } 
     } 
    } 

    void setAvailableSkus(List<Sku> availableSkus) { 
     this.availableSkus = availableSkus; 
    } 

    void setBarId(String barId) { 
     this.barId = barId; 
    } 

    void setCollectionNotificationTime(
      java.util.Date collectionNotificationTime) { 
     this.collectionNotificationTime = collectionNotificationTime; 
    } 

    void setCollectionReadySent(java.util.Date collectionReadySent) { 
     this.collectionNotificationTime = collectionReadySent; 
    } 

    void setCurrencyUnit(CurrencyUnit currencyUnit) { 
     this.currencyUnit = currencyUnit; 
    } 

    void setCustomerId(String customerId) { 
     this.customerId = customerId; 
    } 

    void setOrderId(String orderId) { 
     this.orderId = orderId; 
    } 

    void setOrderTotal(){ 
     orderTotal = calculateOrderTotal(); 
    } 

    void setPaymentProcessorRef(String paymentProcessorRef) { 
     this.paymentProcessorRef = paymentProcessorRef; 
    } 

    void setPaymentSuccessful(Boolean paymentSuccessful) { 
     this.paymentSuccessful = paymentSuccessful; 
    } 
} 


package com.newco.drinks.data; 

import java.io.Serializable; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.Id; 
import javax.persistence.Table; 

import org.joda.money.CurrencyUnit; 
import org.joda.money.Money; 

@Entity 
@Table(name = "Skus") 
public class Sku implements Serializable{ 

    private static final long serialVersionUID = 8375466982619713795L; 
    private String skuId; 
    private String barId; 
    private CurrencyUnit currencyUnit; 
    private double price; 
    private String description; 
    private String brand; 
    private String skuType; 
    private double taxRate; 

    protected Sku(){ 
     // no args constructor for Hibernate use 
    } 

    public Sku(String skuId, String barId, CurrencyUnit currencyUnit, double price, String description, String brand, String skuType, double tax) { 
     setSkuId(skuId); 
     setBarId(barId); 
     setCurrencyUnit(currencyUnit); 
     setPrice(price); 
     setDescription(description); 
     setBrand(brand); 
     setSkuType(skuType); 
     setTaxRate(tax); 
    } 

    @Column(name="barid", nullable=false) 
    public String getBarId() { 
     return barId; 
    } 

    @Column(name="brand", nullable=true) 
    public String getBrand() { 
     return brand; 
    } 

    @Column(name="currencyunit", nullable=false) 
    public CurrencyUnit getCurrencyUnit() { 
     return currencyUnit; 
    } 

    @Column(name="description", nullable=false) 
    public String getDescription() { 
     return description; 
    } 

    @Column(name="price", nullable=false) 
    public Money getPrice() { 
     return Money.of(currencyUnit, price); 
    } 

    @Id 
    @Column(name="skuid", nullable=false) 
    public String getSkuId() { 
     return skuId; 
    } 

    @Column(name="skutype", nullable=false) 
    public String getSkuType() { 
     return skuType; 
    } 

    @Column(name="taxrate", nullable=false) 
    public double getTaxRate() { 
     return taxRate; 
    } 

    void setBarId(String barId) { 
     this.barId = barId; 
    } 

    void setBrand(String brand) { 
     this.brand = brand; 
    } 

    void setCurrencyUnit(CurrencyUnit currencyUnit) { 
     this.currencyUnit = currencyUnit; 
    } 

    void setDescription(String description) { 
     this.description = description; 
    } 

    void setPrice(double price) { 
     this.price = price; 
    } 

    void setSkuId(String skuId) { 
     this.skuId = skuId; 
    } 

    void setSkuType(String skuType) { 
     this.skuType = skuType; 
    } 

    void setTaxRate(double tax) { 
     this.taxRate = tax; 
    } 



} 

[2]:How to persist a HashMap with hibernate,整型

回答

2

首先,定義字段是Map而非HashMap型的(總是傾向於具體類接口用於引用的對象)。

那麼最好的選擇是使用@ElementCollection。這樣,hibernate將創建(如果啓用hbm2ddl)一個單獨的表並在那裏填充結果。如果你使用的是@Lob它仍然可以工作,但是當在java之外讀取時(即命令行mysql工具),該字段將是「黑盒子」