在做開發(fā)時,我們經(jīng)常會遇到這樣一些問題,比如我有一個java中的Date數(shù)據(jù)類型,我想將之存到數(shù)據(jù)庫的時候存成一個1970年至今的毫秒數(shù),怎么實現(xiàn)?再比如我有一個User類,User類中有一個屬性叫做interest,這個屬性用來描述用戶的愛好,它的數(shù)據(jù)類型是一個List集合,那么我想在把這個List集合存入數(shù)據(jù)庫的時候能夠自動的變成{XXX,XXX,XXX}
這樣一個字符串然后存起來,當(dāng)我從數(shù)據(jù)庫讀取的時候也是讀取到這樣一個字符串,讀取成功之后再自動的將之轉(zhuǎn)為一個List集合,OK,以上兩種需求用我們傳統(tǒng)的數(shù)據(jù)庫讀寫操作肯定都是可以實現(xiàn)的,只不過工作量略大,在mybatis中有一個功能略強大的typeHandler專門用來解決數(shù)據(jù)庫中的數(shù)據(jù)類型和Java中的數(shù)據(jù)類型之間的轉(zhuǎn)化問題,那么我們今天以上面兩種需求為例,來看看typeHandler要怎么使用。如果還有小伙伴對mybatis不太熟悉,建議先閱讀一下前面幾篇博客(初識mybatis/初識mybatis(二)/mybatis常用配置/mybatis映射器配置細(xì)則),本文的內(nèi)容將在前面幾篇博客的基礎(chǔ)上展開,當(dāng)然如果小伙伴有mybatis基礎(chǔ),那直接往下看即可。 事實上,mybatis本身已經(jīng)為我們提供了許多typeHandler了,系統(tǒng)提供的typeHandler能夠滿足我們?nèi)粘i_發(fā)中的大部分需求,如上這兩種特殊的需求就需要我們自己去定義typeHandler了。
先來看日期的轉(zhuǎn)換,假設(shè)我現(xiàn)在創(chuàng)建一張表,如下: 這張表中有一個字段叫做regTime,這個字段表示用戶的注冊時間,它的數(shù)據(jù)類型為varchar,OK,然后我再在Java中定義一個實體類:
這個JavaBean中也有一個regTime字段,不同的是這里的數(shù)據(jù)類型是Date,OK,如果我不做任何特殊處理,直接像初識mybatis(二)這篇博客中介紹的那樣向數(shù)據(jù)庫插入數(shù)據(jù),也是可以插入成功的,但是插入成功后是這樣: 這個當(dāng)然不是我想要的,我希望存到數(shù)據(jù)庫里的是這樣的:
就是我直接向數(shù)據(jù)庫寫數(shù)據(jù),要寫的是一個Date對象,但是寫到數(shù)據(jù)庫之后這個Date對象就變成了Date對象所描述的時間到1970年的秒數(shù)了,然后當(dāng)我從數(shù)據(jù)庫讀取這個秒數(shù)之后,系統(tǒng)又會自動幫我將這個秒數(shù)轉(zhuǎn)為Date對象,就是這樣兩個需求。這個時候,我們要做的事情其實很簡單,那就是自定義typeHandler,自定義typeHandler我們有兩種方式,一種是實現(xiàn)TypeHandler接口,還有一種簡化的寫法就是繼承自BaseTypeHandler類,我這里先以第二種為例來進(jìn)行說明。
關(guān)于這個類我說如下幾點:
1.@MappedJdbcTypes定義的是JdbcType類型,這里的類型不可自己隨意定義,必須要是枚舉類org.apache.ibatis.type.JdbcType所枚舉的數(shù)據(jù)類型。 2.@MappedTypes定義的是JavaType的數(shù)據(jù)類型,描述了哪些Java類型可被攔截。 3.在我們啟用了我們自定義的這個TypeHandler之后,數(shù)據(jù)的讀寫都會被這個類所過濾 4.在setNonNullParameter方法中,我們重新定義要寫往數(shù)據(jù)庫的數(shù)據(jù)。 5.在另外三個方法中我們將從數(shù)據(jù)庫讀出的數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換。
自定義好了typeHandler之后,接下來我們需要在userMapper.xml中進(jìn)行簡單的配置,首先我們可以像上文說的,配置resultMap,如下:
<resultMap id="userResultMap" type="org.sang.bean.User"> <result typeHandler="org.sang.db.MyDateTypeHandler" column="regTime" javaType="java.util.Date" jdbcType="VARCHAR" property="regTime"/> </resultMap>配置resultMap的時候我們指定了javaType和jdbcType,同時也指定了處理的typeHandler,然后在select中使用這個resultMap:
<select id="getUser" resultMap="userResultMap"> select * from user4 </select>但是這種方式有一個缺點那就是只適用于查詢操作,即在查詢的過程中系統(tǒng)會啟用我們自定義的typeHandler,會將秒數(shù)轉(zhuǎn)為Date對象,但是在插入的時候卻不會啟用我們自定義的typeHandler,想要在插入的時候啟用自定義的typeHandler,需要我們在insert節(jié)點中簡單配置一下,如下:
<insert id="insertUser" parameterType="org.sang.bean.User"> INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR,typeHandler=org.sang.db.MyDateTypeHandler}) </insert>也可以只配置javaType和jdbcType,如下:
<insert id="insertUser2"> INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,javaType=Date,jdbcType=VARCHAR}) </insert>或者只配置typeHandler:
<insert id="insertUser3"> INSERT INTO user4(username,password,regTime) VALUES (#{username},#{password},#{regTime,typeHandler=org.sang.db.MyDateTypeHandler}) </insert>這三種效果都是一樣的,都是在插入的時候?qū)?shù)據(jù)Date對象轉(zhuǎn)為秒數(shù)。OK,如此之后,我們就可以實現(xiàn)將Date對象插入數(shù)據(jù)庫之后變秒數(shù)以及將數(shù)據(jù)庫中的秒數(shù)讀取之后自動轉(zhuǎn)為Date對象了。我們來看一個簡單的測試:
@Test public void test2() { Sqlsession sqlSession = null; try { sqlSession = DBUtils.openSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setPassword("222222"); user.setUsername("李四"); Date regTime = new Date(); user.setRegTime(regTime); userMapper.insertUser(user); sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); sqlSession.rollback(); } finally { if (sqlSession != null) { sqlSession.close(); } } }插入結(jié)果如下:
讀取代碼:
@Test public void test1() { SqlSession sqlSession = null; try { sqlSession = DBUtils.openSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> list = userMapper.getUser(); for (User user : list) { System.out.println(user); } sqlSession.commit(); } catch (Exception e) { e.printStackTrace(); sqlSession.rollback(); } finally { if (sqlSession != null) { sqlSession.close(); } } }讀取結(jié)果如下:
OK,結(jié)果上面幾步配置我們就完美的解決了讀寫時的數(shù)據(jù)轉(zhuǎn)換問題了,讀取時的數(shù)據(jù)轉(zhuǎn)換除了我們上面介紹的定義resultMap然后在select節(jié)點中引用這種方式之外,也可以使用下面這種方式,注意下面這種方式只能解決讀取時的數(shù)據(jù)轉(zhuǎn)換問題
我們需要在我們的mybatis配置文件中注冊typeHandler,注冊有兩種不同的方式,可以像下面這樣一個類一個類的注冊:
<typeHandlers> <typeHandler handler="org.sang.db.MyDateTypeHandler"/> </typeHandlers>也可以直接注冊一個包中所有的typeHandler,系統(tǒng)在啟動時會自動掃描包下的所有文件,如下:
<typeHandlers> <package name="org.sang.db"/> </typeHandlers>這樣配置完成之后,我們的目的就達(dá)到了,當(dāng)我們進(jìn)行數(shù)據(jù)庫的讀取操作的時候,秒數(shù)就會自動轉(zhuǎn)為Date對象。
OK,經(jīng)過上面的介紹,想必小伙伴對typeHandler的使用已經(jīng)有一定了解了,總結(jié)一下就是讀取時的配置要和插入時的配置分貝來做,讀取時數(shù)據(jù)轉(zhuǎn)換我們有兩種配置方式,分別是resultMap和在mybatis配置文件中配置typeHandlers,插入時的配置就是在insert節(jié)點中進(jìn)行配置。
OK,如果小伙伴們學(xué)會了如何把Date轉(zhuǎn)為秒數(shù),那么對于List集合的轉(zhuǎn)換我就不再贅述了,道理都是一樣的,大家可以可以直接在文末下載Demo,Demo中有List集合轉(zhuǎn)換的案例。 這里給大家看兩張效果圖吧: List集合存入數(shù)據(jù)庫之后變成這樣: 讀取出來之后又自動轉(zhuǎn)為List集合了,下圖是查詢操作,實體類,和查詢結(jié)果:
OK,以上就是我們對typeHandler的一個簡單介紹。
本文案例下載: 本文案例GitHub地址https://github.com/lenve/JavaEETest/tree/master/Test27-mybatis6。
以上。
參考資料: 《深入淺出MyBatis 技術(shù)原理與實戰(zhàn)》第三章
新聞熱點
疑難解答