Hibernate映射自訂資料類型
資料庫中存在一個email欄位,並允許其中儲存多個email地址,各地址之間使用 ; 分割,但是在POJO中,為了便於處理,email定義為一個List對象。
如何將String欄位對應為List類型,Hibernate並沒有提供原生支援,需要我們實現自己的UserType。
1.資料庫定義
DROP TABLE t_user_mail;CREATE TABLE t_user_mail ( id INT NOT NULL AUTO_INCREMENT , name VARCHAR ( 50 ) , age INT , email VARCHAR ( 300 ) , PRIMARY KEY (id));
2.
實現了UserType的自訂資料類型類EMailList
package cn.blogjava.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.apache.commons.lang.StringUtils;import org.hibernate.Hibernate;import org.hibernate.HibernateException;import org.hibernate.usertype.UserType;public class EMailList implements UserType { private static final char SPLITTER = ' ; ' ; private static final int [] TYPES = new int [] {Types.VARCHAR}; public int [] sqlTypes() { // TODO Auto-generated method stub 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 ; } public int hashCode(Object arg0) throws HibernateException { // TODO Auto-generated method stub return 0 ; } 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 ; } } 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 deepCopy(Object value) throws HibernateException { List sourceList = (List)value; List targetList = new ArrayList(); targetList.addAll(sourceList); return targetList; } public boolean isMutable() { // TODO Auto-generated method stub return false ; } public Serializable disassemble(Object arg0) throws HibernateException { // TODO Auto-generated method stub return null ; } public Object assemble(Serializable arg0, Object arg1) throws HibernateException { // TODO Auto-generated method stub return null ; } public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException { // TODO Auto-generated method stub return null ; } private String assemble(List emailList) { StringBuffer strBuf = new StringBuffer(); for ( int i = 0 ; i < emailList.size()- 1 ; i ++ ) { strBuf.append((String)emailList.get(i)).append(SPLITTER); } strBuf.append(emailList.get(emailList.size()- 1 )); return strBuf.toString(); } private List parse(String value) { String[] strs = StringUtils.split(value, SPLITTER); List emailList = new ArrayList(); for ( int i = 0 ; i < strs.length; i++ ) { emailList.add(strs[i]); } return emailList; }}
3.
設定檔TUserMail.hbm.xml
<?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><!-- Auto-generated mapping file from the hibernate.org cfg2hbm engine--> <class name="cn.blogjava.usertype.TUserMail" table="t_user_mail" catalog="sample"> <id name="id" type="integer"> <column name="id" /> <generator class="native" /> </id> <property name="name" type="string"> <column name="name" length="50" /> </property> <property name="age" type="integer"> <column name="age" /> </property> <property name="email" type="cn.blogjava.usertype.EMailList"> <column name="email" length="300" /> </property> </class></hibernate-mapping>
4.
在hibernate.cfg.xml載入TUserMail.hbm.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.connection.password">1234</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/sample</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="cn/blogjava/start/TUser.hbm.xml" /> <mapping resource="cn/blogjava/usertype/TUserMail.hbm.xml" /> </session-factory></hibernate-configuration>
5.
測試類別HibernateTest.java
package cn.blogjava.usertype;import java.util.ArrayList;import java.util.List;import junit.framework.Assert;import junit.framework.TestCase;import org.hibernate.HibernateException;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;public class HibernateTest extends TestCase { Session session = null; /** * JUnit中的setUp方法在TestCase初始化的時候會自動調用 * 一般用於初始化公用資源 */ protected void setUp() { try { /** * 可以採用hibernate.properties或者hibernate.cfg.xml * 設定檔的初始化代碼 * * 採用hibernate.properties * Configuration config = new Configuration(); * config.addClass(TUser.class); */ //採用hibernate.cfg.xml設定檔,與上面的方法對比,兩個差異 //1.Configuration的初始化方式 //2.xml Configuration config = new Configuration().configure(); SessionFactory sessionFactory = config.buildSessionFactory(); session = sessionFactory.openSession(); } catch (HibernateException e) { // TODO: handle exception e.printStackTrace(); } } /** * JUnit中的tearDown方法在TestCase執行完畢的時候會自動調用 * 一般用於釋放資源 */ protected void tearDown() { try { session.close(); } catch (HibernateException e) { // TODO: handle exception e.printStackTrace(); } } /** * 對象持久化測試(Insert方法) */ public void testInsert() { Transaction tran = null; try { tran = session.beginTransaction(); TUserMail user = new TUserMail(); user.setName("byf"); List list = new ArrayList(); list.add("baiyf@msn.com"); list.add("bexy@163.com"); user.setEmail(list); session.save(user); session.flush(); tran.commit(); Assert.assertEquals(user.getId().intValue()>0 ,true); } catch (HibernateException e) { // TODO: handle exception e.printStackTrace(); Assert.fail(e.getMessage()); if(tran != null) { try { tran.rollback(); } catch (Exception e1) { // TODO: handle exception e1.printStackTrace(); } } } } /** * 對象讀取測試(Select方法) */ public void testSelect(){ String hql = " from TUserMail where name='byf'"; try { List userList = session.createQuery(hql).list(); TUserMail user = (TUserMail)userList.get(0); List mailList = user.getEmail(); Assert.assertEquals((String)mailList.get(0), "baiyf@msn.com"); Assert.assertEquals((String)mailList.get(1), "bexy@163.com"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); Assert.fail(e.getMessage()); } }}
運行測試代碼,觀察資料庫中,可以發現email地址資訊已經以";"分隔的形式儲存。同時,在資料讀取時,我們也無需面對原始的";"分隔字串,轉而只需要處理List類型資料即可。