2012-12-28 74 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工具),该字段将是“黑盒子”