浅谈Mybatis+mysql 存储Date类型的坑

yizhihongxing

下面是详细讲解 “浅谈Mybatis+mysql 存储Date类型的坑”的完整攻略。

问题描述

在使用 Mybatis + mysql 存储 Date 类型的数据时,我们可能会遇到以下两个问题:

  1. java.util.Date 类型无法直接存储到 mysql 数据库中;
  2. 存储后读取出来的 Date 类型的数据丢失了时区信息。

接下来我们将着重分析这两个问题并提供解决方案。

问题一:java.util.Date 无法直接存储到 mysql 数据库中

若我们直接使用 Mybatis + mysql 存储 java.util.Date 类型的数据,往往会发生以下错误:

Cause: java.sql.SQLException: Value '2021-05-04 13:20:14.000000' can not be represented as java.sql.Timestamp

这是因为在 mysql 中,timestamp 类型支持的最大范围只到 2038 年,而 java.util.Date 类型支持的范围则远远超出了这个范围,因此我们需要将 Date 类型转换为 mysql 支持的类型后再进行存储。

解决方案是通过 MySQL 中的 TIMESTAMP 表示,将 java.util.Date 转换成 java.sql.Timestamp 再插入数据库。Mybatis 会自动将 java.sql.Timestamp 类型转换成 MySQL 的 timestamp 类型进行存储。

具体实现方法如下:

在 Mapper 接口中定义 insert 方法:

public interface UserMapper {
    void insert(User user);
}

在对应的 UserMapper.xml 文件中,写入 insert 的 SQL 语句:

<insert id="insert" parameterType="com.example.demo.entity.User">
    insert into user(id, name, birth)
    values (#{id}, #{name}, #{birth, jdbcType=TIMESTAMP})
</insert>

其中 jdbcType=TIMESTAMP 表示将 Java 对象的类型转换成 jdbc 中的数据类型。

在实体类 User 中将出生日期字段 birth 指定为 java.sql.Timestamp 类型即可:

public class User {
    private Integer id;
    private String name;
    private Timestamp birth;

    // getter & setter
}

这样,我们就成功地解决了将 Java 中的 Date 类型存储到 mysql 数据库中的问题。

问题二:Date 类型的数据丢失时区信息

在存储 Date 类型的数据到 mysql 数据库中时,我们可能会发现 Date 类型的数据丢失了时区信息。举个例子,如果在东八区本地时间为 2021 年 5 月 4 日 20 时 28 分时,插入数据库时得到的时间会变成 2021 年 5 月 4 日 12 时 28 分。这是因为 mysql 数据库存储的是 UTC 时间,而不是本地时间。

解决方法是将 Java 日期类型转换成 UTC 格式的字符串存储到数据库中,在读取数据时再将字符串转换成 Java 日期类型。具体实现方法如下:

在 Mapper 接口中定义 insert 和 selectById 两个方法:

public interface UserMapper {
    void insert(User user);

    @Select("select * from user where id = #{id}")
    @Results({
            @Result(property = "birth", column = "birth", jdbcType = JdbcType.VARCHAR, typeHandler = LocalDateStringTypeHandler.class)
    })
    User selectById(Integer id);
}

在对应的 UserMapper.xml 文件中,写入 insert 和 selectById 的 SQL 语句:

<insert id="insert" parameterType="com.example.demo.entity.User">
    insert into user(id, name, birth)
    values (#{id}, #{name}, #{birth, jdbcType=VARCHAR})
</insert>
<select id="selectById" parameterType="java.lang.Integer" resultType="com.example.demo.entity.User">
    select * from user where id = #{id}
</select>

其中,selectById 中的 typeHandler = LocalDateStringTypeHandler.class 表示读取数据时使用自定义类型处理器 LocalDateStringTypeHandler 对 VARCHAR 类型的数据进行转换。

创建一个 LocalDateStringTypeHandler 类,实现将 Java 日期类型转换成 UTC 格式的字符串,和将 UTC 格式的字符串转换成 Java 日期类型的方法:

public class LocalDateStringTypeHandler extends BaseTypeHandler<Date> {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        // 将日期类型转换成 UTC 格式的字符串存储到数据库中
        ps.setString(i, dateFormat.format(parameter));
    }

    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 将 UTC 格式的字符串转换成本地日期类型
        return rs.getTimestamp(columnName);
    }

    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getTimestamp(columnIndex);
    }

    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getTimestamp(columnIndex);
    }
}

这样,我们的问题就完美地解决了。

示例说明

这里给出两个示例,用于说明上述两个问题的详细解决方案。

示例一:Java 代码存储 Date 类型到 MySQL

假设我们有一个 User 实体类,其中有一个生日字段 birth:

public class User {
    private Integer id;
    private String name;
    private Date birth;

    // getter & setter
}

我们需要将生日字段存储到 mysql 数据库中。首先,在 UserMapper 接口中定义 insert 方法:

public interface UserMapper {
    void insert(User user);
}

然后,在 UserMapper.xml 文件中,写入 insert 的 SQL 语句:

