MyBatis中使用LocalDateTime

本文详细介绍了如何在MyBatis中使用Java 8的JSR310日期时间API,包括配置依赖、修改mybatis-config.xml、调整mapper.xml以及通过generator插件自动生成代码的过程。

背景

项目中使用MySQL数据库,然后用mybatis做数据持久化。最近使用时,想把一些model类的gmtCreate、gmtModified等字段从java.util.Date 改成java8的java.time.LocalDateTime,此类是不可变类,且自带很好用的日期函数api。

原本依赖如下:

        compile "org.mybatis:mybatis:3.3.0"
        compile "org.mybatis:mybatis-spring:1.2.5"
        compile "org.mybatis.generator:mybatis-generator-core:1.3.2"

直接修改代码有两两个问题:
1. mapper、model、xml文件都是用generator插件生成,维护成本高。
2. 会报错如下:


Caused by: java.lang.IllegalStateException: No typehandler found for property createTime
	at org.apache.ibatis.mapping.ResultMapping$Builder.validate(ResultMapping.java:151)
	at org.apache.ibatis.mapping.ResultMapping$Builder.build(ResultMapping.java:140)
	at org.apache.ibatis.builder.MapperBuilderAssistant.buildResultMapping(MapperBuilderAssistant.java:382)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildResultMappingFromContext(XMLMapperBuilder.java:378)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:280)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElement(XMLMapperBuilder.java:252)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.resultMapElements(XMLMapperBuilder.java:244)
	at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:116)

修改

首先,添加了以下依赖:(jsr310是java规范310)

compile "org.mybatis:mybatis-typehandlers-jsr310:1.0.2"

接着,在mybatis-config.xml 中添加如下配置:(该配置来源于官网

<typeHandlers>
  <!-- ... -->
  <typeHandler handler="org.apache.ibatis.type.InstantTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalDateTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalDateTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.OffsetDateTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.OffsetTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.ZonedDateTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.YearTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.MonthTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.YearMonthTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.JapaneseDateTypeHandler" />
</typeHandlers>

看了文档发现需要mybatis 3.4.0版本

compile "org.mybatis:mybatis:3.4.0"

不过这里有个问题了,会出现版本不兼容问题,报错如下:

java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer;
	at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:85)
	at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:62)
	at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
	at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
	at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

查看发现 SpringManagedTransaction未实现Transaction中的getTimeout() 方法

/**
 * Wraps a database connection.
 * Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close. 
 *
 * @author Clinton Begin
 */
public interface Transaction {

