论坛首页 Java版 Hibernate

不知HIBERNATE是否支持对结果集过滤更替的功能,,,

浏览 8491 次
该帖已经被评为精华帖
作者 正文
最后更新时间:2005-09-07
在项目中常遇到用数字表示一种意思的字段,例如:type  ,1 表示正常状态,2表示冻结状态,,,如果用HIBERNATE返回查询的结果集,只是把数据返回到页面是不行的,必须得对结果集进行过滤,把“1” 更替为 “正常状态后”,再返回,,如果用程序写,较麻烦,而且效率低,,得把结果集解开,更替,再封装,,,
不知道HIBERNATE有没有提供一种过滤更替的功能呢?
   
最后更新时间:2005-09-07
这是一类典型的问题,即枚举类型的映射和表示。在Hibernate2.1版本中提供了enum的接口类型来处理这类问题,参考这个讨论:

http://forum.javaeye.com/viewtopic.php?t=8114&postdays=0&postorder=asc&highlight=enum&start=0

但是在Hibernate3中取消了enum类型的支持,Hibernate官方推荐使用UserType接口。不过使用UserType接口实际上会比较复杂,下面我将使用一个简单的案例来讲解使用UserType接口来解决这类问题的办法:

User对象,是一个简单的域模型,User有一个UserTitle属性,可以是"雇员","经理","总监","CEO"等等,这是一个枚举属性,定义如下:

package com.javaeye.simple.domain;

import java.io.Serializable;

public class UserTitle implements Serializable {

	private int title;

	public static final UserTitle EMPLOYEE = new UserTitle(0);

	public static final UserTitle MANAGER = new UserTitle(1);

	public static final UserTitle DIRECTOR = new UserTitle(2);

	public static final UserTitle CEO = new UserTitle(3);

	private UserTitle(int title) {
		this.title = title;
	}

	public int toInt() {
		return title;
	}

	public static UserTitle fromInt(int title) {
		switch (title) {
		case 0:
			return EMPLOYEE;
		case 1:
			return MANAGER;
		case 2:
			return DIRECTOR;
		case 3:
			return CEO;
		default:
			throw new RuntimeException("Unknown Status");
		}
	}

	public String toString() {
		switch (title) {
		case 0:
			return "雇员";
		case 1:
			return "经理";
		case 2:
			return "总监";
		case 3:
			return "CEO";
		default:
			throw new RuntimeException("Unknown Status");
		}
	}

}


要说明的是,如果使用JDK5.0的enum,可以简化这个类的代码,此外真实的项目开发,不应该在代码中直接写中文,应该去读一个外部的配置文件,获得中文字符串的内容,这个地方大家可以自己去改。
   
0 请登录后投票
最后更新时间:2005-09-07
User对象的代码:

/*
 * Created on 2004-11-11
 *
 */
package com.javaeye.simple.domain;

import java.util.Date;

/**
 * @author Robbin Fan
 * 
 */
public class User {

	private Long id;

	private String username;

	private String password;

	private Date birthday;

	private String gender;

	private int age ;

	private String department;

	private String mail;

	private UserTitle title;

	public UserTitle getTitle() {
		return title;
	}

	public void setTitle(UserTitle title) {
		this.title = title;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Date getBirthday() {
		return birthday;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getDepartment() {
		return department;
	}

	public void setDepartment(String department) {
		this.department = department;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getMail() {
		return mail;
	}

	public void setMail(String mail) {
		this.mail = mail;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

}


注意,我们需要一个UserTitleType的类,实现UserType接口,定义一个User对象的UserTitle属性是如何向数据库持久化的:

package com.javaeye.simple.domain;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Types;
import java.sql.SQLException;

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

public class UserTitleType implements UserType {

	public int[] sqlTypes() {
		int[] types = {Types.INTEGER};		
		return types;
	}

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

	public boolean equals(Object x, Object y) throws HibernateException {
		return x == y;
	}

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

	public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
		int title = rs.getInt(names[0]);
		return rs.wasNull() ? null : UserTitle.fromInt(title);
	}

	public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
		if (value == null) {
			st.setNull(index, Types.INTEGER);
		} else {
			st.setInt(index, ((UserTitle) value).toInt());
		}
	}

	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

	public boolean isMutable() {
		return false;
	}

	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) deepCopy(value);
	}

	public Object assemble(Serializable cached, Object owner) throws HibernateException {
		return deepCopy(cached);
	}

	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return deepCopy(original);
	}

}


