一、现在要实现一个功能:
1、表tabf中主要字段:
   id 表的主键;
   status 当前记录的状态 1 待处理/2 处理中;
   usercode 操作当前记录的用户代码,当status为1时,该字段为空;
2、对tabf中的记录进行查询
   -->如果状态为“待处理”,则当用户点击该条记录时,将用户代码存入tabf.usercode中、并修改tabf.status为2(处理中);
   -->如果状态为“处理中”,则只有tabf.usercode对应的用户有权处理;二、遇到的问题:
如果多用户同时点击某条状态为“待处理”的记录,则数据错误(多用户都可以修改其数据,数据库中记录的信息是最后一个用户的)。三、想实现:
在用户点击“待处理”记录时触发的事务开始时就对数据记录进行上锁,直到该事务提交后放开。四、程序情况:
1、java;
2、*DAO extends JdbcDaoSupport,因为根据系统情况需要自己写UPDATE,而没有用HibernateDaoSupport;
   在网上看到了不少对并发控制的解决方法,其中就有hibernate中的悲观锁和乐观锁,但是现在的情况是用不了。如何解决?

解决方案 »

  1.   

    可以执行update,根据返回的值来确定是否自己得到处理状态。不需要加锁。如果加锁的话,效率太低。
      

  2.   

    加个version字段.......怎么就不能用乐观锁?
      

  3.   

    1、需要加锁是因为:
    如果两个用户在查询结果列表中看到同一条待处理任务,同时点击该任务,则因为还未更来的及更改tabf表中的status和usercode字段,所以两个用户都进入了处理该条任务的界面。最后保存在数据库中的信息是较晚提交的那个用户的信息。所以有错误数据。
    希望能在事务启动时,就将满足查询条件的记录上锁,即行上锁、而非表上锁。如此不会影响效率,因为就是要达到某条任务被某用户处理,其他用户就再无权进行处理的效果。2、不能用悲观锁、乐观锁,是因为现在用的DAO继承的是JdbcDaoSupport,没有如继承HibernateDaoSupport程序处理中那样的实体。而且可以用的话,根据实际情况应该用悲观锁就可以了。
    可能是因为我不熟悉悲观锁、乐观锁的用法,我现在觉得在程序中无法使用。下面是程序片段,希望有人能尽快告诉我如何解决。
    public int updateTwoTable(final Entity entity) {
        ConnectionCallback conCallback = new ConnectionCallback() {
                public Object doInConnection(Connection con) throws SQLException {
                    ... // 省略变量定义等                try {
                        autoCommit = con.getAutoCommit();
                        con.setAutoCommit(false);                    sql0 = "SELECT id, status, usercode FROM tabf WHERE id=?";
                        ps0 = con.prepareStatement(sql0);
                        ps0.setLong(1, entity.getId() == null ? 0 : entity.getId().longValue());
                        rs0 = ps0.executeQuery();
                        while (rs0.next()) {
                            status = rs0.getString(2);
                        }
                        if (status != null && !"1".equals(status.trim())) {
                            return "-2"; // 返回,提示已经有人进行处理,所以无权进行处理
                        }
                        // 就是因为如下update中需要取数据库的当前时间给time赋值,所以才未用继承HibernateDaoSupport的处理方式
                        sql1 = "UPDATE tabf SET status=?, usercode=?, time=SYSDATE WHERE id=?";
                        ps1 = con.prepareStatement(sql1);
                        ps1.setString(1, "2"); // 回访中
                        ps1.setString(2, entity.getUserCode() == null ? "" : entity.getUserCode());
                        ps1.setLong(3, entity.getId() == null ? 0 : entity.getId().longValue());
                        ps1.executeUpdate();                    ... // 省略对另外一个关联表进行update的处理                    con.setAutoCommit(autoCommit);
                        con.commit();
                        return "1";
                    } catch(Exception exception) {
                        try {
                            con.rollback();
                        } catch(SQLException sqlException) {
                            sqlException.printStackTrace();
                        }
                        exception.printStackTrace();
                        return "-1";
                    } finally {
                        ...
                    }
                }
        };
        return Integer.parseInt(this.getJdbcTemplate().execute(conCallback).toString());
    }
      

  4.   

    nanjg,你好!你说在方法前加synchronized,由于我对synchronized不了解,所以不太明白。
    在网上看了关于synchronized的帖子,还是有些不太明白。
    synchronized修饰方法,有两种:
    1、修饰一个非static的method,相当于:
       public void aMethod() {
           synchronized(this) {
               //some code
           }
       }
       是否是指对类的同一个实例进行限制?
    2、修饰一个static的method,相当于:
       public void aMethod() {
           synchronized(this) {
               //some code
           }
       }
       是否是指对类的所有实例进行限制?当前的需求是:
    1、多用户同时点击不同的待处理任务,是可以同步进行处理的;
    2、多用户同时点击相同的待处理任务,只能有一个用户可以进行处理,即对行上锁;
    不知道在方法前加synchronized能否实现上述需求?我找了很多的帖子,有遇到和我相同问题的,但是都没有看到什么解决方法。
    其实我想实现的就是:select * from tabf where id=... for update的效果,就是hibernate中悲观锁需要实现的效果。
    但是我觉得我现在的程序中无法使用悲观锁,是否还有其他的解决方法?
      

  5.   

    不好意思,上述回复中,关于如下片段有错误:
    2、修饰一个static的method,相当于:
       public void aMethod() {
           synchronized(this) {
               //some code
           }
       }
       是否是指对类的所有实例进行限制?应该修改为
    2、修饰一个static的method,相当于:
       public static void aMethod() {
           synchronized(XX.class) {
               //some code
           }
       }
       是否是指对类的所有实例进行限制?
      

  6.   

    我不想用存储过程实现。
    如果是在没有解决方法,我就修改类,用继承HibernateDaoSupport的处理方式,使用悲观锁。
    不过如果修改了类,给time用当前数据库时间赋值的处理,就需要更改为
    1、先查询出SYSDATE,然后赋值给entity的time属性;
    2、再进行其他的处理;
    当初就是不想用这种给time赋值的方法,才用写了上述的程序片段(参见4楼中的程序)。由于今天下班之前就需要更改完毕,是否有好的解决方法?另,我在上述程序中如果写:select...for update,在程序执行时,看控制台中打出的sql,没有了for update,是否hibernate对其进行了处理?
      

  7.   

    问题解决了。
    1、修改了类,改为继承FoHibernateDaoSupport;
    2、修改方法,运用hibernate中的悲观锁,程序片段如下:
    public int updateTwoTable(final Table1_Entity entity1, final Table2_Entity entity2) {
        HibernateCallback hibernateCallback = new HibernateCallback() {
                public Object doInHibernate(Session session) throws HibernateException {
                    List table1_List = null;
                    String returnString = "";                Transaction transaction = session.beginTransaction();
                    try {
                        try {
                            String sql1 = "FROM Table1_Entity a WHERE a.id=" + entity1.getId().longValue();
                            Query query = session.createQuery(sql1);
                            query.setLockMode("[color=#FF0000]a", LockMode.UPGRADE);[/color] // 第一个参数与上面查询语句中的别名对应
                            table1_List = query.list();
                        } catch(Exception exception) {
                            try {
                                transaction.rollback();
                            } catch (HibernateException hibernateException) {
                                hibernateException.printStackTrace();
                            }
                            exception.printStackTrace();
                            return "-1"; // 该任务的状态不是“待处理”,当前用户无权进行处理(锁)
                        }                    if (table1_List == null || table1_List.size() == 0) {
                            returnString = "-2"; // 没有满足条件的任务
                        } else {
                            Table1_Entity entity1_Query = (Table1_Entity)table1_List.get(0);
                            if (entity1_Query.getUserCode() != null || entity1_Query.getStatus() != null && !"1".equals(entity1_Query.getStatus())) {
                                returnString = "-3"; // 该任务的状态不是“待处理”,当前用户无权进行处理
                            } else {
                                entity1.setStatus("2");
                                session.clear();
                                session.update(entity1);
                                if (entity1.getCounter().intValue() == 1) { // 第一次处理任务
                                    entity2.setStatus("2");
                                    session.update(entity2);
                                }
                                returnString = "1";
                            }
                        }
                        transaction.commit();
                    } catch(Exception exception) {
                        try {
                            transaction.rollback();
                        } catch (HibernateException hibernateException) {
                            hibernateException.printStackTrace();
                        }
                        exception.printStackTrace();
                        return "-4";
                    }
                    return returnString;
                }
        };
        return Integer.parseInt((String)this.getHibernateTemplate().execute(hibernateCallback));
    }
    3、关于上面提到的给Time赋值的问题也解决了,用了hibernate3自己定义insert、update、delete的方法,如下:
    (1)*.hbm.xml中如下:
    <?xml version="1.0" encoding='UTF-8'?>
    <!DOCTYPE hibernate-mapping PUBLIC
                                "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                                "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
    ...    <class name="Table1_Entity" table="TABF">
            <id name="id" column="P_ID" type="java.lang.Long">
                <generator class="sequence">
                  <param name="sequence">SEQ_TABF_ID</param>
                </generator>
            </id>
            <property name="userCode" type="java.lang.String" column="P_USER_CODE" not-null="true" />
            <property name="status" type="java.lang.String" column="P_STATUS" not-null="true" />
            [b]<property name="time" type="java.util.Date" column="P_TIME" update="false" />[/b]
            <property name="counter" type="java.lang.Long" column="P_COUNTER" update="false" />
            <property name="..." type="java.lang.String" column="P_..." update="false" />
            <property name="..." type="java.lang.String" column="P_..." update="false" />
            <property name="..." type="java.lang.String" column="P_..." update="false" />
            ...
            <sql-update>UPDATE TABF SET P_USER_CODE=?, P_STATUS=?, time=SYSDATE WHERE P_ID=?</sql-update>
        </class>
    需要注意:
    --><sql-update>中需要写数据库中的表名和字段名,而不是实体名和属性名;
    --><sql-update>中需要更改的字段顺序需要和上面property定义的顺序相同;
    -->如果字段不需要出现在<sql-update>中,则需要在上面property定义是加【update="false】。如上例中<sql-update>中出现time=SYSDATE,则在定义<property name="time" type="java.util.Date" column="P_TIME"... />时需要加入【update="false"】如上是我这一天的成果,期间上网看了好多的帖子,但是总是有地方没有明确说明,所以我不断的调试,最后终于成功了。第一次发表帖子,比较的高兴,希望此帖子对别人能有帮助。
      

  8.   

    楼上的程序中有点错误,因为发表帖子时想突出信息,但是不知道为什么显示出来html代码了。
    1、query.setLockMode("a", LockMode.UPGRADE); // 第一个参数与上面查询语句中的别名对应
    应该是:
    query.setLockMode("a", LockMode.UPGRADE); // 第一个参数与上面查询语句中的别名对应
    2、 <property name="time" type="java.util.Date" column="P_TIME" update="false" />
    应该是:
    <property name="time" type="java.util.Date" column="P_TIME" update="false" />