使用hibernate与mysql时数据不能插入的原因及解决办法
1.背景
之前从没用过hibernate,因此在网上搜了一下hibernate快速入门方面的信息,最后我按照《Myeclipse Hibernate 快速入门 中文版》(CSDN,百度文库都有)一文开始了我的hibernate之旅(为项目做技术准备)。
下面是在学习使用时用到的三个开发工具(Myeclipse , Mysql , SQL-Front)及其版本说明:
Myeclipse版本为6.5(没用最新的Myeclipse8.5,这是由于之前组里的项目都是在Myeclipse6.5中开发的,并且本项目是与其它人合作完成的,为了防止可能由于开发环境的不一致而引起的问题,我们统一使用Myeclipse6.5,所以我就在Myeclipse6.5环境下学习使用hibernate了)。
Mysql使用的是5.1版本。Mysql安装完后,需要手动进行配置,其中有一项是“please select the database usage”,我在这里选择的是“Muitifunctional Database”(如下图所示)。这里其实选则的是使用何种类型的数据库(InnoDB还是MyISAM),如果选第三个,就不能用InnoDB类型的数据库了,这个在每一个选项的说明中可以看到。
为了方便mysql的使用,又安装了图形化界面的SQL-Front,版本是5.1。
2.遇到的问题
我用SQL-Front在数据库中建了一简单的表用于学习,等同的SQL语句如下:
“author”表有两个字段,一个是主键“Id”,一个是“name”。需要说明的是SQL-Front在建表的时候默认数据库类型为InnoDB。
针对“author”表,按照《Myeclipse Hibernate 快速入门 中文版》一文的说明操作完成后,写了一段简单的测试代码:
即插如一条name字段为“author”的数据,但是发现执行完后数据根本就没有插入到数据库中。后来在网上搜了一下,有人给出了解决办法,即使用事务来解决,修改后的代码如下:
数据插入的问题是解决了,但是发现插入数据后,Id已经在插入前自动增加了(不是从1开始了),也就是说之前的测试虽然数据看似没有插入数据库,但是实际效果却跟插入了数据库一样(要不然Id不会自动增加),这又是为什么?在网上搜了一下,结合自己的理解,我分析了一下原因。
3.原因
上述问题的原因的本质我认为在于使用的数据库类型。
我使用的数据库类型是“InnoDB”,这是一个支持事务的数据库类型,这种数据库你无论什么操作,最后如果你不“commit”的话,等于啥也没干(这其中的道理在网上搜一下对数据库中事务的简单介绍应该不难理解)。虽然通过save方法可以将sql语句发送到数据库让其执行(备注:并不是所有的save方法都会将sql语句发送到数据库,当主键生成策略为native的时候会发送到数据库,比如“author”的主键Id,其生成策略就为native,不过可以通过dao.getSession().flush()强行将sql语句发送到数据库。这些我也只是知道一点皮毛,有时间还需要深入了解),数据库也确实执行了(分配了Id,并且将Id自动加1),但是这个执行的结果只是临时的,如果不“commit”的话,随着会话session的结束(即上述代码中dao.getSession().close()语句),这个临时的执行结果也就没了,直接的体现就是数据没有最终插入数据库。
通过SQL-Front可以很好的观察到这一过程(针对未引入事务的那段代码),首先在dao.getSession().Close()处设置断点,然后用SQL-Front打开这个表,并设置SQL-Front与mysql的会话隔离级别为(Read Uncommitted,默认的级别为Repeatable Read,这个网上也有很多介绍的,也不多说了),然后执行测试代码,执行到断点处后,也即刚刚执行完dao.save(author)而还没有关闭本次会话,通过SQL-Front会发现,这个数据插入到了表中(当然是“临时”插入数据,如果会话隔离级别不是Read Uncommitted的话是看不到这条插入的数据的),继续执行dao.getSession().close()后,通过SQL-Front刷新“author”表中的数据,会发现刚刚插入的那条数据又消失了。
不过这其中有一个比较特殊的地方,那就是Id这个自段了。这个字段是“author”表的主键,并且它的值是数据库自己产生的,在插入数据的时候不需要指定这个字段的值。这个字段的特殊之处在于你在插入数据的时候,无论最后是否“commit”,这个字段的值都会自动加1供下一次插入数据时使用,不会说由于本次会话没有“commit”,而在会话结束时自动减1恢复到原来的值。
这一点我觉得应该是出于并发的考虑(没有查阅相关资料,只是提出我自己的猜测)。假设有三个会话A和B,都要向“author”表中插入数据(假设表中没有数据,Id从1开始),A首先调用了save方法,数据库为A插入的数据分配的Id是1,然后“author”表将Id自动加1并保存,在A未“commit”之前,B也调用了save方法,自然数据库为B插入的数据分配的Id是2(不会是1,如果是1,A和B将要提交的数据主键Id值就冲突了,会造成A和B谁后“commit”数据谁失败,数据库就无法并发了),“author”表将Id字段再次加1后保存(此时为下一组数据使用的Id值为3),假设B首先“commit”了数据,即“author”表中有了Id字段值为2的数据,而A最终没有“commit”数据,如果此时“author”表的字段自动减1,可以看到,下一次数据插入分配的Id字段值就会是2,和现有的数据发成了主键冲突。因此,对于“author”表中Id这个字段,无论某个插入操作是否最终“commit”,只要调用了save方法,Id字段就会自加1。这也能解释之前遇到的那个问题,即数据没有插入,Id字段却自动增加了。
4.解决方法
总共有两种解决方法:
第一种: 可以考虑使用“MyISAM”类型的数据库,这种类型的数据库不支持事务,因此在调用save方法的时候(注意,如果主键生成策略不是native的,必须在save后调用dao.getSession().flush()方法,即强行将sql语句发送到数据库,否则一样没有插入数据),数据就已经最终插入到数据库里了(注意,这是最终结果,不是临时结果,这和使用“InnoDB”类型数据库时“commit”的效果是一样的)。当然了,直接使用事务机制(就像上面那段修改后的代码一样)也是可以的(“MyISAM”类型的数据库虽然不支持事务,但是并不代表不能用hibernate里的事务机制,这两个概念还不太一样。当主键生成策略不是native的时候,使用事务机制还省的调用dao.getSession().flush()方法了)。mysql支持“MyISAM”类型的数据库,可以在SQL-Front中直接将“InnoDB”类型的数据库转为“MyISAM”类型的数据库(建议这么做的时候小心,因为看到网上有人说这么做可能会产生问题,不过我转换的时候倒是没碰到什么问题,也可能是我的“author”表简单的缘故),也可在建表的时候直接指定类型为“MyISAM”。关于“MyISAM”类型的数据库的更多信息(比如相对于“InnoDB”类型数据库有什么优缺点)网上也有很多介绍,就不在这里罗嗦了。
第二种:还是使用“InnoDB”类型的数据库。这时可以通过两种途径解决:一是像上面那个修改后的代码一样,加入事务机制,这是最保险的(也推荐使用);第二个途径就是在hibernate的配置文件中,加入自动提交的属性,如下图所示:
这个属性的作用是,一旦调用了save方法(和第一种解决方法一样,如果主键生成策略不是native的,必须在save后调用dao.getSession().flush()方法),hibernate会自动帮你“commit”,在代码里不需要自己写关于事务的那些代码(例如commit调用)。这么做的缺点网上也有很多说明,也不在这里多说了。
5.总结
由于我水平实在有限(大菜鸟一个啦),表达能力也不是很好,所以说了这么多也可能还没说明白,没说透彻。不过我觉的对大家有用的一个结论就是,无论什么操作,都放在事务里提交,这样是最省事,也是最保险的。
整整浪费了我一天时间啊 我操
使用hibernate与mysql时数据不能插入的原因及解决办法的更多相关文章
- installshield制作的安装包卸载时提示重启动的原因以及解决办法
原文:installshield制作的安装包卸载时提示重启动的原因以及解决办法 有时候卸载installshield制作的安装包程序,卸载完会提示是否重启电脑以完成所有卸载,产生这个提示的常见原因有如 ...
- mysql delete数据 空间占用不减少的解决办法
今天空间商告诉我数据库空间满了,检查了一下,发现网站用户行为记录数据表竟然占了20多MB.积累了半年了,该删除释放一下空间了.果断delete之后发现数据库空间竟然没少,虽然数据记录数是零. 原来这是 ...
- python对MySQL进行数据的插入、更新和删除之后需要commit,数据库才会真的有数据操作。(待日后更新)
今天在尝试用下面的python代码对MySQL进行数据的插入.更新和删除时, 突然发现代码执行成功, 通过代码查询也显示数据已经插入或更新, 但是当我在MySQL客户端通过SQL语句查询时, 数据库中 ...
- chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法[bubuko.com]
chart.js插件生成折线图时数据普遍较大时Y轴数据不从0开始的解决办法,原文:http://bubuko.com/infodetail-328671.html 默认情况下如下图 Y轴并不是从0开始 ...
- 向SDE图层中添加大量数据时,出现ORA-00604以及ORA-01000的解决办法
转自原文 向SDE图层中添加大量数据时,出现ORA-00604以及ORA-01000的解决办法 写了一个小程序,从一个列表中读取坐标串,每个坐标串生成一个IPolygon,然后将这些Polygon添加 ...
- WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法
WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法 在WPF的TextBox的LostFocus事件中直接使用Focus()方法会出现死循环的问题 正确的使用方式有2中方法: 方法一 ...
- mysql数据库死锁的产生原因及解决办法
这篇文章主要介绍了mysql数据库锁的产生原因及解决办法,需要的朋友可以参考下 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同 ...
- mysql保存中文乱码的原因和解决办法
当你遇到这个mysql保存中文乱码问题的时候,期待找到mysql保存中文乱码的原因和解决办法这样一篇能解决问题的文章是多么激动人心. 也许30%的程序员会选择自己百度,结果发现网友已经贴了很多类 ...
- 卸载MySQL以及重装卡到Start Services的解决办法(亲测有效,刚重装成功)
卸载MySQL以及重装卡到Start Services的解决办法 重装系统永远是个好办法,但是对于我们程序员来说只要一想到电脑上的环境变量和其他的配置就蔫了.所以这一条就当作是废话吧. 一般来说装My ...
随机推荐
- Oracle安装完成后如何创建表空间及用户
1.select file_Name from dba_data_files;(查询表空间) 2.create tablespace QUAN datafile '/app/ADMINISTRATOR ...
- 00001 - Linux下 环境变量/etc/profile、/etc/bashrc、~/.bashrc的区别
①/etc/profile: 该文件登录操作系统时,为每个用户设置环境信息,当用户第一次登录时,该文件被执行.也就是说这个文件对每个shell都有效,用于获取系统的环境信息. # /etc/profi ...
- 第9课 基于范围的for循环
1. 基于范围的for循环(range-based for) (1)语法:for(decl : coll){//statement} ①decl用于声明元素及类型,如int elem或auto ele ...
- 用微信小程序连接WordPress网站
随着微信小程序的功能越来越强,特别对个人开发者的开放,让个人开发者有机会尝试微信小程序.如果你有自己的个人网站,就可以把个人网站搬到微信小程序里,通过小程序直接访问网站的内容. 要想微信小程序可以获取 ...
- Error running app: Default Activity not found ; 安卓程序运行不了,也不报错。
我最近copy一个工程,写完了去运行时不能运行,项目不报错,就是运行的地方有个叉号:尝试很多办法后准备重新New一个时发现:"10:17 Error running app: Default ...
- 常用正则表达式—邮箱(Email)
本文针对有一点正则基础的同学,如果你对正则一无所知,请移步“正则表达式30分钟入门教程”学习. 要验证一个字符串是否为邮箱的话,首先要了解邮箱账号的格式.我尝试过在网上找出一个标准的格式,但是很遗憾 ...
- JavaEE进阶——全文检索之Solr7.4服务器
I. Solr Solr简介 Solr是Apache的顶级开源项目,使用java开发 ,基于Lucene的全文检索服务器. Solr比Lucene提供了更多的查询语句,而且它可扩展.可配置,同时它对L ...
- python中的sockeserver模块简单实用
1. socketserver模块简介 在python的socket编程中,实用socket模块的时候,是不能实现多个连接的,当然如果加入其它的模块是可以的,例如select模块,在这里见到的介绍下s ...
- TableStore:多行数据操作
1.批量写 public static void batchWriteRow(SyncClient client) { BatchWriteRowRequest request = new Batch ...
- Angularjs给动态生成的元素绑定事件
//获取动态生成的元素 getJqforAnguar:function(jqid){ angular.element(document).injector().invoke(function($com ...