1. MySQL的字段类型和Java类型的对应关系
MySQL Connector/J( MySQL官方JDBC驱动程序)在处理MySQL数据类型和Java数据类型之间的转换方面非常灵活。
- 通常,任何MySQL数据类型都可以转换为java.lang.String。
- 可以将字符串和任何数字类型转换为任何Java数字类型,尽管可能会发生舍入、溢出或精度损失。
从Connector/J 3.1.0开始,JDBC驱动程序会发出警告或引发JDBC规范所要求的DataTruncation异常(数据截断异常),除非通过使用jdbcCompliantTruncation属性并将其设置为false来将连接配置为不这样做。
MySQL和Java类型之间始终保证有效的转换
MySQL类型 | 可转换的Java类型 |
---|---|
char, varchar, blob, text, enum, set | java.lang.String , java.io.InputStream, java.io.Reader, java.sql.Blob, java.sql.Clob |
float, real, double, precision, numeric, decimal , tinyint, smallint, mediumint, integer, bigint | java.lang.String , java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double, java.math.BigDecimal |
date, time, datetime, timestamp | java.lang.String , java.sql.Date, java.sql.Timestamp |
注意 如果选择的Java数字数据类型的精度或容量低于要转换的MySQL数据类型,则可能会发生舍入、溢出或精度损失。
案例
2. 二者的对应关系(仅仅是推荐)
MySQL类型 | 对应Java类型 | 用途 |
---|---|---|
tinyint | 如果配置属性 tinyInt1isBit 设置为 true (默认)并且存储大小为1,则为 java.lang.Boolean,否则是 java.lang.Integer。 | |
int | java.lang.Integer, 如果是 unsigned 则是 java.lang.Long | |
integer | java.lang.Integer, 如果是 unsigned 则是 java.lang.Long | |
bigint | java.lang.Long, 如果是 unsigned 则是 java.math.BigInteger | |
double | java.lang.Double | |
float | java.lang.Float | |
decimal | java.math.BigDecimal | 金额 |
numeric | Java | |
char | java.lang.String (除非列的字符集是BINARY,否则返回byte[]。) | |
varchar | java.lang.String (除非列的字符集是BINARY,否则返回byte[]。) | |
date | java.sql.Date | 日期 |
time | java.sql.Time | 时间 HH:MM:SS |
timestamp | java.sql.Timestamp | 时间戳 YYYY-MM-DD HH:MM:SS |
datetime | java.sql.Timestamp | YYYY-MM-DD HH:MM:SS |
blob | byte[] | |
text | java.lang.String | |
longtext | java.lang.String | |
enum | java.lang.String |
表格中的对应关系仅仅是推荐的,具体如何映射可以根据业务需求进行变更 ,并不是定死就这样,如:Mysql中字段类型是Decimal类型,在满足业务需求的情况下,可以在Java类型中可以使用Double在对应的数据接收。
3. Sql中decimal、float、double类型的区别与用法
3.1 三者的区别介绍
float:浮点型,含字节数为4,32bit,数值范围为-3.4E383.4E38(7个有效位) double:双精度实型,含字节数为8,64bit数值范围-1.7E3081.7E308(15个有效位) decimal:数字型,128bit,不存在精度损失,常用于银行帐目计算。(28个有效位)
3.2 decimal的详细介绍
定义
decimal(a,b)
参数说明
- a 指定指定小数点左边和右边可以存储的十进制数字的最大个数,最大精度38。
- b 指定小数点右边可以存储的十进制数字的最大个数。小数位数必须是从 0 到 a之间的值。默认小数位数是 0。
3.3 对比案例
float f = 345.98756f; --结果显示为345.9876,只显示7个有效位,对最后一位数四舍五入。
double d=345.975423578631442d; --结果显示为345.975423578631,只显示15个有效位,对最后一位四舍五入。
--注:float和double的相乘操作,数字溢出不会报错,会有精度的损失。
decimal dd=345.545454879..--可以支持28位,对最后一位四舍五入。
--:当对decimal类型进行操作时,数值会因溢出而报错。
4. Mybatis中JdbcType的作用
个人疑问的由来:今天在学习公司项目代码的时候惊奇的发现Mybatis中的映射类型和Java中的映射类型不一致,Mysql中设置的字段类型是Decimal类型,但是在Java中实体对应属性的数据类型缺选择使用的是Double作为对应,并且在查看代码的时候发现属性映射中有使用到JdbcType这个字段,一开始怀疑是JdbcType的作用,查阅了资料有了以下的答案
对于如下一条insert语句(这里只是做测试,实际中肯定不会这么写),如果我们的age传的空,那么对于mysql数据库可以正常插入,对于oracle数据库,会报错“无效的列类型”。
也就是说对于mysql数据库的插入来说,jdbcType是没用的,oracle数据库是有用的(值为null时有用)。
insert into test(id, name, age)
values (7, #{name,jdbcType=VARCHAR},#{age})
我没有测试更新的情况,但是我猜测结论应该是一样的。**对于查询操作,在mybatis配置时也可以指定jdbcType,这里的jdbcType至少在我测试时也没发现有什么作用(mysql, oracle都是),**当然可能只是我没发现,不代表没有作用,不作为编码建议。
4.1 问题产生
insert操作时jdbcType的作用测试这里只分析insert操作时,为什么mysql和oracle表现会不同。
我们用如下代码来进行测试。
mybatis mapper中的sql如下,因为oracle自增主键比较麻烦,这里为了方便写死了id,仅测试用。
<insert id="testJdbcType">
insert into t_user(id, name, age)
values (7, #{name,jdbcType=VARCHAR}, #{age})
</insert>
java测试代码如下,这里就不贴全部代码了,用过mybatis应该都能看懂。
User user = new User();
user.setName("zzj");
user.setAge(null);
mapper.testJdbcType(user);
然后我们分别连接mysql和oracle
查看执行结果
- mysql可以正常插入
- 但是oracle数据库会报如下错误(Error setting null for parameter #2 with JdbcType OTHER . java.sql.SQLException: 无效的列类型)
4.2 原因分析
mybatis中的增删改查基本都是用PreparedStatement来实现的,PreparedStatement有个setNull方法 ,方法签名如下:
void setNull(int parameterIndex, int sqlType) throws SQLException;
当我们要插入的值是null时,mybatis就会调用PreparedStatement的setNull来设置对应的参数。
我们跟踪下mybatis执行过程,可以发现是在org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
这个方法中进行sql的参数设置。我们断点执行到这个方法,如下图
- 从上图可以发现,由于我们age参数没有设置jdbcType,JdbcType jdbcType = parameterMapping.getJdbcType(); ——> 这一行代码获取到的jdbcType为null,这时就会进入到if里,把jdbcType赋值为默认值,默认值是JdbcType.OTHER。
- 然后接着执行第87行typeHandler.setParameter(ps, i + 1, value, jdbcType); ——> 这一行就是对sql中的第二个参数进行设置,继续向下调试会发现 ——> 最终会调用PreparedStatement的setNull方法去设置null值,由于java.sql.PreparedStatement是个接口,具体怎么setNull,是由不同的数据库驱动程序去实现的。
我们先来看mysql的setNull方法,如下图。
可以看到,尽管第2个参数为sqlType,但是mysql实现中并没有用到这个参数,所以jdbcType的值对mysql是没有什么用的。
我们再来看oracle是如何实现setNull的
如下图,由于下载不到源码,所以oracle的代码看起来没那么直观,不过对于分析完全够用了。
- 从上图可以看出,oracle的setNull会调用setNullCritical,而setNullCritical中又会用jdbcType这个参数调用getInternalType ——> 这个方法内部是一长串的switch case判断,可以发现对于找不到的jdbcType,这个方法会抛出异常,这个异常就是上面我们做测试时的异常来源。
- 而我们知道,上面mybatis把jdbcType默认设置成了JdbcType.OTHER,这个值oracle是不支持的,所以报错了。
4.3 总结
有了上面的分析,我们就知道mybatis在插入数据时是怎么使用jdbcType的了,由此也可得出结论,对于插入操作,如果某个字段值为null,jdbcType的设置对mysql数据库没什么用,对oracle数据库有用。
资料来源
今天的文章MySQL字段类型和Java实体类型的转换问题分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22067.html