MyBatis之TypeHandler
在大学写web应用的时候经常会遇到这么个问题,当我要插入一条数据,某个数据是Date类型,数据库中却是VARCHAR类型,这个时候可能会傻乎乎的先把这个数据自己手动转换成String类型再插入到数据库中,其实大可不必。MyBatis为我们提供了更好的方法即是TypeHandler来应对Java和jdbc字段类型不匹配的情况。MyBatis中内置了不少的TypeHandler,如果我们想要自己自定义一个TypeHandler可以实现TypeHandler接口,也可以继承BaseTypeHandler类。下面我们实现一个将Java中的Date类型利用我们自定义的ExampleTypeHandler来转换为JDBC的VARCHAR类型。
我们对MyBatis的介绍先局限在使用,在会使用过后我们再究其原理、源码。
1 package day_8_mybatis.util; 2 3 import java.sql.CallableStatement; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.util.Date; 8 9 import org.apache.ibatis.type.BaseTypeHandler; 10 import org.apache.ibatis.type.JdbcType; 11 12 /** 13 * 注意在引入Date所在的包时,是java.util.Date,而不是java.sql.Date,这一点不要搞错。 14 * @author turbo 15 * 16 * 2016年10月23日 17 */ 18 public class ExampleTypeHandler extends BaseTypeHandler<Date> { 19 20 /* 根据列名,获取可以为空的结果 21 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, java.lang.String) 22 */ 23 @Override 24 public Date getNullableResult(ResultSet rs, String columnName) 25 throws SQLException { 26 String sqlTimetamp = rs.getString(columnName); 27 if (null != sqlTimetamp){ 28 return new Date(Long.valueOf(sqlTimetamp)); 29 } 30 return null; 31 } 32 33 /* 根据列索引,获取可以为空的结果 34 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, int) 35 */ 36 @Override 37 public Date getNullableResult(ResultSet rs, int columnIndex) 38 throws SQLException { 39 String sqlTimetamp = rs.getString(columnIndex); 40 if (null != sqlTimetamp){ 41 return new Date(Long.valueOf(sqlTimetamp)); 42 } 43 return null; 44 } 45 46 /*47 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.CallableStatement, int) 48 */ 49 @Override 50 public Date getNullableResult(CallableStatement cs, int columnIndex) 51 throws SQLException { 52 String sqlTimetamp = cs.getString(columnIndex); 53 if (null != sqlTimetamp){ 54 return new Date(Long.valueOf(sqlTimetamp)); 55 } 56 return null; 57 } 58 59 /* 设置非空参数 60 * @see org.apache.ibatis.type.BaseTypeHandler#setNonNullParameter(java.sql.PreparedStatement, int, java.lang.Object, org.apache.ibatis.type.JdbcType) 61 */ 62 @Override 63 public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, 64 JdbcType jdbcType) throws SQLException { 65 ps.setString(i, String.valueOf(parameter.getTime())); 66 } 67 68 69 70 }
我们已经自定义了一个TypeHandler,接着我们要在mybatis-config.xml中注册。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 6 <configuration> 7 <!-- 注意configuration中各个属性配置的顺序应为:properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory,reflectorFactory,plugins,environments,databaseIdProvider,mappers)--> 8 <properties> 9 <property name="driver" value="com.mysql.jdbc.Driver"/> 10 <property name="url" value="jdbc:mysql://localhost:3306/test"/> 11 <property name="username" value="root"/> 12 <property name="password" value="0000"/> 13 </properties> 14 <typeHandlers> 15 <typeHandler handler="day_8_mybatis.util.ExampleTypeHandler" javaType="java.util.Date" jdbcType="VARCHAR"/> 16 </typeHandlers> 17 18 <environments default="development"> 19 <environment id="development"> 20 <transactionManager type="JDBC" /> 21 <dataSource type="POOLED"> 22 <property name="driver" value="${driver}"/> 23 <property name="url" value="${url}"/> 24 <property name="username" value="${username}"/> 25 <property name="password" value="${password}"/> 26 </dataSource> 27 </environment> 28 </environments> 29 <mappers> 30 <mapper resource="day_8_mybatis/mapper/UserMapper.xml"/> 31 <mapper resource="day_8_mybatis/mapper/NoteMapper.xml"/> 32 </mappers> 33 34 </configuration> 35 36
注意各个属性配置有顺序之分,不能随意穿插。
准备工作已经做完了,我们接着按部就班的实现POJO类 Note里面包含id和date字段。
1 package day_8_mybatis.pojo; 2 3 import java.util.Date; 4 5 /** 6 * @author turbo 7 * 8 * 2016年10月23日 9 */ 10 public class Note { 11 private int id; 12 private Date date; 13 public int getId() { 14 return id; 15 } 16 public void setId(int id) { 17 this.id = id; 18 } 19 public Date getDate() { 20 return date; 21 } 22 public void setDate(Date date) { 23 this.date = date; 24 } 25 }
接着是负责与数据库交互Dao层的NoteMapper接口,我们只举例查询和插入。
1 package day_8_mybatis.mapper; 2 3 import day_8_mybatis.pojo.Note; 4 5 /** 6 * @author turbo 7 * 8 * 2016年10月23日 9 */ 10 public interface NoteMapper { 11 Note queryNote(int id); 12 void insertNote(Note note); 13 }
我们再来看看在NoteMapper.xml中是如何利用我们刚才自定义的TypeHandler。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="day_8_mybatis.mapper.NoteMapper"> 6 <resultMap type="day_8_mybatis.pojo.Note" id="note-base"> 7 <result property="id" column="id"/> 8 <result property="date" column="date" typeHandler="day_8_mybatis.util.ExampleTypeHandler"/> 9 </resultMap> 10 11 <select id="queryNote" parameterType="int" resultMap="note-base"> 12 select * from note where id = #{id} 13 </select> 14 <insert id="insertNote" parameterType="day_8_mybatis.pojo.Note"> 15 insert into note (id, date) values(#{id}, #{date, typeHandler=day_8_mybatis.util.ExampleTypeHandler}) <!--使用我们自定义的TypeHandler--> 16 </insert> 17 </mapper>
最后我们在客户端测试一下。
1 package day_8_mybatis; 2 3 import java.io.IOException; 4 import java.util.Date; 5 6 import org.apache.ibatis.session.SqlSession; 7 8 import day_8_mybatis.mapper.NoteMapper; 9 import day_8_mybatis.pojo.Note; 10 import day_8_mybatis.util.SessionFactory; 11 12 /** 13 * 客户端 14 * @author turbo 15 * 16 * 2016年9月11日 17 */ 18 public class Main { 19 20 /** 21 * @param args 22 * @throws IOException 23 */ 24 public static void main(String[] args) throws Exception { 25 String resource = "day_8_mybatis/mybatis-config.xml"; //获取mybatis配置文件路径 26 SqlSession sqlSession = SessionFactory.getSqlSession(resource); //通过SessionFactory工具类(此工具类为自己构造即util包中的SessionFactory)构造SqlSession 27 28 NoteMapper noteMapper = sqlSession.getMapper(NoteMapper.class); 29 30 Note note = new Note(); 31 note.setId(1); 32 note.setDate(new Date()); 33 noteMapper.insertNote(note); //插入 34 sqlSession.commit(); //注意需要手动提交事务 35 36 note = noteMapper.queryNote(2); //查询 37 System.out.println(note.getDate()); 38 } 39 40 }
注意在34行代码,需要手动提交事务,默认是关闭自动提交的,所以必须手动提交。开始没有提交事务,无论怎么都没办法插入到数据库,后来debug单步调试的时候发现了autoCommit=false,才想起来在以前大学的时候也遇到过这个这个问题所以一下就定位问题在哪儿了。
数据库中只有一个note表,字段为id类型为int,date字段为varchar。
至此我们就完成了自定义的TypeHandler,其实MyBatis为我们提供的TypeHandler已经不少了,不过我们还是自己试验一把,先把MyBatis学会使用,再究其原理。
这是一个能给程序员加buff的公众号