我是Java和Hibernate的新手。
我已经实现了一个生成请求的功能。基于已保存的请求号。这是通过找到最大请求号来完成的。并将其递增1,然后再将其保存到数据库中。
但是我遇到多线程问题。当两个线程同时访问我的代码时,两者都生成相同的请求号。我的代码已经同步了。请提出一些解决方案。
synchronized (this.getClass()) {
System.out.println("start");
certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
System.out.println("end");
}
以下是显示我同步的输出:
start
end
start
end
是否有一些Hibernate问题。 在Spring中使用transactional属性会影响我的Case中的代码提交吗?
我使用以下交易属性:
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
编辑:聊天室中显示的generateRequestNumber()代码。
public String generateRequestNumber(String accNumber) throws Exception {
String requestNumber = null;
if (accNumber != null) {
String SQL_QUERY = "select CERTREQUEST.requestNbr from CertRequest as CERTREQUEST, "
+ "CertActObjRel as certActObjRel where certActObjRel.certificateObjkeyId=CERTREQUEST.requestId "
+ " and certActObjRel.certObjTypeCd=:certObjTypeCd "
+ " and certActObjRel.certAccountId=:accNumber ";
String[] parameterNames = {"certObjTypeCd", "accNumber"};
Object[] parameterVaues = new Object[]
{
Constants.REQUEST_RELATION_CODE, accNumber
};
List<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
parameterNames, parameterVaues);
// List<?> resultSet = dao.retrieveTableData(SQL_QUERY);
if (resultSet != null && resultSet.size() > 0) {
requestNumber = (String) resultSet.get(0);
}
int maxRequestNumber = -1;
if (requestNumber != null && requestNumber.length() > 0) {
maxRequestNumber = maxValue(resultSet.toArray());
requestNumber = Integer.toString(maxRequestNumber + 1);
} else {
requestNumber = Integer.toString(1);
}
System.out.println("inside function request number" + requestNumber);
return requestNumber;
}
return null;
}
答案 0 :(得分:1)
不要在通过getClass()获得的Class实例上进行同步。它可能有一些奇怪的副作用。见https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=43647087
例如使用:
synchronize(this) {
// synchronized code
}
或
private synchronized void myMethod() {
// synchronized code
}
要在对象实例上进行同步。
或者做:
private static final Object lock = new Object();
private void myMethod() {
synchronize(lock) {
// synchronized code
}
}
像@diwakar建议的那样。这使用常量字段进行同步,以保证此代码在同一个锁上同步。
编辑:根据聊天信息,您使用SELECT获取最大requestNumber并增加代码中的值。然后在CertRequest上设置此值,然后通过DAO将其保存在数据库中。如果未提交此持久化操作(例如,通过使用@Transactional方法或其他方法),则另一个线程仍将看到旧的requestNumber值。所以你可以通过使代码进行事务处理来解决这个问题(如何依赖于你使用的框架等)。但我同意@ VA31的回答,该回答指出你应该使用数据库序列而不是增加代码中的值。您也可以考虑在CertRequest中使用自动入口字段,而不是序列,例如:
@GeneratedValue(strategy=GenerationType.AUTO)
private int requestNumber;
要从序列中获取下一个值,您可以查看this question。
答案 1 :(得分:0)
优良作法是生成&#34;请求编号(唯一ID)&#34;使用数据库序列,这样您就不需要同步服务/ DAO方法。
答案 2 :(得分:0)
您在问题中提到了这些信息。
I have implemented a functionality where I generate request nos. based on already saved request no. This is done by finding the maximum request no. and incrementing it by 1,and then again save i it to database.
初看起来,这似乎是多appserver代码引起的问题。线程在一个JVM(appserver)内同步。如果您使用多个appserver,则必须使用更强大的方法,通过使用服务器到服务器通信或通过批量分配请求no来为每个应用程序服务器执行此操作。
但是,如果您只使用一个appserver并且多个线程访问相同的代码,那么您可以锁定类的实例而不是类本身。
synchronized(this) {
lastName = name;
nameCount++;
}
或者您可以使用类实例的私有锁
private Object lock = new Object();
.
.
synchronized(lock) {
System.out.println("start");
certRequest.setRequestNbr(generateRequestNumber(certInsuranceRequestAddRq.getAccountInfo().getAccountNumberId()));
reqId = Utils.getUniqueId();
certRequest.setRequestId(reqId);
ItemIdInfo itemIdInfo = new ItemIdInfo();
itemIdInfo.setInsurerId(certRequest.getRequestId());
certRequest.setItemIdInfo(itemIdInfo);
dao.insert(certRequest);
addAccountRel();
System.out.println("end");
}
但请确保在下一个线程访问新序列之前,通过新序列更新数据库以获取新序列。
答案 3 :(得分:-2)
第一件事:
为什么要在方法中获取线程?我不需要这里。
另外,有一件事;
你能尝试这样一次:
final static Object lock = new Object();
synchronized (lock)
{
.....
}
我觉得你所说的对象是不同的,所以试试这个。