在用hibernate开发的过程中,无意间碰到如下的一个问题。
我的测试代码如下:
1.vo类:
package com.huajtech.vo; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Table(name = "tbl_comment") @Entity public class Comment { @Id @GeneratedValue private Long id; @Column(name = "name") private String name; @ManyToOne(fetch = FetchType.EAGER, targetEntity = Topic.class) @JoinColumn(name = "topic") private Topic topic; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Topic getTopic() { return topic; } public void setTopic(Topic topic) { this.topic = topic; } }
package com.huajtech.vo; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; @Table(name = "tbl_topic") @Entity public class Topic { @Id @GeneratedValue private Long id; @Column(name = "name") private String name; @OneToMany(mappedBy = "topic", fetch = FetchType.LAZY, targetEntity = Comment.class) private final Set<Comment> comments = new HashSet<Comment>(); @Column(name = "comment_count") private Long commentCount = 0L; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getCommentCount() { return commentCount; } public void setCommentCount(Long commentCount) { this.commentCount = commentCount; } public Set<Comment> getComments() { return comments; } }
2.Dao类
package com.huajtech.dao; import org.hibernate.Session; public interface GenericDao { <T> T getObject(Class<T> cls, Long id); Long saveObject(Object entity); void updateObject(Object entity); void saveOrUpdateObject(Object entity); <T> void deleteObject(Class<T> cls, Long id); int execSqlUpdate(String sqlStr, Object[] params); Session getCurrentSession(); }
package com.huajtech.dao; import org.hibernate.HibernateException; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate4.HibernateCallback; import org.springframework.orm.hibernate4.HibernateTemplate; import org.springframework.stereotype.Repository; @Repository(value = "genericDao") public class GenericDaoImpl implements GenericDao { @Autowired private HibernateTemplate hibernateTemplate; @Override public Long saveObject(Object entity) { Long entityId = (Long) hibernateTemplate.save(entity); hibernateTemplate.flush(); return entityId; } @Override public void updateObject(Object entity) { hibernateTemplate.update(entity); hibernateTemplate.flush(); } @Override public <T> void deleteObject(Class<T> cls, Long id) { T obj = hibernateTemplate.get(cls, id); if (null != obj) { hibernateTemplate.delete(obj); } hibernateTemplate.flush(); } @Override public void saveOrUpdateObject(Object entity) { hibernateTemplate.saveOrUpdate(entity); hibernateTemplate.flush(); } @Override public <T> T getObject(Class<T> cls, Long id) { return hibernateTemplate.get(cls, id); } @Override public int execSqlUpdate(final String sqlStr, final Object[] params) { return hibernateTemplate.execute(new HibernateCallback<Integer>() { @Override public Integer doInHibernate(Session session) throws HibernateException { SQLQuery query = session.createSQLQuery(sqlStr); if (params != null && params.length > 0) { for (int i = 0; i < params.length; i++) { query.setParameter(i, params[i]); } } Integer resultCount = query.executeUpdate(); hibernateTemplate.flush(); return resultCount; } }); } @Override public Session getCurrentSession() { return hibernateTemplate.getSessionFactory().getCurrentSession(); } }
3. service类
package com.huajtech.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.huajtech.dao.GenericDao; import com.huajtech.vo.Comment; import com.huajtech.vo.Topic; @Service public class TopicServiceImpl { @Autowired private GenericDao genericDao; @Transactional(propagation = Propagation.REQUIRED) public synchronized void saveComment(Long topicId, String commentName) { System.out.println(Thread.currentThread() + "=======================" + genericDao.getCurrentSession().hashCode()); Topic topic = genericDao.getObject(Topic.class, topicId); Comment comment = new Comment(); comment.setName(commentName); comment.setTopic(topic); genericDao.saveObject(comment); System.out.println(topic.getCommentCount() + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); topic.setCommentCount(topic.getCommentCount() + 1L); // 如果改成如下的方式就可以正确的计数... // genericDao.execSqlUpdate("update tbl_topic set comment_count=comment_count+1 where id=?", // new Object[] {topicId}); genericDao.saveObject(topic); System.out.println(topic.getCommentCount() + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); System.out.println(Thread.currentThread() + "----------------------" + genericDao.getCurrentSession().hashCode()); } @Transactional(propagation = Propagation.REQUIRED) public void saveTopic() { Topic topic = new Topic(); topic.setName("话题1"); genericDao.saveObject(topic); } @Transactional(propagation = Propagation.REQUIRED) public void deleteRecord() { genericDao.execSqlUpdate("delete from tbl_comment", null); // genericDao.execSqlUpdate("delete from tbl_topic", null); Topic topic = genericDao.getObject(Topic.class, 1L); topic.setCommentCount(0L); } }
4.测试类:
package com.huajtech.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import com.huajtech.service.TopicServiceImpl; public class TransactionTest { public static void main(String[] args) throws InterruptedException { ApplicationContext ac = new FileSystemXmlApplicationContext("D:\\dev\\workspaces\\ssh\\src\\spring-hibernate.xml"); TopicServiceImpl service = (TopicServiceImpl) ac.getBean("topicServiceImpl"); // service.deleteRecord(); service.saveTopic(); Runnable run = new SaveThread(service); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); } private static class SaveThread implements Runnable { private final TopicServiceImpl service; public SaveThread(TopicServiceImpl service) { super(); this.service = service; } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } // service.saveComment(1L, "线程" + Thread.currentThread().getId() + "的comment" + i); service.saveComment(1L, "" + Thread.currentThread().getId() + "comment" + i); } } } }
5.spring的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd"> <!-- 启动自动扫描该包下所有的Bean 注意这块,也非常重要 --> <context:component-scan base-package="com.huajtech" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <context:include-filter type="annotation" expression="org.springframework.beans.factory.annotation.Autowired"/> <context:include-filter type="annotation" expression="org.springframework.beans.factory.annotation.Qualifier"/> </context:component-scan> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8" /> <property name="username" value="root" /> <property name="password" value="" /> </bean> <!-- 配置hibernate SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> </props> </property> <property name="packagesToScan" value="com.huajtech.vo." /> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <!-- 事务管理器 --> <bean id="transactionManager" name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> </beans>
测试结果如下:
Thread[Thread-3,5,main]=======================731806090 Hibernate: select topic0_.id as id1_0_, topic0_.comment_count as comment2_1_0_, topic0_.name as name1_0_ from tbl_topic topic0_ where topic0_.id=? Hibernate: insert into tbl_comment (name, topic) values (?, ?) 0>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hibernate: update tbl_topic set comment_count=?, name=? where id=? 1<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Thread[Thread-3,5,main]----------------------731806090 Thread[Thread-5,5,main]=======================1134383678 Hibernate: select topic0_.id as id1_0_, topic0_.comment_count as comment2_1_0_, topic0_.name as name1_0_ from tbl_topic topic0_ where topic0_.id=? Hibernate: insert into tbl_comment (name, topic) values (?, ?) 0>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hibernate: update tbl_topic set comment_count=?, name=? where id=? 1<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Thread[Thread-5,5,main]----------------------1134383678 Thread[Thread-6,5,main]=======================1317455901 Hibernate: select topic0_.id as id1_0_, topic0_.comment_count as comment2_1_0_, topic0_.name as name1_0_ from tbl_topic topic0_ where topic0_.id=? Hibernate: insert into tbl_comment (name, topic) values (?, ?) 1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Hibernate: update tbl_topic set comment_count=?, name=? where id=? 2<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Thread[Thread-6,5,main]----------------------1317455901
要求:自己要实现的一个功能,如果给给comment插入一条记录的话,那么就更新这条comment对应的topic的记录保存的comment的总数。(为测试代码,我只更新topic记录为1的记录)
结果:总数总是统计的不正确。
自己的分析过程:
1.第一次写没有加同步,所以mysql数据库报死锁,这个是肯定的,然后加了同步之后,不发生死锁了。
2.加了同步之后 ,不会出现死锁,但仍然记录不正确,日志中前两个线程打印出来的日志就可以看到,第一个线程已经把计数更新为1了(数据库真的更新了么?我就不知道了),然后第一个线程查询的话,还是之前的0。
3.我怀疑就是mysql事务的问题,此时我把service中的方法注释的放开(让hibernate直接执行原始的mysql)这下就好了。
4.如果是mysql事务的问题的话,那用原生态的sql更新记录的话,仍然会出现不一致的问题。
5.会不会同一个session,这样hibernate取的就是内存中的数据了,但是通过sql的语句就知道每次都是去数据差,并且打印的session都不一样。
6.myql的缓存池?也不对啊,用hibernate的save方法,也看到了它执行的sql,跟原生态的区别不大。
7.分析到这里就真不会了。
PS:本来想上传工程的,发现依赖的包比较多,我想大家估计也不会下载,所以就没上传。
求各位大神如果知道原因的话,麻烦告诉小弟,小弟将感激涕零啊。
相关推荐
优化Hibernate性能的几点建议
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
java开发中的struts2+hibernate的综合运用
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
21.1 多个事务并发运行时的并发问题 21.1.1 第一类丢失更新 21.1.2 脏读 21.1.3 虚读 21.1.4 不可重复读 21.1.5 第二类丢失更新 21.2 数据库系统的锁的基本原理 21.2.1 锁的多粒度性及自动锁升级 ...
第一个可持久化类 1.3. 映射cat 1.4. 与猫同乐 1.5. 结语 2. 体系结构 2.1. 总览 2.2. JMX集成 2.3. JCA支持 3. SessionFactory配置 3.1. 可编程配置方式 3.2. 获取SessionFactory...
Hibernate入门 OR映射技术 通过Hibernate API操纵数据库 检索策略和方式 数据库事务、并发、缓存与性能优化 高级配置。
1.2. 第一个持久化类 1.3. 映射cat 1.4. 与Cat同乐 1.5. 结语 2. 架构(Architecture) 2.1. 概况(Overview) 2.2. 实例状态 2.3. JMX整合 2.4. 对JCA的支持 3. 配置 3.1. 可编程的配置方式 3.2. 获得SessionFactory ...
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
第一部分 从Hibernate和EJB 3.0开始 第1章 理解对象/关系持久化 1.1 什么是持久化 1.1.1 关系数据库 1.1.2 理解SQL 1.1.3 在Java中使用SQL 1.1.4 面向对象应用程序中的持久化 1.2 范式不...
2.2. 第一部分 - 第一个Hibernate程序 2.2.1. 第一个class 2.2.2. 映射文件 2.2.3. Hibernate配置 2.2.4. 用Ant编译 2.2.5. 安装和帮助 2.2.6. 加载并存储对象 2.3. 第二部分 - 关联映射 2.3.1. 映射...
高并发复用数据库链接技术详解之数据库连接池 类加载器的高级特性(自定义类加器实现加密解密) iBATIS开源主流框架(实现半自动化hibernate) 企业实用技能之详解(眼睛横纹模式验证码防止恶意登陆) 动态页面的静态化...
第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. 映射...
1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...
一个在线2k的游戏,每秒钟并发都吓死人。传统的hibernate直接插库基本上是不可行的。我就一步步推导出一个无锁的数据库操作,详情看下
文档内容包括:Hibernate入门、OR映射技术、通过Hibernate API操纵数据库、检索策略和方式、数据库事务、并发、缓存与性能优化、高级配置
Spring MVC 3.2.8支持的最高Hibernate版本是4.1.7,更高的Hibernate版本和Spring MVC 3.2.8组合会遇到兼容问题。 4、Hibernate集成二级缓存框架Ehcache。 5、数据库是MySQL、Oracle和SQL Server,Hibernate的Dialect...