刘小文 2013-06-27
本篇介绍hibernate的自定义数据类型的用法
有些时候,出于设计上的统一性考虑,需要针对数据结构可能重复出现的数据模式,引入一些自定义数据类型。也就是说,目的是对某些数据处理方式封装起来,让系统业务逻辑更清晰。
UserType
这是一个Hibernate的接口,阁下可到官方Doc阅览个梗概: http://docs.jboss.org/hibernate/orm/4.1/javadocs/
此处举一例以说明之,假设有一member实体,表中有一个email字段为varchar类型,但实际上member可以拥有多个email地址,对应的POJO中email为一个List。
有一计谋为:在表中的email字段存储形式为 “[email protected];[email protected];...”即以“;”为分隔符将多个email拼接起来成为一个字符串。从表中读取时再将其处理成List,
逐个存放email。此时,我们可以通过Hibernate中的自定义类型为email字段做处理,将上述的繁琐逻辑封装起来,请看以下项目:
采用Maven搭建,MySQL做DB,只需专注跟TMember相关的材料即可:
项目结构:
1.SQL
create table t_member ( id int(11) not null primary key, name varchar(80) not null, email varchar(120) );
2.pom.xml(maven 项目,通过其可以非常方便引入所需jar包)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>learnHibernate</groupId> <artifactId>learnHibernate</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>learnHibernate</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.1.4.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>4.1.4.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>4.1.4.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.15</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> </dependencies> </project>
3.hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class"> com.mysql.jdbc.Driver </property> <property name="hibernate.dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="hibernate.connection.url"> jdbc:mysql://localhost:3306/hibernate </property> <property name="hibernate.connection.username"> root </property> <property name="hibernate.connection.password"> root </property> <property name="hibernate.show_sql"> true </property> <property name="hibernate.format_sql"> true </property> <!-- 配置C3P0 --> <property name="hibernate.connection.provider_class"> org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider </property> <property name="hibernate.c3p0.max_size">10</property> <property name="hibernate.c3p0.min_size">1</property> <property name="hibernate.c3p0.max_statements">3</property> <property name="hibernate.c3p0.timeout">30</property> <property name="hibernate.c3p0.acquire_increment">1</property> <property name="hibernate.c3p0.idle_test_periodt">10</property> <!-- 配置二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property> <!-- Hibernate4 这里和Hibernate3不一样 要特别注意!!!--> <property name="hibernate.cache.region.factory_class"> org.hibernate.cache.EhCacheRegionFactory </property> <!-- Hibernate3 --> <!-- <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> --> <!-- 实体映射文件 --> <mapping resource="learnHibernate/bean/TUser.hbm.xml" /> <mapping resource="learnHibernate/bean/TMember.hbm.xml" /> </session-factory> </hibernate-configuration>
4.HibernateLocalUtil.java
package learnHibernate.util; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; public final class HibernateLocalUtil { private static SessionFactory sessionFactory; private HibernateLocalUtil() { } static { try { Configuration configuration = new Configuration().configure("hibernate\\hibernate.cfg.xml"); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(configuration.getProperties()).buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); }catch (Throwable e) { throw new ExceptionInInitializerError(e); } } public static SessionFactory getSessionFactory () { return sessionFactory; } }
5.TMember.java
package learnHibernate.bean; import java.io.Serializable; import java.util.List; public class TMember implements Serializable{ private static final long serialVersionUID = -2487367694260008988L; private int id; private String name; private List email; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List getEmail() { return email; } public void setEmail(List email) { this.email = email; } }
6.TMember.hbm.xml
<?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 package="learnHibernate.bean"> <class name="TMember" table="t_member"> <id name="id" column="id" type="java.lang.Integer"> <generator class="native"/> </id> <property name="name" column="name" type="java.lang.String"/> <property name="email" column="email" type="learnHibernate.bean.EmailList" /> </class> </hibernate-mapping>
7.EmailList.java(本例关键核心所在)
package learnHibernate.bean; 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.engine.spi.SessionImplementor; import org.hibernate.type.StringType; import org.hibernate.type.Type; import org.hibernate.usertype.UserType; public class EmailList implements UserType { private static final String seperator = ";"; private static final int[] TYPES = new int[] {Types.VARCHAR}; /** * 重新构建一个处于可缓存状态的对象 */ @Override public Object assemble(Serializable arg0, Object arg1) throws HibernateException { // TODO Auto-generated method stub return null; } /** * 提供自定义类型的完全复制方法 * 本方法将用作构造返回对象 * 当nullSafeGet方法调用后,我们获得了自定义数据对象。在向用户返回 * 自定义数据之前,deepCopy方法将被调用,它将根据自定义数据对象构造 * 一个完全的copy,并将copy返回给用户使用。 * 此时,我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始 * 版本,其二是我们通过deepCopy构造的复制版本,原始版本将有hibernate负责维护, * 复制版本将由用户使用,原始版本用作稍后的脏数据检查依据;hibernate将在脏数据检查 * 过程中将两个版本的数据进行比对(通过调用equals方法),如果数据发生了变化(equals返回false), * 则执行对应的持久化操作 */ @Override public Object deepCopy(Object value) throws HibernateException { if(value != null) { List sourceList = (List)value; List trgList = new ArrayList(); trgList.addAll(sourceList); return trgList; } return null; } /** * 将对象转化为可缓存状态 */ @Override public Serializable disassemble(Object arg0) throws HibernateException { // TODO Auto-generated method stub return null; } /** * 自定义数据类型额对比方法 * 此方法将用作脏数据检查,参数代表两个副本, * 若equals返回false,则hibernate将认为数据发生变化,并将变化更新到数据库 */ @Override 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 xStr = (String)xList.get(i); String yStr = (String)yList.get(i); if( !xStr.equals(yStr) ) return false; } return true; } return false; } @Override public int hashCode(Object arg0) throws HibernateException { return 0; } /** * 本类型实例是否可变 */ @Override public boolean isMutable() { return false; } /** * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回 * (此方法要求对可能出现的null值进行处理) * names中包含了当前自定义类型的映射字段名称。 * 此处从resultSet中取出email字段,并将其解析为List类型后返回 */ @Override public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { String value = StringType.INSTANCE.nullSafeGet(rs, names[0], session); if(value != null) { return _parseToList(value); }else { return null; } } /** * 本方法将在hibernate进行数据保存时被调用 * 可以通过preparedStatement将自定义数据写入对应的库表字段 * 此处将List型的email信息组装成字符串之后保存到email字段 */ @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { System.out.println("Set method executed"); if(value != null) { String str = _parseToStr((List)value); StringType.INSTANCE.nullSafeSet(st, str, index, session); }else { StringType.INSTANCE.nullSafeSet(st, value, index, session); } } /** * 在下不明白此方法作用,若有知者能在留言指点一二,不胜感激 */ @Override public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException { // TODO Auto-generated method stub return null; } /** * UserType.nullSafeGet()所返回的自定义数据类型 */ @Override public Class returnedClass() { return List.class; } /** * 返回UserType所映射字段的SQL类型(java.sql.Types) * 返回类型为int[],其中包含了映射各字段的SQL类型代码(UserType可以映射到一个或者多个字段) */ @Override public int[] sqlTypes() { return TYPES; } private List _parseToList(String value) { String[] strArr = value.split(seperator); List emailList = new ArrayList(); for(int i=0; i<strArr.length; i++) { emailList.add(strArr[i]); } return emailList; } private String _parseToStr(List emailList) { StringBuffer strBuf = new StringBuffer(); for(int i=0; i<emailList.size()-1; i++) { strBuf.append(emailList.get(0)).append(seperator); } strBuf.append(emailList.get(emailList.size()-1)); return strBuf.toString(); } }
8. TestCase2.java(测试用例Junit4.8.2,若阁下使用Eclipse并有Junit插件,右键Junit test即可)
package learnHibernate; import java.util.ArrayList; import java.util.List; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.junit.Test; import learnHibernate.bean.TMember; import learnHibernate.util.HibernateLocalUtil; public class TestCase2 { // @Test public void saveTMember() { TMember t = new TMember(); List emailList = new ArrayList(); emailList.add("[email protected]"); emailList.add("[email protected]"); emailList.add("[email protected]"); t.setName("Oham"); //t.setEmail(emailList); SessionFactory sessionFactory = HibernateLocalUtil.getSessionFactory(); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(t); tx.commit(); session.close(); } @Test public void selectAllTMember() { SessionFactory sessionFactory = HibernateLocalUtil.getSessionFactory(); Session session = sessionFactory.openSession(); Criteria criteria = session.createCriteria(TMember.class); List members = criteria.list(); for(Object t : members) { TMember m = (TMember)t; List emailList = m.getEmail(); if(emailList != null) { System.out.println(emailList.toString()); }else { System.out.println("no email"); } } session.close(); } }
请君动手搞一搞,为求得一个体会