`

Hibernate在高并发的情况下的一个问题

阅读更多

在用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&amp;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性能的几点建议

    优化Hibernate性能的几点建议

    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

    java开发中的struts2+hibernate的综合运用

    HibernateAPI中文版.chm

    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. ...

    hibernate3.2中文文档(chm格式)

    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:Java 对象持久化技术详解(第2版).part2

     21.1 多个事务并发运行时的并发问题  21.1.1 第一类丢失更新  21.1.2 脏读  21.1.3 虚读  21.1.4 不可重复读  21.1.5 第二类丢失更新  21.2 数据库系统的锁的基本原理  21.2.1 锁的多粒度性及自动锁升级  ...

    hibernate 教程

    第一个可持久化类 1.3. 映射cat 1.4. 与猫同乐 1.5. 结语 2. 体系结构 2.1. 总览 2.2. JMX集成 2.3. JCA支持 3. SessionFactory配置 3.1. 可编程配置方式 3.2. 获取SessionFactory...

    精通hibernate

    Hibernate入门 OR映射技术 通过Hibernate API操纵数据库 检索策略和方式 数据库事务、并发、缓存与性能优化 高级配置。

    最全Hibernate 参考文档

    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 ...

    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. ...

    Hibernate 中文 html 帮助文档

    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实战(第2版 中文高清版)

    第一部分 从Hibernate和EJB 3.0开始  第1章 理解对象/关系持久化   1.1 什么是持久化   1.1.1 关系数据库   1.1.2 理解SQL   1.1.3 在Java中使用SQL   1.1.4 面向对象应用程序中的持久化   1.2 范式不...

    Hibernate教程

    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. 映射...

    JAVA高并发高性能高可用高扩展架构视频教程

    高并发复用数据库链接技术详解之数据库连接池 类加载器的高级特性(自定义类加器实现加密解密) iBATIS开源主流框架(实现半自动化hibernate) 企业实用技能之详解(眼睛横纹模式验证码防止恶意登陆) 动态页面的静态化...

    hibernate 体系结构与配置 参考文档(html)

    第一部分 - 第一个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_3.2.0_符合Java习惯的关系数据库持久化

    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实现高并发无锁数据库操作步骤分享

    一个在线2k的游戏,每秒钟并发都吓死人。传统的hibernate直接插库基本上是不可行的。我就一步步推导出一个无锁的数据库操作,详情看下

    Hibernate基础教程

    文档内容包括:Hibernate入门、OR映射技术、通过Hibernate API操纵数据库、检索策略和方式、数据库事务、并发、缓存与性能优化、高级配置

    最新JAVA通用后台管理系统(ExtJS 4.2+Hibernate 4.1.7+Spring MVC 3.2.8)Eclipse版本

    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...

Global site tag (gtag.js) - Google Analytics