欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Hibernate在高并发的情况下的一个问题 博客分类: hibernate hibernate多线程

程序员文章站 2024-03-24 20:33:52
...

在用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多线程

相关标签: hibernate 多线程