  /**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

}

 

查了文档发现:Spring版本跟Mybatis版本有个对照表要满足,详情见官网
 而我需要的mapper.xml文件类似如下:

<mapper namespace="com.my.MyMapper">
    <resultMap id="BaseResultMap" type="com.my.MyModel">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="gmt_create" jdbcType="OTHER" property="gmtCreate" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler"/>

    </resultMap>
    <!--- 省略 --->

</mapper>

之后,查了generator的官方文档,发现还是可以通过generator.xml的配置文件做到的。

        <table tableName="my_table" domainObjectName="MyModel">
            <generatedKey column="id" sqlStatement="JDBC" identity="true"/>
            <columnOverride column="gmt_create" property="gmtCreate" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler" jdbcType="OTHER" javaType="java.time.LocalDateTime" />
            <columnOverride column="gmt_modified" property="gmtModified" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler" jdbcType="OTHER" 
            <!-- 省略其他 -->
        </table>

这里解释下:
columnOverride将数据库中的字段重命名为实体类的属性
column:数据库中字段名
property:POJO属性名
javaType:POJO类型,这里用的是java.time.LocalDateTime
jdbcType:数据库字段类型,SQL中是timestamp,但这里要写OTHER,否则typehandler不处理
typehandler:帮我们处理类型的

此时再用generator生成的mapper.xml就是我想要的了

 

扩展

严格来说生成的mapper.xml是不能修改的。可实际中,很多时候需要我们再手写自定义的查询。此时可以再生成一个扩展的xml文件。或者在generator里面配置插件也会生成一个空的:

mybatis-ext

 

转义字符

在xml里面有些表达式是不允许的,需要转义:

含意思

原符号

转义后

小于号

<

&lt;

小于等于

<=

&lt;=

大于号

>

&gt;

大于等于

>=  

&gt;=

&

&amp;

单引号

&apos;

双引号

&quot;

<think>好的,我现在需要解决用户的问题:在MyBatis中将timestampz类型的数据转换为Java的LocalDateTime类型。用户提到之前遇到了类型不匹配的错误,比如“invalid comparison: java.time.LocalDateTime and java.lang.String”和映射错误。我需要结合提供的引用内容来寻找解决方案。 首先,根据引用[2],用户可能尝试在实体类中使用LocalDateTime,但MyBatis默认可能不支持,导致查询错误。需要确认MyBatis的配置是否正确处理了类型转换。引用[3]提到MyBatis Generator生成代码时需要注意配置,避免覆盖javaType导致缺少import语句。 接下来,用户需要具体的转换示例。可能的解决方案包括: 1. **使用TypeHandler**:MyBatis通过自定义TypeHandler来处理数据库类型和Java类型之间的转换。对于PostgreSQL的timestampz(带时区的时间戳),可能需要编写一个TypeHandler,将ResultSet中的Timestamp对象转换为LocalDateTime。 2. **配置MyBatis Generator**:确保生成代码时正确指定字段类型为LocalDateTime,并避免覆盖javaType导致的问题,如引用[3]所述。 3. **检查数据库驱动兼容性**:确保使用的JDBC驱动支持Java 8时间类型,例如PostgreSQL的驱动应较新版本。 在实现过程中,需要注意数据库类型是否正确映射,例如timestampz在PostgreSQL中对应的是TIMESTAMP WITH TIME ZONE。TypeHandler需要正确处理时区信息,可能需要在转换时使用适当的时区设置。 最后,验证配置是否正确,编写测试用例确保查询时没有类型错误,数据能正确映射到LocalDateTime字段。</think>### MyBatis中timestampz到LocalDateTime的转换示例 在MyBatis中将数据库的`timestampz`(如PostgreSQL的`TIMESTAMP WITH TIME ZONE`)映射为Java的`LocalDateTime`,需要通过自定义类型处理器(`TypeHandler`)实现类型转换。以下是具体步骤: --- #### 1. **编写自定义TypeHandler** 创建一个实现`org.apache.ibatis.type.TypeHandler`接口的类,用于处理`java.time.LocalDateTime`与数据库时间类型的转换: ```java import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import java.sql.*; import java.time.LocalDateTime; import java.time.ZoneId; public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> { @Override public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException { // 将LocalDateTime转换为Timestamp(带时区信息) Timestamp timestamp = Timestamp.from(parameter.atZone(ZoneId.systemDefault()).toInstant()); ps.setTimestamp(i, timestamp); } @Override public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { // 从ResultSet中读取Timestamp并转为LocalDateTime Timestamp timestamp = rs.getTimestamp(columnName); return timestamp != null ? timestamp.toLocalDateTime() : null; } @Override public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { Timestamp timestamp = rs.getTimestamp(columnIndex); return timestamp != null ? timestamp.toLocalDateTime() : null; } @Override public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { Timestamp timestamp = cs.getTimestamp(columnIndex); return timestamp != null ? timestamp.toLocalDateTime() : null; } } ``` --- #### 2. **配置MyBatis使用TypeHandler** 在`mybatis-config.xml`中注册自定义的`TypeHandler`: ```xml <configuration> <typeHandlers> <typeHandler handler="com.example.handler.LocalDateTimeTypeHandler" javaType="java.time.LocalDateTime" jdbcType="TIMESTAMP"/> </typeHandlers> </configuration> ``` --- #### 3. **修改实体类字段类型** 确保实体类中使用`LocalDateTime`类型映射数据库的`timestampz`字段: ```java public class MyEntity { private LocalDateTime createTime; // 对应数据库timestampz字段 // getter/setter省略 } ``` --- #### 4. **调整MyBatis Generator配置** 若使用MyBatis Generator生成代码,需在配置文件中避免覆盖字段类型(参考引用[3]): ```xml <context id="default"> <jdbcConnection driver="org.postgresql.Driver" ... /> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <table tableName="your_table"> <!-- 避免覆盖字段类型 --> <!-- 错误示例:<columnOverride column="create_time" javaType="LocalDateTime"/> --> </table> </context> ``` --- #### 5. **验证SQL映射文件** 在Mapper XML中确保字段映射正确,无需手动指定类型: ```xml <select id="selectById" resultType="MyEntity"> SELECT create_time FROM your_table WHERE id = #{id} </select> ``` --- #### **注意事项** - **时区处理**:数据库的`timestampz`包含时区信息,`LocalDateTime`默认使用系统时区。若需显式指定时区,可在转换时调整`ZoneId`(如`ZoneId.of("UTC")`)。 - **驱动兼容性**:确保使用支持JDBC 4.2及以上版本的数据库驱动(如PostgreSQL JDBC Driver 42.x+)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值