一、结构

二、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.

 package org.jpwh.converter;

 import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.DynamicParameterizedType;
import org.jpwh.model.advanced.MonetaryAmount; import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Currency;
import java.util.Properties; public class MonetaryAmountUserType
implements CompositeUserType, DynamicParameterizedType { protected Currency convertTo; public void setParameterValues(Properties parameters) { /**
* You can access some dynamic parameters here, such as the
* name of the mapped columns, the mapped (entity) table, or even the
* annotations on the field/getter of the mapped property. We won't need
* them in this example though.
*/
ParameterType parameterType =
(ParameterType) parameters.get(PARAMETER_TYPE);
String[] columns = parameterType.getColumns();
String table = parameterType.getTable();
Annotation[] annotations = parameterType.getAnnotationsMethod(); /**
* We only use the <code>convertTo</code> parameter to
* determine the target currency when saving a value into the database.
* If the parameter hasn't been set, we default to US Dollar.
*/
String convertToParameter = parameters.getProperty("convertTo");
this.convertTo = Currency.getInstance(
convertToParameter != null ? convertToParameter : "USD"
);
} /**
* The method <code>returnedClass</code> adapts the given class, in this case
* <code>MonetaryAmount</code>.
*/
public Class returnedClass() {
return MonetaryAmount.class;
} /**
* Hibernate can enable some optimizations if it knows
* that <code>MonetaryAmount</code> is immutable.
*/
public boolean isMutable() {
return false;
} /**
* If Hibernate has to make a copy of the value, it will call
* this method. For simple immutable classes like <code>MonetaryAmount</code>,
* you can return the given instance.
*/
public Object deepCopy(Object value) {
return value;
} /**
* Hibernate calls <code>disassemble</code> when it stores a value in the global shared second-level
* cache. You need to return a <code>Serializable</code> representation. For <code>MonetaryAmount</code>,
* a <code>String</code> representation is an easy solution. Or, because <code>MonetaryAmount</code> is actually
* <code>Serializable</code>, you could return it directly.
*/
public Serializable disassemble(Object value,
SessionImplementor session) {
return value.toString();
} /**
* Hibernate calls this method when it reads the serialized
* representation from the global shared second-level cache. We create a
* <code>MonetaryAmount</code> instance from the <code>String</code>
* representation. Or, if have stored a serialized <code>MonetaryAmount</code>,
* you could return it directly.
*/
public Object assemble(Serializable cached,
SessionImplementor session, Object owner) {
return MonetaryAmount.fromString((String) cached);
} /**
* Called during <code>EntityManager#merge()</code> operations, you
* need to return a copy of the <code>original</code>. Or, if your value type is
* immutable, like <code>MonetaryAmount</code>, you can simply return the original.
*/
public Object replace(Object original, Object target,
SessionImplementor session, Object owner) {
return original;
} /**
* Hibernate will use value equality to determine whether the value
* was changed, and the database needs to be updated. We rely on the equality
* routine we have already written on the <code>MonetaryAmount</code> class.
*/
public boolean equals(Object x, Object y) {
return x == y || !(x == null || y == null) && x.equals(y);
} public int hashCode(Object x) {
return x.hashCode();
} /**
* Called to read the <code>ResultSet</code>, when a
* <code>MonetaryAmount</code> value has to be retrieved from the database.
* We take the <code>amount</code> and <code>currency</code> values as given
* in the query result, and create a new instance of <code>MonetaryAmount</code>.
*/
public Object nullSafeGet(ResultSet resultSet,
String[] names,
SessionImplementor session,
Object owner) throws SQLException { BigDecimal amount = resultSet.getBigDecimal(names[0]);
if (resultSet.wasNull())
return null;
Currency currency =
Currency.getInstance(resultSet.getString(names[1]));
return new MonetaryAmount(amount, currency);
} /**
* Called when a <code>MonetaryAmount</code> value has
* to be stored in the database. We convert the value to the target currency,
* then set the <code>amount</code> and <code>currency</code> on the
* provided <code>PreparedStatement</code>. (Unless the <code>MonetaryAmount</code>
* was <code>null</code>, in that case, we call <code>setNull()</code> to
* prepare the statement.)
*/
public void nullSafeSet(PreparedStatement statement,
Object value,
int index,
SessionImplementor session) throws SQLException { if (value == null) {
statement.setNull(
index,
StandardBasicTypes.BIG_DECIMAL.sqlType());
statement.setNull(
index + 1,
StandardBasicTypes.CURRENCY.sqlType());
} else {
MonetaryAmount amount = (MonetaryAmount) value;
// When saving, convert to target currency
MonetaryAmount dbAmount = convert(amount, convertTo);
statement.setBigDecimal(index, dbAmount.getValue());
statement.setString(index + 1, convertTo.getCurrencyCode());
}
} /**
* Here you can implement whatever currency conversion routine
* you need. For the sake of the example, we simply double the value so we
* can easily test if conversion was successful. You'll have to replace this
* code with a real currency converter in a real application. It's not a
* method of the Hibernate <code>UserType</code> API.
*/
protected MonetaryAmount convert(MonetaryAmount amount,
Currency toCurrency) {
return new MonetaryAmount(
amount.getValue().multiply(new BigDecimal(2)),
toCurrency
);
} public String[] getPropertyNames() {
return new String[]{"value", "currency"};
} public Type[] getPropertyTypes() {
return new Type[]{
StandardBasicTypes.BIG_DECIMAL,
StandardBasicTypes.CURRENCY
};
} public Object getPropertyValue(Object component,
int property) {
MonetaryAmount monetaryAmount = (MonetaryAmount) component;
if (property == 0)
return monetaryAmount.getValue();
else
return monetaryAmount.getCurrency();
} public void setPropertyValue(Object component,
int property,
Object value) {
throw new UnsupportedOperationException(
"MonetaryAmount is immutable"
);
} // ...
}

