本篇介紹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();
}
}
請君動手搞一搞,為求得一個體會