Hibernate高级映射技术(一)自定义数据类型StringList (转载用于收藏)

转载于:http://ajava.org/course/open/14003.html
核心提示:我们在设计数据库时往往会遇到例如用户的多个手机号码的一对多问题,如果设计一个T_MOBILE表保存未免太大动干戈而且影响速度,所以如果没有严格的要求,一般情况我们在T_USER表里设计一个mobiles字段,其中的多个手机号码用;分隔。尽管这样不符合数据库范式

我们在设计数据库时往往会遇到例如用户的多个手机号码的一对多问题,如果设计一个T_MOBILE表保存未免太大动干戈而且影响速度,所以如果没有严格的要求,一般情况我们在T_USER表里设计一个mobiles字段,其中的多个手机号码用;分隔。尽管这样不符合数据库范式的设计原则,但在性能和编码复杂度上确实最低的。
这样如果用Hibernate的String类型来映射,就会得到一个长字符串,每次必须按;分隔后才能使用,这样代码就很冗余。
幸好Hibernate有自定义数据类型的支持,只要实现UserType或CompositeUserType接口。不过这两个接口的内容比较复杂,有很多方法需要实现,不能偷懒哦:-)
下 面是我对于用;分隔的字段的自定义数据类型的实现。

package com.willishz.framework.dao.usertype;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

/**
* 用分隔符分隔的字符串数组.分隔符默认为";"
* @author willishz
*/
public class StringList implements UserType, Serializable {

public StringList() {
super();
}

public StringList(char splitter) {
super();
this.splitter = splitter;
}

public StringList(List<String> attributeList) {
super();
this.attributeList = attributeList;
}

private List<String> attributeList;

/**
* 分隔符
*/
private char splitter = SPLITTER;

/**
* 分隔符默认为";"
*/
private static final char SPLITTER = ';';

private static final int[] SQLTYPES = new int[] { Types.VARCHAR };

public boolean isMutable() {
return false;
}

public int[] sqlTypes() {
return SQLTYPES;
}

public Object assemble(Serializable id, Object obj) throws HibernateException {
return null;
}

/**
* 将List类型的数组拼接成字符串
* @param attributeList
* @return
* @throws HibernateException
*/
public Object assemble(List<String> attributeList) throws HibernateException {
if (attributeList == null) {
return null;
}
StringBuffer asbl = new StringBuffer();
asbl.append(attributeList.get(0));
for (int i = 1; i < attributeList.size(); i++) {
asbl.append(splitter).append(attributeList.get(i));
}
return asbl.toString();
}

/**
* 自定义类型的完全复制方法,返回一个和原自定义数据相同的新对象
*
* @param value the object to be cloned, which may be null
* @return Object a copy
* @see org.hibernate.usertype.UserType#deepCopy(java.lang.Object)
*/
public Object deepCopy(Object value) throws HibernateException {
if (value == null) {
return null;
}
ArrayList sourceList = (ArrayList) value;
ArrayList targetList = new ArrayList();
List<String> _attributeList = new ArrayList<String>();
targetList.addAll(sourceList);
return targetList;
}

/**
* 自定义数据类型的比较方法
*
* @param x
* @param y
* @return boolean
* @see org.hibernate.usertype.UserType#equals(java.lang.Object, java.lang.Object)
*/
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if (x != null && y != null) {
List<String> xList = (List<String>) x;
List<String> yList = (List<String>) y;
if (xList.size() != yList.size()) {
return false;
}
String _x = null;
String _y = null;
for (int i = 0; i < xList.size(); i++) {
_x = xList.get(i);
_y = yList.get(i);
if (!_x.equalsIgnoreCase(_y)) { // case insensitive
return false;
}
}
return true;
}
return false;
}

public int hashCode(Object arg0) throws HibernateException {
return attributeList.hashCode();
}

/**
* 将以分隔符分隔的字符串解析成一个字符串数组
*
* @param value
* @return
*/
public List<String> parse(String value) {
if (value == null) {
return null;
}
String[] strs = org.apache.commons.lang.StringUtils.split(value, splitter);
List<String> attributeList = new ArrayList<String>();
for (int i = 0; i < strs.length; i++) {
attributeList.add(strs[i]);
}
return attributeList;
}

/**
* 从JDBC的ResultSet中读取数据,并将其转换为自定义类型后返回。
* 此方法要求对可能出现null的情况做处理。
* names中包含了当前自定义类型的映射字段名称。
*
* @param rs a JDBC result set
* @param names the column names
* @param owner the containing entity
* @return Object
* @throws HibernateException
* @throws SQLException
* @see org.hibernate.usertype.UserType#nullSafeGet(java.sql.ResultSet, java.lang.String[], java.lang.Object)
*/
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException {
String value = (String) Hibernate.STRING.nullSafeGet(rs, names[0]);
if (value != null) {
return parse(value);
}
return null;
}

/**
* 在Hibernate进行数据保存时被调用
* 可以通过PreparedStatement将自定义数据写入对应的数据库字段中
* names中包含了当前自定义类型的映射字段名称。
*
* @param st a JDBC prepared statement
* @param value the object to write
* @param index statement parameter index
* @throws HibernateException
* @throws SQLException
* @see org.hibernate.usertype.UserType#nullSafeGet(java.sql.ResultSet, java.lang.String[], java.lang.Object)
*/
public void nullSafeSet(PreparedStatement pst, Object value, int index)
throws HibernateException, SQLException {
if (value != null) {
Hibernate.STRING.nullSafeSet(pst, assemble((List<String>) value), index);
} else {
Hibernate.STRING.nullSafeSet(pst, value, index);
}
}

public Class returnedClass() {
return StringList.class;
}

public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
return null;
}

public Serializable disassemble(Object arg0) throws HibernateException {
return null;
}
}

Hibernate配置文件的相关内容如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="com.willishz.apocalypse.ebid.domain">
<class
name="User"
table="t_user"
>
.................
<property
name="mobiles"
column="mobiles"
type="com.willishz.framework.dao.usertype.StringList"
not-null="false"
length="100"
/>
.................
</class>
</hibernate-mapping>

Hibernate映射实体文件的相关内容:

package com.willishz.apocalypse.ebid.domain.base;

import java.io.Serializable;

public abstract class User implements Serializable {
.................

private java.util.List<String> mobiles;

public java.util.List<String> getMobiles() {
return mobiles;
}

public void setmobiles(java.util.List<String> mobiles) {
this.mobiles = mobiles;
}
.................
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值