Hibernate有很多值得学习的地方,这里我们主要介绍Hibernate使用UserType,包括介绍UserType就是用户自定义类型等方面。
1、建立数据库的时候最好建立一个跟业务逻辑完全没有关系的id,这样的好处就是以后当数据量大的时候可以容易的建立索引,而且当业务逻辑变化的时候底层的数据结构不用变化很大。
2、代码的干净是非常非常的重要的,从现在开始尽量让自己的代码写的漂亮把!因为干净的代码不仅容易维护,而且容易发现bug,在Hibernate使用UserType是很好用的。举个经典的例子:
实现Hibernate使用UserType接口的email 类
比如 你需要在 TUSER 表中记录一个人的 email ,单独为其建立一张数据库表是很浪费资源的,一般的方法是将TUSER 一栏 varchar 的来记录email ,多个 email 之间用 " ; "号分开,我曾经做的项目中采取的方法是:取出一个记录着多条 email 的 string然后再来用 ; 号拆开,但是这样子就有个不好,在一堆整齐的 get 方法用出来一个有 bad smell 的代码。
有两个方法,一个是修改映射TUSER的 java 类在其中增加一个 getEmail 方法,返回一个 list ,该方法对原先的 getEmail 方法进行二次处理。第二个方法就是采用UserType 。
UserType就是用户自定义类型,这里的类型指的是除了Hibernate定义的那些类型之外的用户自己定义的。
一个实现Hibernate使用UserType接口的 email 类如下(里面的每个方法都是必须实现的接口方法,许多都可以复制粘贴的,不复杂):(madp:详细代码看文章结尾)
在TUser.hbm.xml 中如此设置:
在TUser.java中如此设置
这样设置后你使用 TUser的时候要取出 email 直接 getEmail 出来的就是一个List 了,而且前台根本不知道后台干了什么,这样代码就简洁多了。
(madp:
看spring源代码中对hibernate的usertype的封装
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import javax.transaction.TransactionManager;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Hibernate UserType implementation for Strings that get mapped to CLOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>Particularly useful for storing Strings with more than 4000 characters in an
* Oracle database (only possible via CLOBs), in combination with OracleLobHandler.
*
* <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be CLOB: For databases like MySQL and MS SQL Server, any
* large enough character type will work.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
*/
public class ClobStringType extends AbstractLobType {
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
public ClobStringType() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected ClobStringType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
super(lobHandler, jtaTransactionManager);
}
public int[] sqlTypes() {
return new int[] {Types.CLOB};
}
public Class returnedClass() {
return String.class;
}
protected Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException {
return lobHandler.getClobAsString(rs, names[0]);
}
protected void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException {
lobCreator.setClobAsString(ps, index, (String) value);
}
}
)
(madp:
首先看一下 hibernate给我们提供的借口。UserType接口。具体的接口方法可以去看hiberate的文档或者看夏昕的深入浅出hiberate。里面都有详细的介绍。下面看一下,我们要实现该接口 需要实现的几个方法。
1. 首先因为 数据库的 字符串映射到java端的list数据类型。所以 需要包含一个list的属性。
private List emails;
2. 其次需要实现 判断俩个自定义数据类型是否相等的函数。
public boolean equals(Object x, Object y) throws HibernateException
3.然后是在hiberate查询和更新操作时真正需要调用的方法,该方法从resultset中取出email字段,并将其解析为List类型后返回。
public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
4//将list类型组装成字符串后保存到email字段
public void nullSafeSet(PreparedStatement st, Object value, int index)
当然,需要自己具体的实现组装list和拆开list使其成为string的方法。
下面贴出代码:
user.hbm.xml
view plaincopy to clipboardprint?
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.chenlong.usertype.tuser" table="tuser">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="increment"></generator>
</id>
<property name="name" type="java.lang.String">
<column name="NAME" length="255" not-null="false" />
</property>
<property name="age" type="java.lang.Integer" column="age"></property>
<property name="email" type="com.chenlong.usertype.EmailList">
<column name = "EMAIL" length="300"/>
</property>
</class>
</hibernate-mapping>
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.chenlong.usertype.tuser" table="tuser">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="increment"></generator>
</id>
<property name="name" type="java.lang.String">
<column name="NAME" length="255" not-null="false" />
</property>
<property name="age" type="java.lang.Integer" column="age"></property>
<property name="email" type="com.chenlong.usertype.EmailList">
<column name = "EMAIL" length="300"/>
</property>
</class>
</hibernate-mapping>
EmailList.java
view plaincopy to clipboardprint?
package com.chenlong.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 java.lang.String;
import org.hibernate.HibernateException;
import org.hibernate.Hibernate;
/**
* 必须得引用java.io.Serializable,UserType是反序列化
* 另外,我使用的hibernate版本是3.2,UserType从2.0到3.2有很大的差异,应该多多看看官方的doc
*
* @author @家军.严重注意org.hibernate.usertype.UserType,不要自己造车呀.
*
*/
public class EmailList implements java.io.Serializable,org.hibernate.usertype.UserType{
private List emails;
private static final String SPLITTER = ";";
private static final int[] TYPES = new int[] { Types.VARCHAR };
public int[] sqlTypes() {
return TYPES;
}
public Class returnedClass() {
// TODO Auto-generated method stub
return List.class;
}
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(); i++) {
String str1 = (String) xList.get(i);
String str2 = (String) yList.get(i);
if (!str1.equals(str2))
return false;
}
return true;
}
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;
}
}
//将list类型组装成字符串后保存到email字段
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
System.out.println("Set method excecuted");
if (value != null) {
String str = assemble((List) value);
Hibernate.STRING.nullSafeSet(st, str, index);
} else {
Hibernate.STRING.nullSafeSet(st, value, index);
}
}
public Object deepCopy(Object value) throws HibernateException {
List sourcelist = (List) value;
List targetlist = new ArrayList();
targetlist.addAll(sourcelist);
return targetlist;
}
public boolean isMutable() {
return false;
}
//将list组装成一个string返回给hibernate
private String assemble(List emailList) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < emailList.size() - 1; i++) {
strBuf.append(emailList.get(i)).append(SPLITTER);
}
strBuf.append(emailList.get(emailList.size() - 1));
return strBuf.toString();
}
//将string拆开,放到list中
private List parse(String value) {
String[] strs = value.split(SPLITTER);
List emailList = new ArrayList();
for (int i = 0; i < strs.length; i++) {
emailList.add(strs[i]);
}
return emailList;
}
public Object assemble(Serializable arg0, Object arg1) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
public Serializable disassemble(Object arg0) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
public int hashCode(Object arg0) throws HibernateException {
// TODO Auto-generated method stub
return 0;
}
public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
}
package com.chenlong.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 java.lang.String;
import org.hibernate.HibernateException;
import org.hibernate.Hibernate;
/**
* 必须得引用java.io.Serializable,UserType是反序列化
* 另外,我使用的hibernate版本是3.2,UserType从2.0到3.2有很大的差异,应该多多看看官方的doc
*
* @author @家军.严重注意org.hibernate.usertype.UserType,不要自己造车呀.
*
*/
public class EmailList implements java.io.Serializable,org.hibernate.usertype.UserType{
private List emails;
private static final String SPLITTER = ";";
private static final int[] TYPES = new int[] { Types.VARCHAR };
public int[] sqlTypes() {
return TYPES;
}
public Class returnedClass() {
// TODO Auto-generated method stub
return List.class;
}
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(); i++) {
String str1 = (String) xList.get(i);
String str2 = (String) yList.get(i);
if (!str1.equals(str2))
return false;
}
return true;
}
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;
}
}
//将list类型组装成字符串后保存到email字段
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
System.out.println("Set method excecuted");
if (value != null) {
String str = assemble((List) value);
Hibernate.STRING.nullSafeSet(st, str, index);
} else {
Hibernate.STRING.nullSafeSet(st, value, index);
}
}
public Object deepCopy(Object value) throws HibernateException {
List sourcelist = (List) value;
List targetlist = new ArrayList();
targetlist.addAll(sourcelist);
return targetlist;
}
public boolean isMutable() {
return false;
}
//将list组装成一个string返回给hibernate
private String assemble(List emailList) {
StringBuffer strBuf = new StringBuffer();
for (int i = 0; i < emailList.size() - 1; i++) {
strBuf.append(emailList.get(i)).append(SPLITTER);
}
strBuf.append(emailList.get(emailList.size() - 1));
return strBuf.toString();
}
//将string拆开,放到list中
private List parse(String value) {
String[] strs = value.split(SPLITTER);
List emailList = new ArrayList();
for (int i = 0; i < strs.length; i++) {
emailList.add(strs[i]);
}
return emailList;
}
public Object assemble(Serializable arg0, Object arg1) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
public Serializable disassemble(Object arg0) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
public int hashCode(Object arg0) throws HibernateException {
// TODO Auto-generated method stub
return 0;
}
public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
// TODO Auto-generated method stub
return null;
}
}
javabean-- tuser.java
view plaincopy to clipboardprint?
package com.chenlong.usertype;
import java.util.List;
/**
* EchoMessageEmail generated by MyEclipse Persistence Tools
*/
public class tuser implements java.io.Serializable {
private Integer id;
private String name;
private Integer age;
private List email;
/** default constructor */
public tuser() {
}
/** minimal constructor */
public tuser(String name) {
this.name = name;
}
/** full constructor */
public tuser(String name,Integer age, List email) {
this.name = name;
this.email = email;
this.age = age;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public List getEmail() {
return email;
}
public void setEmail(List email) {
this.email = email;
}
}
package com.chenlong.usertype;
import java.util.List;
/**
* EchoMessageEmail generated by MyEclipse Persistence Tools
*/
public class tuser implements java.io.Serializable {
private Integer id;
private String name;
private Integer age;
private List email;
/** default constructor */
public tuser() {
}
/** minimal constructor */
public tuser(String name) {
this.name = name;
}
/** full constructor */
public tuser(String name,Integer age, List email) {
this.name = name;
this.email = email;
this.age = age;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public List getEmail() {
return email;
}
public void setEmail(List email) {
this.email = email;
}
}
测试类 -- HibernateTest.java
view plaincopy to clipboardprint?
package com.chenlong.usertype;
import java.util.List;
import java.util.ListIterator;
import com.chenlong.usertype.tuser;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class HibernateTest {
public static void main(String[] args) throws Exception{
Configuration config = new Configuration().configure();
SessionFactory factory = config.buildSessionFactory();
Session session = factory.openSession();
Query query = session.createQuery("from tuser as a");
/**
* 理论上来说,这里不存在lazy加载,为了安全起见使用了Iterator
*
*/
ListIterator iterator = query.list().listIterator();
tuser tt = (tuser) iterator.next();//只找第一个
List emails = tt.getEmail();
for (int i = 0; i < emails.size(); i++) {
String emailStr = (String)emails.get(i);
System.out.println(emailStr);
}
tuser user = (tuser) session.load(tuser.class, new Integer(2));
List list = user.getEmail();
for(int i=0;i<list.size();i++)
{
System.out.println(list.get(i));
}
list.remove("[email protected]");
list.remove("[email protected]");
//list.("[email protected]");
Transaction tx = session.beginTransaction();
session.save(user);
tx.commit();
session.close();
}
}
)