zoukankan      html  css  js  c++  java
  • JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-007UserTypes的用法(@org.hibernate.annotations.Type、@org.hibernate.annotations.TypeDefs、CompositeUserType、DynamicParameterizedType、、、)

    一、结构

    二、Hibernate支持的UserTypes接口

     UserType —You can transform values by interacting with the plain JDBC PreparedStatement (when storing data) and ResultSet (when loading data).By implementing this interface, you can also control how Hibernate caches and
    dirty-checks values. The adapter for MonetaryAmount has to implement this interface.
     CompositeUserType —This extends UserType , providing Hibernate with more details about your adapted class. You can tell Hibernate that the MonetaryAmount component has two properties: amount and currency . You can then reference these properties in queries with dot notation: for example, select avg(i.buyNowPrice.amount) from Item i .
     ParameterizedUserType —This provides settings to your adapter in mappings.You have to implement this interface for the MonetaryAmount conversion,because in some mappings you want to convert the amount to US dollars and in
    other mappings to Euros. You only have to write a single adapter and can customize its behavior when mapping a property.
     DynamicParameterizedType —This more powerful settings API gives you access to dynamic information in the adapter, such as the mapped column and table names. You might as well use this instead of ParameterizedUserType ; there is no additional cost or complexity.
     EnhancedUserType —This is an optional interface for adapters of identifier properties and discriminators. Unlike JPA converters, a UserType in Hibernate can be an adapter for any kind of entity property. Because MonetaryAmount won’t be the type of an identifier property or discriminator, you won’t need it.
     UserVersionType —This is an optional interface for adapters of version properties.
     UserCollectionType —This rarely needed interface is used to implement custom collections. You have to implement it to persist a non- JDK collection and preserve additional semantics.

    三、代码

    1.

      1 package org.jpwh.converter;
      2 
      3 import org.hibernate.engine.spi.SessionImplementor;
      4 import org.hibernate.type.StandardBasicTypes;
      5 import org.hibernate.type.Type;
      6 import org.hibernate.usertype.CompositeUserType;
      7 import org.hibernate.usertype.DynamicParameterizedType;
      8 import org.jpwh.model.advanced.MonetaryAmount;
      9 
     10 import java.io.Serializable;
     11 import java.lang.annotation.Annotation;
     12 import java.math.BigDecimal;
     13 import java.sql.PreparedStatement;
     14 import java.sql.ResultSet;
     15 import java.sql.SQLException;
     16 import java.util.Currency;
     17 import java.util.Properties;
     18 
     19 public class MonetaryAmountUserType
     20     implements CompositeUserType, DynamicParameterizedType {
     21 
     22     protected Currency convertTo;
     23 
     24     public void setParameterValues(Properties parameters) {
     25 
     26         /**
     27          * You can access some dynamic parameters here, such as the
     28          * name of the mapped columns, the mapped (entity) table, or even the
     29          * annotations on the field/getter of the mapped property. We won't need
     30          * them in this example though.
     31          */
     32         ParameterType parameterType =
     33             (ParameterType) parameters.get(PARAMETER_TYPE);
     34         String[] columns = parameterType.getColumns();
     35         String table = parameterType.getTable();
     36         Annotation[] annotations = parameterType.getAnnotationsMethod();
     37 
     38         /**
     39          * We only use the <code>convertTo</code> parameter to
     40          * determine the target currency when saving a value into the database.
     41          * If the parameter hasn't been set, we default to US Dollar.
     42          */
     43         String convertToParameter = parameters.getProperty("convertTo");
     44         this.convertTo = Currency.getInstance(
     45             convertToParameter != null ? convertToParameter : "USD"
     46         );
     47     }
     48 
     49     /**
     50      * The method <code>returnedClass</code> adapts the given class, in this case
     51      * <code>MonetaryAmount</code>.
     52      */
     53     public Class returnedClass() {
     54         return MonetaryAmount.class;
     55     }
     56 
     57     /**
     58      * Hibernate can enable some optimizations if it knows
     59      * that <code>MonetaryAmount</code> is immutable.
     60      */
     61     public boolean isMutable() {
     62         return false;
     63     }
     64 
     65     /**
     66      * If Hibernate has to make a copy of the value, it will call
     67      * this method. For simple immutable classes like <code>MonetaryAmount</code>,
     68      * you can return the given instance.
     69      */
     70     public Object deepCopy(Object value) {
     71         return value;
     72     }
     73 
     74     /**
     75      * Hibernate calls <code>disassemble</code> when it stores a value in the global shared second-level
     76      * cache. You need to return a <code>Serializable</code> representation. For <code>MonetaryAmount</code>,
     77      * a <code>String</code> representation is an easy solution. Or, because <code>MonetaryAmount</code> is actually
     78      * <code>Serializable</code>, you could return it directly.
     79      */
     80     public Serializable disassemble(Object value,
     81                                     SessionImplementor session) {
     82         return value.toString();
     83     }
     84 
     85     /**
     86      * Hibernate calls this method when it reads the serialized
     87      * representation from the global shared second-level cache. We create a
     88      * <code>MonetaryAmount</code> instance from the <code>String</code>
     89      * representation. Or, if have stored a serialized <code>MonetaryAmount</code>,
     90      * you could return it directly.
     91      */
     92     public Object assemble(Serializable cached,
     93                            SessionImplementor session, Object owner) {
     94         return MonetaryAmount.fromString((String) cached);
     95     }
     96 
     97     /**
     98      * Called during <code>EntityManager#merge()</code> operations, you
     99      * need to return a copy of the <code>original</code>. Or, if your value type is
    100      * immutable, like <code>MonetaryAmount</code>, you can simply return the original.
    101      */
    102     public Object replace(Object original, Object target,
    103                           SessionImplementor session, Object owner) {
    104         return original;
    105     }
    106 
    107     /**
    108      * Hibernate will use value equality to determine whether the value
    109      * was changed, and the database needs to be updated. We rely on the equality
    110      * routine we have already written on the <code>MonetaryAmount</code> class.
    111      */
    112     public boolean equals(Object x, Object y) {
    113         return x == y || !(x == null || y == null) && x.equals(y);
    114     }
    115 
    116     public int hashCode(Object x) {
    117         return x.hashCode();
    118     }
    119 
    120     /**
    121      * Called to read the <code>ResultSet</code>, when a
    122      * <code>MonetaryAmount</code> value has to be retrieved from the database.
    123      * We take the <code>amount</code> and <code>currency</code> values as given
    124      * in the query result, and create a new instance of <code>MonetaryAmount</code>.
    125      */
    126     public Object nullSafeGet(ResultSet resultSet,
    127                               String[] names,
    128                               SessionImplementor session,
    129                               Object owner) throws SQLException {
    130 
    131         BigDecimal amount = resultSet.getBigDecimal(names[0]);
    132         if (resultSet.wasNull())
    133             return null;
    134         Currency currency =
    135             Currency.getInstance(resultSet.getString(names[1]));
    136         return new MonetaryAmount(amount, currency);
    137     }
    138 
    139     /**
    140      * Called when a <code>MonetaryAmount</code> value has
    141      * to be stored in the database. We convert the value to the target currency,
    142      * then set the <code>amount</code> and <code>currency</code> on the
    143      * provided <code>PreparedStatement</code>. (Unless the <code>MonetaryAmount</code>
    144      * was <code>null</code>, in that case, we call <code>setNull()</code> to
    145      * prepare the statement.)
    146      */
    147     public void nullSafeSet(PreparedStatement statement,
    148                             Object value,
    149                             int index,
    150                             SessionImplementor session) throws SQLException {
    151 
    152         if (value == null) {
    153             statement.setNull(
    154                 index,
    155                 StandardBasicTypes.BIG_DECIMAL.sqlType());
    156             statement.setNull(
    157                 index + 1,
    158                 StandardBasicTypes.CURRENCY.sqlType());
    159         } else {
    160             MonetaryAmount amount = (MonetaryAmount) value;
    161             // When saving, convert to target currency
    162             MonetaryAmount dbAmount = convert(amount, convertTo);
    163             statement.setBigDecimal(index, dbAmount.getValue());
    164             statement.setString(index + 1, convertTo.getCurrencyCode());
    165         }
    166     }
    167 
    168     /**
    169      * Here you can implement whatever currency conversion routine
    170      * you need. For the sake of the example, we simply double the value so we
    171      * can easily test if conversion was successful. You'll have to replace this
    172      * code with a real currency converter in a real application. It's not a
    173      * method of the Hibernate <code>UserType</code> API.
    174      */
    175     protected MonetaryAmount convert(MonetaryAmount amount,
    176                                      Currency toCurrency) {
    177         return new MonetaryAmount(
    178             amount.getValue().multiply(new BigDecimal(2)),
    179             toCurrency
    180         );
    181     }
    182 
    183     public String[] getPropertyNames() {
    184         return new String[]{"value", "currency"};
    185     }
    186 
    187     public Type[] getPropertyTypes() {
    188         return new Type[]{
    189             StandardBasicTypes.BIG_DECIMAL,
    190             StandardBasicTypes.CURRENCY
    191         };
    192     }
    193 
    194     public Object getPropertyValue(Object component,
    195                                    int property) {
    196         MonetaryAmount monetaryAmount = (MonetaryAmount) component;
    197         if (property == 0)
    198             return monetaryAmount.getValue();
    199         else
    200             return monetaryAmount.getCurrency();
    201     }
    202 
    203     public void setPropertyValue(Object component,
    204                                  int property,
    205                                  Object value) {
    206         throw new UnsupportedOperationException(
    207             "MonetaryAmount is immutable"
    208         );
    209     }
    210 
    211     // ...
    212 }

    2.

     1 @org.hibernate.annotations.TypeDefs({
     2     @org.hibernate.annotations.TypeDef(
     3         name = "monetary_amount_usd",
     4         typeClass = MonetaryAmountUserType.class,
     5         parameters = {@Parameter(name = "convertTo", value = "USD")}
     6     ),
     7     @org.hibernate.annotations.TypeDef(
     8         name = "monetary_amount_eur",
     9         typeClass = MonetaryAmountUserType.class,
    10         parameters = {@Parameter(name = "convertTo", value = "EUR")}
    11     )
    12 })
    13 package org.jpwh.converter;
    14 
    15 import org.hibernate.annotations.Parameter;

    3.

     1 package org.jpwh.model.advanced.usertype;
     2 
     3 import org.jpwh.model.advanced.MonetaryAmount;
     4 
     5 import javax.persistence.Column;
     6 import javax.persistence.Entity;
     7 import javax.persistence.GeneratedValue;
     8 import javax.persistence.Id;
     9 import javax.validation.constraints.NotNull;
    10 
    11 @Entity
    12 public class Item {
    13 
    14     @Id
    15     @GeneratedValue(generator = "ID_GENERATOR")
    16     protected Long id;
    17 
    18     @NotNull
    19     protected String name;
    20 
    21     @NotNull
    22     @org.hibernate.annotations.Type(
    23         type = "monetary_amount_usd"
    24     )
    25     @org.hibernate.annotations.Columns(columns = {
    26         @Column(name = "BUYNOWPRICE_AMOUNT"),
    27         @Column(name = "BUYNOWPRICE_CURRENCY", length = 3)
    28     })
    29     protected MonetaryAmount buyNowPrice;
    30 
    31     @NotNull
    32     @org.hibernate.annotations.Type(
    33         type = "monetary_amount_eur"
    34     )
    35     @org.hibernate.annotations.Columns(columns = {
    36         @Column(name = "INITIALPRICE_AMOUNT"),
    37         @Column(name = "INITIALPRICE_CURRENCY", length = 3)
    38     })
    39     protected MonetaryAmount initialPrice;
    40 
    41     public Long getId() {
    42         return id;
    43     }
    44 
    45     public String getName() {
    46         return name;
    47     }
    48 
    49     public void setName(String name) {
    50         this.name = name;
    51     }
    52 
    53     public MonetaryAmount getBuyNowPrice() {
    54         return buyNowPrice;
    55     }
    56 
    57     public void setBuyNowPrice(MonetaryAmount buyNowPrice) {
    58         this.buyNowPrice = buyNowPrice;
    59     }
    60 
    61     public MonetaryAmount getInitialPrice() {
    62         return initialPrice;
    63     }
    64 
    65     public void setInitialPrice(MonetaryAmount initialPrice) {
    66         this.initialPrice = initialPrice;
    67     }
    68 
    69     // ...
    70 }

    4.

     1 package org.jpwh.model.advanced;
     2 
     3 import java.io.Serializable;
     4 import java.math.BigDecimal;
     5 import java.util.Currency;
     6 
     7 /* 
     8    This value-typed class should be <code>java.io.Serializable</code>: When Hibernate stores entity
     9    instance data in the shared second-level cache (see <a href="#Caching"/>), it <em>disassembles</em>
    10    the entity's state. If an entity has a <code>MonetaryAmount</code> property, the serialized
    11    representation of the property value will be stored in the second-level cache region. When entity
    12    data is retrieved from the cache region, the property value will be deserialized and reassembled.
    13  */
    14 public class MonetaryAmount implements Serializable {
    15 
    16     /*
    17  The class does not need a special constructor, you can make it immutable, even with
    18         <code>final</code> fields, as your code will be the only place an instance is created.
    19      */
    20     protected final BigDecimal value;
    21     protected final Currency currency;
    22 
    23     public MonetaryAmount(BigDecimal value, Currency currency) {
    24         this.value = value;
    25         this.currency = currency;
    26     }
    27 
    28     public BigDecimal getValue() {
    29         return value;
    30     }
    31 
    32     public Currency getCurrency() {
    33         return currency;
    34     }
    35 
    36     /*
    37  You should implement the <code>equals()</code> and <code>hashCode()</code>
    38         methods, and compare monetary amounts "by value".
    39      */
    40     public boolean equals(Object o) {
    41         if (this == o) return true;
    42         if (!(o instanceof MonetaryAmount)) return false;
    43 
    44         final MonetaryAmount monetaryAmount = (MonetaryAmount) o;
    45 
    46         if (!value.equals(monetaryAmount.value)) return false;
    47         if (!currency.equals(monetaryAmount.currency)) return false;
    48 
    49         return true;
    50     }
    51 
    52     public int hashCode() {
    53         int result;
    54         result = value.hashCode();
    55         result = 29 * result + currency.hashCode();
    56         return result;
    57     }
    58 
    59     /*
    60  You will need a <code>String</code> representation of a monetary
    61         amount. Implement the <code>toString()</code> method and a static method to
    62         create an instance from a <code>String</code>.
    63      */
    64     public String toString() {
    65         return getValue() + " " + getCurrency();
    66     }
    67 
    68     public static MonetaryAmount fromString(String s) {
    69         String[] split = s.split(" ");
    70         return new MonetaryAmount(
    71             new BigDecimal(split[0]),
    72             Currency.getInstance(split[1])
    73         );
    74     }
    75 }

    5.

  • 相关阅读:
    Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1288372549423476738' for key 'PRIMARY'
    环形数组循环
    less命令
    ln命令
    Vue中$refs的理解
    cut命令
    除数博弈
    find命令
    file命令
    最长公共前缀
  • 原文地址:https://www.cnblogs.com/shamgod/p/5364801.html
Copyright © 2011-2022 走看看