一、现在要实现一个功能:
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、表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中的悲观锁和乐观锁,但是现在的情况是用不了。如何解决?
解决方案 »
- 点击提交表单后 显示404,请教高手指点!我找不出来错误了
- 请问下做过resin服务器的日志备份问
- 关于Hibernate配置问题
- jsp图片插入
- 问个关于日期的问题
- 本人新手,第一次接触hibernate+SQL server 2005。。现在出了些问题连接不上数据库,请高手帮帮忙~
- 被包含页面的中文显示不出来?应该怎么办?
- Struts2 在JSP页面中如何获取ActionContext.parameters中的值
- 急,在线等!!!一个很简单的structs问题,小弟初学
- 数据链接有误不知道怎么改的拉。
- 及时通信效果
- 大家帮忙看个问题啊。。java.net.UnknownHostException: java.sun.com
如果两个用户在查询结果列表中看到同一条待处理任务,同时点击该任务,则因为还未更来的及更改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());
}
在网上看了关于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中悲观锁需要实现的效果。
但是我觉得我现在的程序中无法使用悲观锁,是否还有其他的解决方法?
2、修饰一个static的method,相当于:
public void aMethod() {
synchronized(this) {
//some code
}
}
是否是指对类的所有实例进行限制?应该修改为
2、修饰一个static的method,相当于:
public static void aMethod() {
synchronized(XX.class) {
//some code
}
}
是否是指对类的所有实例进行限制?
如果是在没有解决方法,我就修改类,用继承HibernateDaoSupport的处理方式,使用悲观锁。
不过如果修改了类,给time用当前数据库时间赋值的处理,就需要更改为
1、先查询出SYSDATE,然后赋值给entity的time属性;
2、再进行其他的处理;
当初就是不想用这种给time赋值的方法,才用写了上述的程序片段(参见4楼中的程序)。由于今天下班之前就需要更改完毕,是否有好的解决方法?另,我在上述程序中如果写:select...for update,在程序执行时,看控制台中打出的sql,没有了for update,是否hibernate对其进行了处理?
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"】如上是我这一天的成果,期间上网看了好多的帖子,但是总是有地方没有明确说明,所以我不断的调试,最后终于成功了。第一次发表帖子,比较的高兴,希望此帖子对别人能有帮助。
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" />