<insert id="insert" parameterType="com.example.demo.entity.User">
    insert into user(id, name, birth)
    values (#{id}, #{name}, #{birth, jdbcType=TIMESTAMP})
</insert>

其中 jdbcType=TIMESTAMP 表示将 Java 对象的类型转换成 JDBC 中的数据类型。

接下来,我们需要创建一个 mybatis-config.xml 文件,用于配置 Mybatis 的全局配置项。在 mybatis-config.xml 中加入如下内容:

<configuration>
    <typeHandlers>
        <typeHandler handler="com.example.demo.typehandler.LocalDateTimeTypeHandler" javaType="java.util.Date"/>
    </typeHandlers>
</configuration>

其中,com.example.demo.typehandler.LocalDateTimeTypeHandler 是我们自定义的类型处理器。

最后,我们需要在 UserMapper.xml 中配置插入生日字段的 TypeHandler:

<insert id="insert" parameterType="com.example.demo.entity.User">
    insert into user(id, name, birth)
    values (#{id}, #{name}, #{birth, typeHandler=com.example.demo.typehandler.LocalDateTimeTypeHandler})
</insert>

这样,我们就成功地将 Date 类型的数据存储到 mysql 数据库中了。

示例二:Java 代码读取 MySQL 中的 Date 类型数据

在读取 mysql 数据库中的 Date 类型数据时,我们需要将 UTC 格式的时间转换成本地时间。下面是示例代码:

public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User selectById(Integer id) {
        User user = userMapper.selectById(id);
        user.setBirth(convertToLocalTime(user.getBirth()));
        return user;
    }

    private Date convertToLocalTime(Date utcTime) {
        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        df.setTimeZone(tz);
        String localTime = df.format(utcTime);
        try {
            return df.parse(localTime);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

其中,convertToLocalTime 方法用于将传入的时间转换成本地时间。我们通过 java.util.TimeZone 类获取到中国时区,并通过 java.text.SimpleDateFormat 类将 UTC 格式的 Date 类型转换成本地时间类型。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Mybatis+mysql 存储Date类型的坑 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • Oracle数据创建虚拟列和复合触发器的方法

    下面是详细讲解“Oracle数据创建虚拟列和复合触发器的方法”的完整攻略。 创建虚拟列 确定需要创建虚拟列的表,并确认虚拟列的计算公式。 使用 ALTER TABLE 语句添加虚拟列,语法如下: sql ALTER TABLE table_name ADD (column_name data_type [GENERATED ALWAYS] AS (expre…

    database 2023年5月21日
    00
  • centos6.8下redis的安装和配置

    下载、安装 在redis官网可以获取到最新版本的redis 进入/usr/local/目录,执行如下命令 wget http://download.redis.io/releases/redis-4.0.2.tar.gztar xzf redis-4.0.2.tar.gzcd redis-4.0.2make 执行make构建redis时报如下错误,这是因为没…

    Redis 2023年4月13日
    00
  • SQL 计算一年有多少天

    计算一年有多少天在SQL中可以使用日期函数和算术运算符来实现。下面是SQL计算一年有多少天的完整攻略: 使用日期函数和算术运算符计算一年有多少天 在SQL中,可以使用日期函数和算术运算符来计算一年有多少天。具体步骤如下: 使用DATEFROMPARTS函数获取今年的年份: sql SELECT DATEFROMPARTS(YEAR(GETDATE()), 1…

    database 2023年3月27日
    00
  • Centos7下安装和配置MySQL5.7.20的详细教程

    下面是详细的“Centos7下安装和配置MySQL5.7.20的详细教程”。 1. 安装MySQL 1.1 下载MySQL软件包 从MySQL官方网站下载MySQL 5.7.20的版本压缩包,下载地址为 https://dev.mysql.com/downloads/mysql/5.7.html 。 建议下载“Generic Linux (Architect…

    database 2023年5月22日
    00
  • MySQL与Oracle差异比较之五存储过程&Function

    MySQL与Oracle差异比较之存储过程&Function 存储过程 MySQL中的存储过程 MySQL中的存储过程是一组SQL语句的集合,可以保存并重复使用,类似于函数的概念。与函数的区别是,存储过程可以接受参数和返回结果集合。存储过程在MySQL中通常使用DELIMITER语句进行定义,并使用CALL语句进行调用。 示例: DELIMITER …

    database 2023年5月21日
    00
  • mysql 数据库备份的多种实现方式总结

    MySQL 数据库备份的多种实现方式总结 在使用MySQL时,为了防止数据丢失或者出现问题,在定期备份MySQL数据库是非常必要的。目前有多种备份MySQL数据库的方法,下面将详细讲解各种方法的实现步骤。 1. 使用 mysqldump 命令备份 mysqldump 是MySQL自带的备份工具,我们可以使用这个工具将MySQL中的数据全部导出并保存到一个文件…

    database 2023年5月21日
    00
  • php+mysql实现微信公众号回复关键词新闻列表

    非常抱歉,我之前理解有误。如果您想要实现在公众号发送关键词,返回新闻列表的功能,可以按照以下步骤进行操作: 1. 创建一个数据库表,用于存储新闻的标题、链接和内容等信息。例如,可以创建一个名为news的表,包含id、title、link和content等字段。 2. 在公众号后台设置自定义菜单或关键词回复,将用户发送的关键词作为参数传递到后台程序。 3. 在…

    MySQL 2023年4月16日
    00
  • mysql无法成功启动服务的解决方法(通俗易懂!)

    下面是详细讲解如何解决 MySQL 启动服务失败的问题的完整攻略。 问题描述 当我们在 Windows 系统上安装 MySQL 数据库后,有可能会遇到无法成功启动 MySQL 服务的问题。此时,我们可以按照下面的步骤来解决这个问题。 解决方法 方法一:查看错误日志 首先,在 MySQL 安装目录下找到 data 目录,然后再进入 data 目录下的 host…

    database 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部