多线程中的数据提交问题 [英] Data commit issue in multithreading

查看:141
本文介绍了多线程中的数据提交问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Java和Hibernate的新手。

我已经实现了生成请求编号的功能。基于已经保存的请求no。这是通过找到最大请求号来完成的。并将其递增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);
}

以下是显示我的同步的输出:

 开始
结束
开始
结束

这是否是一些Hibernate问题。
在Spring中使用事务属性是否会影响我的例子中的代码提交?



我使用以下事务属性:

  @Transactional(readOnly = false,propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
$ b

编辑:在聊天室中显示generateRequestNumber()的代码。

  public String generateRequestNumber(String accNumber)throws Exception {
String requestNumber = null;
if(accNumber!= null){
String SQL_QUERY =从CertRequest中选择CERTREQUEST.requestNbr作为CERTREQUEST,
+CertActObjRel作为certActObjRel,其中certActObjRel.certificateObjkeyId = CERTREQUEST.requestId
+和certActObjRel.certObjTypeCd =:certObjTypeCd
+和certActObjRel.certAccountId =:accNumber;

String [] parameterNames = {certObjTypeCd,accNumber};
Object [] parameterVaues = new Object []
{
Constants.REQUEST_RELATION_CODE,accNumber
};
列表<?> resultSet = dao.executeNamedQuery(SQL_QUERY,
parameterNames,parameterVaues);

//列表<?> 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(函数内部请求号码+ requestNumber);
返回requestNumber;
}
返回null;
}


解决方案

不要同步通过getClass()获得的类实例。它可能有一些奇怪的副作用。请参阅 https://www.securecoding.cert.org/confluence /pages/viewpage.action?pageId=43647087



例如使用:

  synchronize(this){
// synchronized code
}

  private synchronized void myMethod(){
//同步代码
}

在对象实例上进行同步。



或者:

  private static final Object lock = new Object(); 

private void myMethod(){
同步(锁定){
//同步代码
}
}

建议使用@diwakar。这使用一个常量字段进行同步以确保此代码在同一个锁上同步。

编辑:根据聊天信息,您使用SELECT来获取最大的requestNumber并增加代码中的值。然后在CertRequest上设置该值,然后通过DAO将其保存在数据库中。如果这个持久行为没有被提交(例如通过使@Transactional或其他方法),那么另一个线程仍然会看到旧的requestNumber值。所以你可以通过将代码交易来解决这个问题(这取决于你使用的框架等)。但是我赞同@ VA31的回答,其中说你应该使用数据库序列来代替增加代码中的值。您可以考虑在CertRequest中使用自动入列字段,而不是序列:

  @GeneratedValue(strategy = GenerationType .AUTO)
private int requestNumber;

要获得序列中的下一个值,您可以查看此问题


I am new to Java and Hibernate.

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.

However I am facing issues with multithreading. When two threads access my code at the same time both generate same request no. My code is already synchronized. Please suggest some solution.

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");
}

Following is the output showing my synchronization:

start
end
start
end

Is it some Hibernate issue. Does the use of transactional attribute in Spring affects the code commit in my Case?

I am using the following Transactional Attribute:

@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

EDIT: code for generateRequestNumber() shown in chat room.

    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;
    }

解决方案

Don't synchronize on the Class instance obtained via getClass(). It can have some strange side effects. See https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=43647087

For example use:

synchronize(this) {
    // synchronized code
}

or

private synchronized void myMethod() {
    // synchronized code
}

To synchronize on the object instance.

Or do:

private static final Object lock = new Object();

private void myMethod() {
    synchronize(lock) {
        // synchronized code
    }
}

Like @diwakar suggested. This uses a constant field to synchronize on to guarantee that this code is synchronizing on the same lock.

EDIT: Based on information from chat, you are using a SELECT to get the maximum requestNumber and increasing the value in your code. Then this value is set on the CertRequest which is then persisted in the database via a DAO. If this persist action is not committed (e.g. by making the method @Transactional or some other means) then another thread will still see the old requestNumber value. So you could solve this by making the code transactional (how depends on which frameworks you use etc.). But I agree with @VA31's answer which states that you should use a database sequence for this instead of incrementing the value in code. Instead of a sequence you could also consider using an auto-incement field in CertRequest, something like:

@GeneratedValue(strategy=GenerationType.AUTO)
private int requestNumber;

For getting the next value from a sequence you can look at this question.

这篇关于多线程中的数据提交问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