`

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 事务和并发控制

    在Java的持久化框架中,Hibernate是一个非常重要的角色,它提供了强大的对象关系映射(ORM)功能,使得开发者能够方便地在Java应用中操作数据库。本文将深入探讨Hibernate中的事务和并发控制,这对于开发高效、稳定...

    Hibernate事务和并发控制

    理解并正确使用这些机制,可以确保在高并发环境下应用的稳定性和数据的准确性。在实际开发中,开发者需要根据项目需求和环境选择合适的事务管理策略,并合理设计并发控制策略,以应对各种复杂场景。

    优化Hibernate性能的几点建议

    使用`List`查询时,Hibernate会一次性将所有数据加载到内存中,这可能导致内存不足的问题。而使用`Iterator`进行查询时,每次只加载一条数据,这样可以有效避免内存溢出的风险,同时也减少了数据库的压力。 #### 七...

    Hibernate教程26_事务并发处理

    本教程主要聚焦于Hibernate中的事务并发处理,包括悲观锁和乐观锁两种策略,这对于我们理解如何在高并发环境中确保数据的一致性和完整性至关重要。 首先,事务是数据库操作的基本单元,它确保一组操作要么全部成功...

    Hibernate中,利用版本管理机制来控制事务并发

    1. 版本号原理:Hibernate使用乐观锁策略,即假定在正常情况下,同一时间只有一个用户修改数据,当多个用户同时修改时,通过比较版本号判断是否有冲突。每个被管理的对象都有一个版本属性,每次更新对象时,...

    Hibernate4实战 之第五部分:Hibernate的事务和并发

    - **扩展周期的 Session 和自动版本化**:Hibernate 使用扩展周期的 Session 来提供自动版本检查,可以在同步时检测并发修改并抛出异常。这种方式更为高效,减轻了开发者的负担。 #### 七、总结 Hibernate 的事务...

    hibernate的事务核并发

    描述:本文深入解析了Hibernate框架中事务管理和并发控制的核心概念及其实现机制,基于一份详尽的“hibernate详细解析.pdf”文档,覆盖了非托管环境、JTA环境下的事务处理以及如何在不同场景下确保数据的一致性和...

    Hibernate Search In Action

    在集群环境下,Hibernate Search也支持了Lucene搜索引擎的扩展,使得全文搜索可以跨多个节点进行,提升了系统在高并发访问下的稳定性和扩展性。对于那些需要在集群环境下部署全文搜索应用的开发者来说,这是非常重要...

    hibernate3全部jar包:hibernate3.jar.zip 下载

    Hibernate3还支持第二级缓存,这可以显著提高性能,特别是在多用户并发访问相同数据的情况下。通过集成第三方缓存提供商,如Ehcache,可以实现这一功能。此外,Hibernate3提供了强大的关联映射功能,如一对一、一对...

    Hibernate的多对一和一对多操作实例

    这种方式虽然可能减轻了数据库的设计负担,但对于数据完整性和一致性提出了更高的要求,特别是在并发场景下。 对于客户表(CUSTOMER)和订单表(ORDERS),我们看到,客户表中的ID字段被设为主键,而订单表中除了ID...

    Java高并发秒杀API

    Java高并发秒杀API是一个典型的企业级应用场景,主要用于处理大量用户在同一时间抢购限量商品的情况。在设计这样的系统时,我们需要关注多个关键知识点,包括但不限于数据库操作、并发控制、性能优化以及分布式协调...

    hibernate中文参考文档

    13. 原生SQL查询:虽然Hibernate提供强大的HQL和Criteria查询,但在某些特殊情况下可能需要直接使用原生SQL。这部分将介绍如何在Hibernate中执行原生SQL查询。 14. 性能提升:本部分可能包括如何通过合理的配置、...

    达梦 Hibernate 方言 2.0 至 4.0

    "24.jpg"可能是一个示例或说明图,而"DmDialect2.0-4.0.rar"很可能包含了详细的技术文档或者源代码示例,用于指导开发者如何在Hibernate中配置和使用达梦方言,以及如何解决实际开发过程中遇到的问题。 总的来说,...

    基于Spring+SpringMVC+Hibernate+Dubbo分布式开发系统架构,提供高并发

    利用Spring全家桶来构建后端服务,通过SpringBoot简化配置并快速启动应用,Hibernate处理数据库交互,Dubbo实现服务间的通信和治理,MySQL作为数据存储,所有这些组件共同协作,提供了一个能够应对高并发场景的...

    memcache也spring,hibernate的配置

    综上所述,这个项目展示了如何在Java环境下,利用Maven构建工具,将Memcached、Hibernate和Spring集成,实现高效的数据缓存策略,提升应用性能。对于开发人员来说,理解和掌握这些配置能帮助他们更好地优化大型...

    java程序struts2+hibernate

    Struts2是一款基于MVC(Model-View-Controller)设计模式的Web应用框架,而Hibernate则是一个对象关系映射(ORM)工具,用于简化数据库操作。接下来,我们将深入探讨这两个框架的核心概念、工作原理以及它们在实际...

    java socket 多线程并发控制 hibernate mysql

    多线程并发控制是解决高并发问题的关键技术。Java提供了丰富的线程API,如Thread、Runnable接口,以及同步机制如synchronized关键字、Lock接口(如ReentrantLock)等。在设计多线程程序时,需要注意资源的竞争和死锁...

    hibernate一级缓存和二级缓存的区别与联系

    它是一个事务范围的缓存,也就是说,每个 Hibernate Session 对应一个一级缓存,仅在当前事务中有效。一级缓存主要存储了 Session 在当前事务中加载和修改的对象实例。当 Session 执行 CRUD 操作时,对象会自动放入...

    hibernate_flush 深入了解

    例如,在高并发场景下,可以使用批量操作或定时Flush,以减少频繁的数据库交互。 综上所述,理解Hibernate的Flush机制以及它与事务和隔离级别的关系,是优化ORM性能和保证数据一致性的关键。通过合理配置和使用,...

    Hibernate 经典例子全

    乐观锁则是并发控制的一种方法,它假设在大多数情况下不会发生冲突,只有在更新时检查版本号以确保数据未被其他事务修改。 **六、查询缓存和第二级缓存** `hibernate_query_cache`和`hibernate_cache_level_2`揭示...

Global site tag (gtag.js) - Google Analytics