最近看到hibernate的自定義類型,這個(gè)以前沒接觸過,在這里記錄一下,當(dāng)是對(duì)自己知識(shí)的鞏固,也讓沒有接觸過的朋友一起學(xué)習(xí)研究一番。
1)自定義類型,顧名思義,當(dāng)然就是由于內(nèi)部的類型不滿足需求,而自己來進(jìn)行實(shí)現(xiàn)的類型。這種情況不多,但我們還是有必要學(xué)習(xí)一下,技多不壓身嘛。也學(xué)習(xí)一下,別人在做框架的時(shí)候是怎么去考慮的,怎么去思考擴(kuò)展性的。
自定義類型有兩個(gè)方法來實(shí)現(xiàn),一種是實(shí)現(xiàn)UserType,另外一種實(shí)現(xiàn)CompositeUserType,另外可能還有一些方法,但我暫時(shí)沒用到,先不講了。
我暫時(shí)只用到UserType,我們就先看一下UserType接口的定義:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
public interface UserType { /** * Return the SQL type codes for the columns mapped by this type. The * codes are defined on <tt>java.sql.Types</tt>. */ public int [] sqlTypes(); /** * The class returned by <tt>nullSafeGet()</tt>. */ public Class returnedClass(); /** * Compare two instances of the class mapped by this type for persistence "equality". * Equality of the persistent state. */ public boolean equals(Object x, Object y) throws HibernateException; /** * Get a hashcode for the instance, consistent with persistence "equality" */ public int hashCode(Object x) throws HibernateException; /** * Retrieve an instance of the mapped class from a JDBC resultset. Implementors * should handle possibility of null values. */ public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException; /** * Write an instance of the mapped class to a prepared statement. Implementors * should handle possibility of null values. A multi-column type should be written * to parameters starting from <tt>index</tt>. */ public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException; /** * Return a deep copy of the persistent state, stopping at entities and at * collections. It is not necessary to copy immutable objects, or null * values, in which case it is safe to simply return the argument. */ public Object deepCopy(Object value) throws HibernateException; /** * Are objects of this type mutable? * * @return boolean */ public boolean isMutable(); /** * Transform the object into its cacheable representation. At the very least this * method should perform a deep copy if the type is mutable. That may not be enough * for some implementations, however; for example, associations must be cached as * identifier values. (optional operation) * * @param value the object to be cached * @return a cachable representation of the object * @throws HibernateException */ public Serializable disassemble(Object value) throws HibernateException; /** * Reconstruct an object from the cacheable representation. At the very least this * method should perform a deep copy if the type is mutable. (optional operation) */ public Object assemble(Serializable cached, Object owner) throws HibernateException; /** * During merge, replace the existing (target) value in the entity we are merging to * with a new (original) value from the detached entity we are merging. For immutable * objects, or null values, it is safe to simply return the first parameter. For * mutable objects, it is safe to return a copy of the first parameter. For objects * with component values, it might make sense to recursively replace component values. */ public Object replace(Object original, Object target, Object owner) throws HibernateException; } |
其實(shí)大家看英文一般情況下都能理解,不再多做解釋了,這里我們最主要的就是實(shí)現(xiàn)nullSafeSet() 方法,這個(gè)方法主要用到把此類型的值保存到數(shù)據(jù)庫,這一次我們先學(xué)怎么用,以后我們?cè)俾芯績(jī)?nèi)部是怎么來實(shí)現(xiàn)的。
2)我學(xué)習(xí)時(shí)寫的例子是參照夏昕的例子,所以肯定和網(wǎng)上的大部分都一樣,我們只是大概分析一下:
下面是User類
1
2
3
4
5
6
7
8
9
|
package org.hibernate.tutorial.domain; import java.io.Serializable; import java.util.List; public class User implements Serializable{ public Long id; private String name; private List emails; 省略Get/Set方法 } |
下來是自定義的EmailList類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
package org.hibernate.tutorial.domain; 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.usertype.UserType; public class EmailList implements UserType { private static final char SPLITTER = ';' ; private static final int [] TYPES = new int [] {Types.VARCHAR}; private String assemble(List emailList) { StringBuilder strBuf = new StringBuilder(); 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(); } private List parse(String value) { String[] strs = org.hibernate.util.StringHelper.split(value,String.valueOf(SPLITTER)); List emailList = new ArrayList(); for ( int i = 0 ;i < strs.length; i++) { emailList.add(strs[i]); } return emailList; } public Object deepCopy(Object value) throws HibernateException { List sourceList = (List)value; List targetList = new ArrayList(); targetList.add(sourceList); return targetList; } public Serializable disassemble(Object value) throws HibernateException { return null ; } public boolean equals(Object x, Object y) throws HibernateException { if (x == y) return true ; System.out.println( "X:" +x+ "Y:" +y); 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 boolean isMutable() { return false ; } 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); //把List通過;分割 } else { return null ; } } public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException { System.out.println( "Set Method Executed!" ); System.out.println( "value:" + value); if (value != null ){ String str = assemble((List)value); //把字符串用;拼接 Hibernate.STRING.nullSafeSet(st, str, index); } else { Hibernate.STRING.nullSafeSet(st, value, index); } } public Class returnedClass() { return List. class ; } public int [] sqlTypes() { return TYPES; } //省略其他不需要修改的方法 } |
類中實(shí)現(xiàn)的方法是需要修改的方法,其他不需要修改暫時(shí)不用的方法則沒有寫出來,但還是需要實(shí)現(xiàn)的。
3)接下來就是User類的映射文件:
1
2
3
4
5
6
7
|
< class name = "User" table = "USER" > < id name = "id" column = "USER_ID" type = "java.lang.Long" > < generator class = "native" /> </ id > < property name = "name" type = "string" column = "USER_NAME" /> < property name = "emails" type = "org.hibernate.tutorial.domain.EmailList" column = "emails" /> </ class > |
相信大家都知道怎么進(jìn)行修改,這里也不進(jìn)行講解了,主要是修改emails的type,修改為我們剛才定義的EmailList類。
4)最后我們來寫一個(gè)測(cè)試類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.ArrayList; import junit.framework.TestCase; import org.hibernate.EntityMode; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.tutorial.domain.User; public class HibernateTest extends TestCase{ private Session session = null ; protected void setUp() throws Exception { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); session = sessionFactory.openSession(); } public void testInsert(){ Transaction tran = null ; try { tran = session.beginTransaction(); User user = new User(); user.setName( "shun" ); List list = new ArrayList(); user.setEmails(list); session.save(user); tran.commit(); } catch (Exception ex) { ex.printStackTrace(); if (tran != null ){ tran.rollback(); } } } protected void tearDown() throws Exception { session.close(); } } |
這里可能會(huì)出現(xiàn)問題,當(dāng)我們只保存一個(gè)email時(shí),它會(huì)出現(xiàn)異常,在數(shù)據(jù)庫里面是email字段是空的,而當(dāng)我們?nèi)缟厦娲a一樣,有兩個(gè)時(shí),并不會(huì)出現(xiàn)問題,數(shù)據(jù)庫中結(jié)果如圖:
而當(dāng)我們只保存一個(gè)時(shí),異常如下:
1
|
java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String |
它發(fā)生在EmailList的equals方法中的String str1 = (String)xList.get(i);這句代碼中,經(jīng)檢查是在插入數(shù)據(jù)傳到EmailList的nullSafeSet方法時(shí)變成了List的List,即
value:[[[email protected], [email protected]]]這樣的形式,這樣在比較的時(shí)候就會(huì)出問題,它永遠(yuǎn)都只有一個(gè)值,而在比較的時(shí)候卻是不同的,
1
|
if (xList.size() != yList.size()) return false ; |
所以在強(qiáng)制轉(zhuǎn)換時(shí)會(huì)出問題。
而經(jīng)過檢查,equals方法里:
1
|
X:[[ 12312 @sfsdf .com, 123 @123 .com]]Y:[ 12312 @sfsdf .com, 123 @123 .com] |
這樣的結(jié)果卻是很奇怪的。網(wǎng)上并沒有講到為什么會(huì)出現(xiàn)這種情況。這里提出一下:我用的hibernate版本是Hibernate 3.3.2.GA。不知道是版本問題還是其他問題,我們明天再研究一下。如果有哪位兄弟知道為什么的,希望也不吝告訴我一下。