在使用mybatis进行db操作的时候,我们经常会干的一件事情就是将db中字段映射到java bean,通常我们使用ResultMap
来实现映射,通过这个标签可以指定两者的绑定关系,那么如果java bean中的字段类型与db中的不一样,应该怎么处理呢?
如db中为timestamp, 而java bean中定义的却是long
通过BaseTypeHandler
来实现自定义的类型转换
I. 环境准备 1. 数据库准备 使用mysql作为本文的实例数据库,新增一张表
1 2 3 4 5 6 7 8 9 10 CREATE TABLE `money` ( `id` int (11 ) unsigned NOT NULL AUTO_INCREMENT, `name` varchar (20 ) NOT NULL DEFAULT '' COMMENT '用户名' , `money` int (26 ) NOT NULL DEFAULT '0' COMMENT '钱' , `is_deleted` tinyint (1 ) NOT NULL DEFAULT '0' , `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' , `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' , PRIMARY KEY (`id` ), KEY `name` (`name` ) ) ENGINE =InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET =utf8mb4;
2. 项目环境 本文借助 SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
进行开发
pom依赖如下
1 2 3 4 5 6 7 8 9 10 11 <dependencies > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.0</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > </dependencies >
db配置信息 application.yml
1 2 3 4 5 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password:
II. 实例演示 1. entity定义 注意上面case中的create_at
与 update_at
的类型都是timestmap
,我们定义的Entity如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Data public class MoneyPo { private Integer id; private String name; private Long money; private Integer isDeleted; private Timestamp createAt; private Long updateAt; }
2. Mapper测试接口 定义一个简单的查询接口,这里直接使用注解的方式(至于xml的写法差别也不大)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Select ("select * from money where id = #{id}" )@Results (id = "moneyResultMap" , value = { @Result (property = "id" , column = "id" , id = true , jdbcType = JdbcType.INTEGER), @Result (property = "name" , column = "name" , jdbcType = JdbcType.VARCHAR), @Result (property = "money" , column = "money" , jdbcType = JdbcType.INTEGER), @Result (property = "isDeleted" , column = "is_deleted" , jdbcType = JdbcType.TINYINT), @Result (property = "createAt" , column = "create_at" , jdbcType = JdbcType.TIMESTAMP), @Result (property = "updateAt" , column = "update_at" , jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class )}) MoneyPo getById(@Param("id") int id); @SelectProvider (type = MoneyService.class , method = "getByIdSql" )@ResultMap (value = "moneyResultMap" )MoneyPo getByIdForProvider (@Param("id" ) int id) ;
说明:
@Results
: 这个注解与 ResultMap 标签效果一致,主要用于定义db的字段与java bean的映射关系
id = "moneyResultMap"
这个id定义,可以实现@Results的复用
@Result
: 关注下updateAt
的typeHandler,这里指定了自定义的TypeHandler,来实现JdbcType.TEMSTAMP
与Java Bean中的long的转换
3. 类型转换 自定义类型转换,主要是继承BaseTypeHandler
类,泛型的类型为Java Bean中的类型
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 @MappedTypes (value = Long.class ) @MappedJdbcTypes (value = {JdbcType.DATE, JdbcType.TIME, JdbcType.TIMESTAMP})public class Timestamp2LongHandler extends BaseTypeHandler <Long > { @Override public void setNonNullParameter (PreparedStatement preparedStatement, int i, Long aLong, JdbcType jdbcType) throws SQLException { if (jdbcType == JdbcType.DATE) { preparedStatement.setDate(i, new Date(aLong)); } else if (jdbcType == JdbcType.TIME) { preparedStatement.setTime(i, new Time(aLong)); } else if (jdbcType == JdbcType.TIMESTAMP) { preparedStatement.setTimestamp(i, new Timestamp(aLong)); } } @Override public Long getNullableResult (ResultSet resultSet, String s) throws SQLException { return parse2time(resultSet.getObject(s)); } @Override public Long getNullableResult (ResultSet resultSet, int i) throws SQLException { return parse2time(resultSet.getObject(i)); } @Override public Long getNullableResult (CallableStatement callableStatement, int i) throws SQLException { return parse2time(callableStatement.getObject(i)); } private Long parse2time (Object value) { if (value instanceof Date) { return ((Date) value).getTime(); } else if (value instanceof Time) { return ((Time) value).getTime(); } else if (value instanceof Timestamp) { return ((Timestamp) value).getTime(); } return null ; } }
setNonNullParameter:将java类型,转换为jdbc类型
getNullableResult:将jdbc类型转java类型
4. TypeHandler注册 我们自己定义一个TypeHandler没啥问题,接下来就是需要它生效,一般来讲,有下面几种方式
4.1 result标签中指定 通过result标签中的typeHandler指定
使用xml的方式如
1 <result column ="update_at" property ="updateAt" jdbcType ="TIMESTAMP" typeHandler ="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler" />
注解@Result的方式如
1 @Result (property = "updateAt" , column = "update_at" , jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class )
4.2 SqlSessionFactory全局配置 上面的使用姿势为精确指定,如果我们希望应用到所有的场景,则可以通过SqlSessionFactory
来实现
1 2 3 4 5 6 7 8 9 10 11 @Bean (name = "sqlSessionFactory" )public SqlSessionFactory sqlSessionFactory (DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml" )); bean.setTypeHandlers(new Timestamp2LongHandler()); return bean.getObject(); }
4.3 全局xml配置 除上面case之外,还有一个就是借助mybatis-config.xml
配置文件来注册,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//ibatis.apache.org//DTD Config 3.1//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings > <typeHandlers > <typeHandler handler ="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler" /> </typeHandlers > </configuration >
注意,使用上面的配置文件,需要在SpringBoot中指定如下配置,否则将不会生效
1 2 mybatis: config-location: classpath:mybatis-config.xml
4.4 SpringBoot配置方式 springboot配置文件,可以通过指定type-handlers-package
来注册TypeHandler
1 2 mybatis: type-handlers-package: com.git.hui.boot.mybatis.handler
5. 小结 本文主要介绍db中的类型与java bean中类型的映射适配策略,主要是通过继承BaseTypeHandler
来实现自定义的类型转化
要使用自定义的TypeHandler,有全局生效与精确指定两种方式
@Result
/<result>
标签中,通过typeHandler指定
SqlSessionFactory 全局设置typeHandler
mybatis-config.xml
配置文件设置typeHandlers
此外本文的配置中,还支持了驼峰与下划线的互转配置,这个也属于常见的配置,通过在mybatis-config
中如下配置即可
1 <setting name ="mapUnderscoreToCamelCase" value ="true" />
接下来问题来了,驼峰可以和下划线互转,那么有办法实现自定义的name映射么,如果有知道的小伙伴,请不吝指教
III. 不能错过的源码和相关知识点 0. 项目
mybatis系列博文
1. 一灰灰Blog 尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
一灰灰blog
Be the first person to leave a comment!