UserType and CompositeUserType为Hibernate中提供的用户类型自定义接口。根据这个接口,可以实现自定义的数据类型。
最近在做一个博客系统的后台,设计数据库的时候考虑如何设计添加和记录好友的功能。
由于以前没有这方面的经验,所以不知道从何下手。最近看<<深入浅出Hibernate>>,作者在书中介绍了Hibernate 提供的用户自定义数据类型,于是效仿书中的方法,在数据库表中添加一个字段用于记录所有好友的userid,每个userid之间用";"加以分隔,同时将该字段映射为一个特殊的List集合,利用UserType interface实现String解析后将各个userid封装在List中,将List中的记录封装成以";"分隔的String。
使用UserType接口,实现了较好的设计风格,以及更好的重用性。
/*
* 用户自定义的数据类型,对应数据库中的一个字段,在该字段中,保存了
* 多个用户需要的信息,之间用";"加以分隔.
* @Author:Paul
* @Date:April 18th,2008
*/
package com.globalhands.hibernate.userTypes;

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

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

public class SpecialList implements UserType {
private List specialList;

private static final char SPLITTER = ';';

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

public String assemble(Serializable arg0, Object arg1)
throws HibernateException {
return null;
}

/*
* 将List封装为一个String对象
*/
public String assemble(List specialList) throws HibernateException {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < specialList.size() - 1; i++) {
sb.append(specialList.get(i)).append(this.SPLITTER);
}
sb.append(specialList.get(specialList.size() - 1));
return sb.toString();
}

/*
* 创建一个新的List实例,包含原有的List实例中的所有元素.
*/
public Object deepCopy(Object value) throws HibernateException {
List sourceList = (List) value;
List targetList = new ArrayList();
targetList.addAll(sourceList);
return targetList;
}

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

/*
* 判断specialList是否发生变化
*/
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if (x != null && y != null) {
List xList = (List) x;
List yList = (List) y;

if (xList.size() != yList.size()) {
return false;
}

for (int i = 0; i <= xList.size() - 1; i++) {
String str1 = (String) xList.get(i);
String str2 = (String) yList.get(i);
if (!xList.equals(yList)) {
return false;
}
}
return true;
}
return false;
}

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

public boolean isMutable() {
return false;
}

/*
* 从resultset中取出email字段,并将其解析为List类型后返回
*/
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);
} else {
return null;
}
}

/*
* 将以";"分隔的字符串解析为一个字符串数组
*/
private List parse(String value) {
String[] strs = value.split(";");
List specialList = new ArrayList();
for (int i = 0; i <= strs.length - 1; i++) {
specialList.add(strs[i]);
}
return specialList;
}

/*
* 将List型的email信息组装成字符串之后保存到email字段
*/
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
if (value != null) {
String str = assemble((List) value);
Hibernate.STRING.nullSafeSet(st, str, index);
} else {
Hibernate.STRING.nullSafeSet(st, value, index);
}
}

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

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

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

}
同时,修改相应的[ormapping_filename].hbm.xml中相应字段的映射信息
<property name="buddy" type="com.globalhands.hibernate.userTypes.SpecialList">
            <column name="buddy" length="2000" not-null="true" />
        </property>
之后,修改POJO类中该字段的返回类型为List。
使用JUnit测试程序。
评论
chen-516888 2008-04-25   回复
谢谢大家:)
marcian 2008-04-25   回复
这个还是用关联表更好些。
peak 2008-04-24   回复
魔力猫咪深的hibernate的精髓,说的很有道理,做映射是hibernate非常好用的手段
icewubin 2008-04-24   回复
chen-516888 写道
再问一下。。。
好友对象也是User
如何让User 映射的表和自己关联???
那自己 是 inverse=false
还是 inverse=true??????

inverse=true
控制权交给User好友方,自己不能随意修改和删除自己的好友信息吧。

但是自己映射好友有问题,好友方不能多对一映射自己,因为这样做的,变成对方只能做你的好友,做不了其他人了,你这个映射应该是多对多。

你自己做个user到user的多对多的配置,数据库中肯定要有个表叫user_2_user的table,这个table不需要model直接映射的,你看了多对多映射的例子就明白了,自动查这张中间表的。
chen-516888 2008-04-24   回复
再问一下。。。
好友对象也是User
如何让User 映射的表和自己关联???
那自己 是 inverse=false
还是 inverse=true??????
chen-516888 2008-04-19   回复
非常感谢:)

着手重构
魔力猫咪 2008-04-19   回复
就是把模型改成我建议的样子。还有,如果可能,可以在缓存策略中设置用户对象为读写缓存策略。更具体的地方应该不用我写了。
chen-516888 2008-04-19   回复
那需要修改那些地方?
能不能再具体些
非常感谢!!!:)
魔力猫咪 2008-04-19   回复
不是的。每次读取User的时候,好友列表缺省是延迟加载的。只有你需要使用好友列表的时候,才会读取好友列表对象数据。
不会加重数据库负担的。而且如果有二级缓存的话,还可以大大减轻数据库负担。
chen-516888 2008-04-19   回复
那如果有100个好友的话
每次读取User的时候不是要加载100个User的实例吗?
我是想在页面中循环输出
<a src="/viewothers.do?otheruserid=<%=friendid%>">
    <font><%=friendname%></font>
</a>
要看某个好友的信息
把friendid传给action
然后再从数据库中读取某一个好友的信息,调到好友的页面
魔力猫咪 2008-04-19   回复
假设用户对象为User,那么它带有一个属性,用集合的方式表示好友。
class User {
    private Integer id;
    //好友集合
    private Set<User> friendly
}

就是这样。具体到数据库就会出现一张关联表

id userid friendly
*   1      2
*   1      3

你读取User的时候,就可以直接读取好友。当用户删除的时候,关联表中用户的所有好友关联也会被删除。
chen-516888 2008-04-19   回复
如何映射好友对象???
能否说具体些
本人是新手
多多执教:)
魔力猫咪 2008-04-18   回复
我觉得这样不怎么样。表面上看是取了个巧。这么保存可以省一个关联表。而且在读取好友列表的时候也可以减少一部分数据库查询。
但是这么做破坏了对象之间的关系。甚至破坏了数据库范式。同时,如果某个人被删除了呢?你如何维护其他人的好友列表?
查询的时候也一样。你得到的是好友id而不是好友对象。你还是要查询数据库。如果你实际上映射的是好友对象,那么你就可以直接取好友列表,Hibernate会自动帮你完成。如果你使用了二级缓存,那么取好友列表的速度甚至比从数据库直接取要快得多。
自定义类型以前最常见的是映射LOB成String或byte[],最著名的是Spring写的映射类型。
但是你这么乱用日后要吃苦头的。
发表评论

提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则

您还没有登录,请登录后发表评论

chen-516888
搜索本博客
博客分类
我的相册
1a8a55a2-8a9d-35ea-bba5-9d7fc566e002-thumb
IMG_9122
共 1 张
最近加入圈子
存档
最新评论