注意nullSafeGet和nullSafeSet方法的代码,是如何实现UserTitle的字符串值和数据库中保存数字进行转换的。

最后是映射文件:

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

<hibernate-mapping package="com.javaeye.simple.domain">

	<class name="User">
		<id name="id" unsaved-value="null">
			<generator class="native"/>
		</id>
		
		<property name="username"/>
		<property name="password"/>
		<property name="gender"/>
		<property name="age"/>
		<property name="department"/>
		<property name="mail"/>
		<property name="birthday"/>
		<property name="title" type="com.javaeye.simple.domain.UserTitleType">
			
		</property>
	</class>
    
</hibernate-mapping>


指明title属性是用户自定义的某类型。
   
0 请登录后投票
最后更新时间:2005-09-07
在Java程序中指定user的title属性:

user.setTitle(UserTitle.MANAGER);
session.save(user);

在页面输出:

<% user.getTitle() %>

就可以打印出中文字符串表示。
   
0 请登录后投票
最后更新时间:2005-09-07
这样在页面上显示不麻烦了,但保存时又得做转换了,总之还是麻烦。:wink:
user.setTitle(UserTitle.fromInt(Integer.parseInt(request.getParameter("userTitle"))));
   
0 请登录后投票
最后更新时间:2005-09-07
这个标题有点误导啊,初看还以为是session.filter的应用呢。

其实如果状态比较少,那么我个人认为还不如将问题推向前台,在前台进行逻辑处理,如果用JSTL,那么只要${type == 1 ? 'CEO' : '雇员'}。当然,如果状态比较多,可以考虑使用UserType。

事实上,写一个UserType所要关注的东西很多,其中的deepCopy,nullSafeGet和nullSafeSet等方法都要自己小心编写。robbin给出的应该只是一个简单的示例。

这种情况,你认为是在前台写一些类似<c:choose>或者各种逻辑判断的显示逻辑来得简单呢,还是在后台小心翼翼的写一个UserType方便呢?虽然在view层有逻辑有点bad smell
   
0 请登录后投票
最后更新时间:2005-09-07
我们通常在这种情况下,直接文件操作,或在数据库建look up table

robbin好文,受教不少。

不过,个人感觉确实是有点麻烦,如果有很多这样的域模型存在的情况下,那岂不是我要写大量相应的UserTitle和实现UserType接口的Type类。。。,那我还不如直接做个I/O交互算了,尽管要牺牲效率。

ps:或许是由于不习惯吧。
   
0 请登录后投票
最后更新时间:2005-09-08
我一般用数组。数据库里存的是下标。存数据库也可以,也不是每次都要访问数据库,如果不是常常变得话,一次读入内存就够了。变了就把内存给刷一次。
   
0 请登录后投票
最后更新时间:2005-09-08
downpour 写道
这个标题有点误导啊,初看还以为是session.filter的应用呢。

其实如果状态比较少,那么我个人认为还不如将问题推向前台,在前台进行逻辑处理,如果用JSTL,那么只要${type == 1 ? 'CEO' : '雇员'}。当然,如果状态比较多,可以考虑使用UserType。

事实上,写一个UserType所要关注的东西很多,其中的deepCopy,nullSafeGet和nullSafeSet等方法都要自己小心编写。robbin给出的应该只是一个简单的示例。

这种情况,你认为是在前台写一些类似<c:choose>或者各种逻辑判断的显示逻辑来得简单呢,还是在后台小心翼翼的写一个UserType方便呢?虽然在view层有逻辑有点bad smell


在页面处理并不好,一旦需要修改title的名称,你就满世界去改页面吧。
   
0 请登录后投票
最后更新时间:2005-09-08
z_jordon 写道
这样在页面上显示不麻烦了,但保存时又得做转换了,总之还是麻烦。:wink:
user.setTitle(UserTitle.fromInt(Integer.parseInt(request.getParameter("userTitle"))));



Integer.parseInt(request.getParameter("userTitle"))

这段代码是标准的从HTTPRequest里面取得参数值,和我的方案无关,即使不用我这种方案,你也是必须要写的。实际上我的方案仅仅是:

user.setTitle(UserTitle.fromInt(title));


和我上面举的例子一样

user.setTitle(UserTitle.MANAGER);


并没有增加任何复杂度。
   
0 请登录后投票
论坛首页 Java版 Hibernate

跳转论坛:
JavaEye推荐