2.

 @org.hibernate.annotations.TypeDefs({
@org.hibernate.annotations.TypeDef(
name = "monetary_amount_usd",
typeClass = MonetaryAmountUserType.class,
parameters = {@Parameter(name = "convertTo", value = "USD")}
),
@org.hibernate.annotations.TypeDef(
name = "monetary_amount_eur",
typeClass = MonetaryAmountUserType.class,
parameters = {@Parameter(name = "convertTo", value = "EUR")}
)
})
package org.jpwh.converter; import org.hibernate.annotations.Parameter;

3.

 package org.jpwh.model.advanced.usertype;

 import org.jpwh.model.advanced.MonetaryAmount;

 import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull; @Entity
public class Item { @Id
@GeneratedValue(generator = "ID_GENERATOR")
protected Long id; @NotNull
protected String name; @NotNull
@org.hibernate.annotations.Type(
type = "monetary_amount_usd"
)
@org.hibernate.annotations.Columns(columns = {
@Column(name = "BUYNOWPRICE_AMOUNT"),
@Column(name = "BUYNOWPRICE_CURRENCY", length = 3)
})
protected MonetaryAmount buyNowPrice; @NotNull
@org.hibernate.annotations.Type(
type = "monetary_amount_eur"
)
@org.hibernate.annotations.Columns(columns = {
@Column(name = "INITIALPRICE_AMOUNT"),
@Column(name = "INITIALPRICE_CURRENCY", length = 3)
})
protected MonetaryAmount initialPrice; public Long getId() {
return id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public MonetaryAmount getBuyNowPrice() {
return buyNowPrice;
} public void setBuyNowPrice(MonetaryAmount buyNowPrice) {
this.buyNowPrice = buyNowPrice;
} public MonetaryAmount getInitialPrice() {
return initialPrice;
} public void setInitialPrice(MonetaryAmount initialPrice) {
this.initialPrice = initialPrice;
} // ...
}

4.

 package org.jpwh.model.advanced;

 import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Currency; /*
This value-typed class should be <code>java.io.Serializable</code>: When Hibernate stores entity
instance data in the shared second-level cache (see <a href="#Caching"/>), it <em>disassembles</em>
the entity's state. If an entity has a <code>MonetaryAmount</code> property, the serialized
representation of the property value will be stored in the second-level cache region. When entity
data is retrieved from the cache region, the property value will be deserialized and reassembled.
*/
public class MonetaryAmount implements Serializable { /*
The class does not need a special constructor, you can make it immutable, even with
<code>final</code> fields, as your code will be the only place an instance is created.
*/
protected final BigDecimal value;
protected final Currency currency; public MonetaryAmount(BigDecimal value, Currency currency) {
this.value = value;
this.currency = currency;
} public BigDecimal getValue() {
return value;
} public Currency getCurrency() {
return currency;
} /*
You should implement the <code>equals()</code> and <code>hashCode()</code>
methods, and compare monetary amounts "by value".
*/
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MonetaryAmount)) return false; final MonetaryAmount monetaryAmount = (MonetaryAmount) o; if (!value.equals(monetaryAmount.value)) return false;
if (!currency.equals(monetaryAmount.currency)) return false; return true;
} public int hashCode() {
int result;
result = value.hashCode();
result = 29 * result + currency.hashCode();
return result;
} /*
You will need a <code>String</code> representation of a monetary
amount. Implement the <code>toString()</code> method and a static method to
create an instance from a <code>String</code>.
*/
public String toString() {
return getValue() + " " + getCurrency();
} public static MonetaryAmount fromString(String s) {
String[] split = s.split(" ");
return new MonetaryAmount(
new BigDecimal(split[0]),
Currency.getInstance(split[1])
);
}
}

5.

JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-007UserTypes的用法(@org.hibernate.annotations.Type、@org.hibernate.annotations.TypeDefs、CompositeUserType、DynamicParameterizedType、、、)的更多相关文章

  1. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-006类型转换器( @Converter(autoApply = true) 、type="converter:qualified.ConverterName" )

    一.结构 二.代码 1. package org.jpwh.model.advanced; import java.io.Serializable; import java.math.BigDecim ...

  2. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-001Mapping basic properties(@Basic、@Access、access="noop"、@Formula、@ColumnTransformer、@Generated、 @ColumnDefaul、@Temporal、@Enumerated)

    一.简介 在JPA中,默认所有属性都会persist,属性要属于以下3种情况,Hibernate在启动时会报错 1.java基本类型或包装类 2.有注解 @Embedded 3.有实现java.io. ...

  3. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-005控制类型映射(Nationalized、@LOB、@org.hibernate.annotations.Type)

    一.简介 1. 2. 3. 4. to override this default mapping. The JPA specification has a convenient shortcut a ...

  4. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-004嵌套组件的注解AttributeOverrides

    一.数据库 二.代码 1. package org.jpwh.model.advanced; import javax.persistence.AttributeOverride; import ja ...

  5. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-003使用@AttributeOverrides

    Each @AttributeOverride for a component property is “complete”: any JPA or Hibernate annotation on t ...

  6. JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-002使用@Embeddable

    一.数据库 二.代码 1. package org.jpwh.model.simple; import javax.persistence.Column; import javax.persisten ...

  7. JavaPersistenceWithHibernate第二版笔记-第四章-Mapping persistent classes-003映射实体时的可选操作(<delimited-identifiers/>、PhysicalNamingStrategy、PhysicalNamingStrategyStandardImpl、、、)

    一.自定义映射的表名 1. @Entity @Table(name = "USERS") public class User implements Serializable { / ...

  8. JavaPersistenceWithHibernate第二版笔记-第六章-Mapping inheritance-002Table per concrete class with implicit polymorphism(@MappedSuperclass、@AttributeOverride)

    一.结构 二.代码 1. package org.jpwh.model.inheritance.mappedsuperclass; import javax.persistence.MappedSup ...

  9. JavaPersistenceWithHibernate第二版笔记-第六章-Mapping inheritance-003Table per concrete class with unions(@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)、<union-subclass>)

    一.代码 1. package org.jpwh.model.inheritance.tableperclass; import org.jpwh.model.Constants; import ja ...

随机推荐

  1. 通过修改注册表来破解sqlyog

    Sqlyog作为一款可视化的数据库管理工具,各种方便我就不说了,但是未经汉化或者绿色过的软件存在30天的生命期,到期后我们就不可以使用了,要摸卸载重装,我们还可以去修改注册表,来延长它的生命期,具体步 ...

  2. Java Day 13

    线程的状态 被创建 运行 冻结 消亡  被创建--start()--> 运行 运行----run()----> 消亡         stop() 运行---sleep(time)---& ...

  3. 如何分离数据库 (SQL Server Management Studio)

    在 SQL Server Management Studio 对象资源管理器中,连接到 SQL Server 数据库引擎的实例上,再展开该实例. 展开“数据库”,并选择要分离的用户数据库的名称. 分离 ...

  4. 前端之JavaScript第二天学习(5)-JavaScript-语句

    JavaScript 语句 JavaScript 语句向浏览器发出的命令.语句的作用是告诉浏览器该做什么. 下面的 JavaScript 语句向 id="demo" 的 HTML ...

  5. 第五周作业 关于C语言的问卷调查

    你对自己的未来有什么规划?做了哪些准备? 目前还不是很了解,我希望自己再毕业后可以在一家IT公司上班.  目前效果还不是很明显,只是对于专业的学习更加勤奋而已. 2.你认为什么是学习?学习有什么用?现 ...

  6. poj 1269 Intersecting Lines

    题目链接:http://poj.org/problem?id=1269 题目大意:给出四个点的坐标x1,y1,x2,y2,x3,y3,x4,y4,前两个形成一条直线,后两个坐标形成一条直线.然后问你是 ...

  7. 创建REST服务应用程序

    Web服务类别有两种,一种是基于SOAP协议的服务,另一种是基于HTTP协议的REST架构风格的服务.REST服务的数据格式有两种:XML 和 JSON,REST服务已被大量应用于移动互联网中. 本文 ...

  8. ubuntu文件定时加密压缩

    #!/bin/sh #zip.sh DATE=`date -d yesterday +"%Y%m%d"` #password PASS="123456" if ...

  9. C#简单windows服务

    因为做后台比较多,所以经常需要写一些后台服务.一般的流程是先创建一个服务项目,加入代码.然后打包一个安装程序或者直接用dos命令安装服务.下面是详细内容:    1. 创建windows服务项目.  ...

  10. Python3中的新特性(3)——代码迁移与2to3

    1.将代码移植到Python2.6 建议任何要将代码移植到Python3的用户首先将代码移植到Python2.6.Python2.6不仅与Python2.5向后兼容,而且支持Python3中的部分新特 ...