交易中弹簧性能优化

时间:2014-07-02 18:45:51

标签: java spring-jdbc

我正在制作一个企业应用程序。其中我有任务解析一个包含假设50,000 k记录的csv文件。最终用户在注册时提供的此csv文件。我创建了一个程序来将csv文件解析为java对象,然后将所有这些对象保存到数据库中。此文件包含手机号码,在将csv文件保存为java对象之前,它首先验证了手机号码。它是否存在于数据库中。它存在然后它未通过验证并停止执行。 现在假设两个不同的用户A和B发送注册请求。控制器通过以下代码监听此请求。 控制器层

@Transactional
    @RequestMapping(value = "/saveCsvData")
    public ModelAndView saveVMNDataFromCsv(@ModelAttribute("vmn") @Valid VMN vmn,   BindingResult result, HttpSession session, HttpServletRequest request) {

    String response = parseCsvService.parseAndPersistCsv(vmn.getVmnFile(),vmn.getNumberType(), request);
}

在控制器方法上我有用户@Transactional注释,这样这个方法就可以完全完成它的工作。这个控制器调用helper调用从csv文件中逐行读取并将它们放入java对象。获取VMN列表后在循环的帮助下对象我调用服务方法,再次调用eoh行的dao方法。 助手班

public String parseAndPersistCsv(MultipartFile csvFile,String numberType, HttpServletRequest request){
            List<VMN> vmnList = new ArrayList<VMN>();
                if(save){
                    for(VMN vmn : vmnList){
                    System.out.println("Remote Host :" + request.getRemoteHost());
                    System.out.println("Remote Add :" + request.getRemoteAddr());

                    vmnService.saveVmn(vmn, numberType); 
                    }
                    response = constantService.getSuccess();
                }
}

服务层

public String saveVmn(final VMN vmn, String numberType) {
    vmnService.saveVmn(vmn, numberType); 
}

在Dao Layer方法看起来像这样。此方法将记录插入到多个表中,因为它可以在方法代码中看到。

Dao Layer

public String saveVmn(final VMN vmn, String numberType) {
String result = "error";
try {
final StringBuffer sql = new StringBuffer();
sql.append(constantService.getInsertInto());
sql.append(VMNTableSingleton.getInstance().getTableName());
sql.append(" (");
sql.append(VMNTableSingleton.getInstance().getVmnNo());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getNumberType());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getOperator());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getCircle());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getBuyingPrice());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getRecurringPrice());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getCreationDate());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getUpdationDate());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getActive());
sql.append(constantService.getComma());
sql.append(VMNTableSingleton.getInstance().getStatus());
sql.append(")");
sql.append(constantService.getValues());
sql.append(" (?,?,?,?,?,?,?,?,?,?)");
logger.info("Saving Vmn..." + sql);
KeyHolder keyHolder = new GeneratedKeyHolder();
int response = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
ps.setObject(1, vmn.getVmnNo());
ps.setObject(2, vmn.getNumberType());
ps.setObject(3, vmn.getOperator());
ps.setObject(4, vmn.getCircle());
ps.setObject(5, vmn.getBuyingPrice());
ps.setObject(6, vmn.getBuyingPrice());
ps.setObject(7, new Date());
ps.setObject(8, new Date());
ps.setObject(9, true);
ps.setObject(10, vmn.getStatus());
return ps;
}
}, keyHolder);
logger.info("Saved Successfully");
if (response == 1) {
if(vmn.getMappedVmn() != null){
Long vmnId = keyHolder.getKey().longValue();
if(vmnId > 0){
StringBuffer mappedsql = new StringBuffer();
mappedsql.append(constantService.getInsertInto());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getTableName());
mappedsql.append(" (");
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getDidId());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getMappedId());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getType());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getCreationDate());
mappedsql.append(constantService.getComma());
mappedsql.append(MapDIDVMNTableSingleton.getInstance().getModifiedDate());
mappedsql.append(")");
mappedsql.append(constantService.getValues());
mappedsql.append(" (?,?,?,?,?)");
logger.info("Mapping... DID with VMN");
int mappedresponse = jdbcTemplate.update(mappedsql.toString(),
new Object[] {vmn.getMappedVmn().getVmnId(),vmnId ,vmn.getNumberType(),new Date(),new Date()});
logger.info("Mapped Successfully");
if(mappedresponse == 1){
stringBuffer updatesql = new StringBuffer();
updatesql.append(constantService.getUpdate());
updatesql.append(VMNTableSingleton.getInstance().getTableName());
updatesql.append(constantService.getSet());
updatesql.append(VMNTableSingleton.getInstance().getStatus());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
updatesql.append(constantService.getComma());
updatesql.append(VMNTableSingleton.getInstance().getAllocationDateTime());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
updatesql.append(constantService.getWhere());
updatesql.append(VMNTableSingleton.getInstance().getVmnId());
updatesql.append(constantService.getEqual());
updatesql.append(constantService.getQuestionMark());
logger.info("Updating Vmn..." + updatesql);
jdbcTemplate.update(updatesql.toString(),
new Object[] { constantService.getMapped(),new Date(), vmn.getMappedVmn().getVmnId()});
logger.info("Saved Successfully");
}
}
}
result = "success";
} else {
result = "error";
}
} catch (Exception ex) {
}
return result;
}

现在我已经向控制人员发送了注册请求。然后我在控制台上看到了两个线程访问方法一个接一个。通过这个代码在for循环中。

System.out.println(&#34;远程主机:&#34; + request.getRemoteHost()); //10.0.0.0114 System.out.println(&#34; Remote Add:&#34; + request.getRemoteAddr()); // 110.0.0.115

这不是脏读问题,因为如果一个线程正在读取数据,那么其他线程可能正在插入。所以为了解决这个问题,我使用了同步块。喜欢这个

synchronized (this) {
                if(save){
                    for(VMN vmn : vmnList){
                    System.out.println("Remote Host :" + request.getRemoteHost());
                    System.out.println("Remote Add :" + request.getRemoteAddr());

                    vmnService.saveVmn(vmn, numberType); 
                    }
                    response = constantService.getSuccess();
                }               
            }

现在我的问题是,这是正确的方法吗,或者也可以通过其他方式完成。

1 个答案:

答案 0 :(得分:0)

  

这可能是脏读问题,因为如果一个线程正在读取数据   然后其他人可能会插入

如果您的关注是可见性,那么我认为可以通过配置的事务隔离(通常默认为READ COMMITTED)来处理,其中线程只能看到提交的数据及其尝试更新的数据。

此外,您应该考虑使用JDBC batchUpdate JDBCTemplate方法,该方法使用jdbc批处理功能。批量读取并检查数字是否存在,然后分批更新。

Spring Batch框架通常可以很好地处理这些功能。您的用例符合Spring作业的描述,该作业可以利用内置的csv读取器,处理器和基于块的处理来实现大量数据处理。可以使用Spring MVC控制器从UI触发作业。有关详细信息,请查看here