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