You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Eclipse动态Web项目:Spring(4) MVC+Hibernate(4)文件读取及MySQL上传咨询

嘿,我来一步步拆解在Spring 4 MVC + Hibernate 4环境下,实现批量导入手机号文件到MySQL的方案——毕竟处理大量数据,得兼顾效率和稳定性,以下都是实战过的流程:

1. 先搞定Spring MVC的文件上传配置

Spring MVC默认不支持文件上传,得先配置MultipartResolver,用Apache的Commons FileUpload组件就行。

首先在你的spring-servlet.xml里加这段配置:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 设置最大上传文件大小,比如10MB -->
    <property name="maxUploadSize" value="10485760"/>
    <!-- 设置默认编码 -->
    <property name="defaultEncoding" value="UTF-8"/>
</bean>

然后别忘了在pom.xml(如果用Maven)里加依赖:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

如果是手动导包,就下载这两个jar包放到WEB-INF/lib里。

2. 写个简单的前端上传页面

弄个JSP页面(比如upload.jsp),用表单提交文件,注意一定要加enctype="multipart/form-data"

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>手机号批量上传</title>
</head>
<body>
    <h3>上传手机号文件(每行一个手机号)</h3>
    <form action="${pageContext.request.contextPath}/phone/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="phoneFile" accept=".txt,.csv" required/>
        <br/><br/>
        <input type="submit" value="开始上传"/>
    </form>
</body>
</html>

3. 编写Spring MVC控制器处理上传请求

控制器里接收上传的文件,然后调用Service层处理批量导入:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/phone")
public class PhoneUploadController {

    @Autowired
    private PhoneService phoneService;

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public ModelAndView uploadPhoneFile(@RequestParam("phoneFile") MultipartFile file) {
        ModelAndView mv = new ModelAndView("uploadResult");
        try {
            if (file.isEmpty()) {
                mv.addObject("msg", "请选择要上传的文件!");
                return mv;
            }
            // 调用Service处理批量导入
            int successCount = phoneService.batchImportPhones(file);
            mv.addObject("msg", "成功导入 " + successCount + " 条手机号数据!");
        } catch (Exception e) {
            mv.addObject("msg", "上传失败:" + e.getMessage());
            e.printStackTrace();
        }
        return mv;
    }
}

4. Service层实现批量导入核心逻辑

这里要注意批量处理,避免一次性加载大量数据到内存导致OOM,同时用Hibernate的批量插入优化效率:

先写实体类Phone.java,对应MySQL的表(比如phone_numbers,字段id自增,phone唯一):

import javax.persistence.*;

@Entity
@Table(name = "phone_numbers")
public class Phone {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "phone", unique = true, nullable = false, length = 11)
    private String phone;

    // 省略getter、setter、构造方法
}

然后是Service接口和实现:

// PhoneService接口
public interface PhoneService {
    int batchImportPhones(MultipartFile file) throws Exception;
}

// 实现类
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

@Service
@Transactional
public class PhoneServiceImpl implements PhoneService {

    @Autowired
    private SessionFactory sessionFactory;

    // 手机号正则(11位数字)
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    // 每批次提交的数量,根据服务器内存调整,比如1000
    private static final int BATCH_SIZE = 1000;

    @Override
    public int batchImportPhones(MultipartFile file) throws Exception {
        int count = 0;
        int batchCount = 0;
        Session session = sessionFactory.getCurrentSession();

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(file.getInputStream(), "UTF-8"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                // 跳过空行和格式不正确的手机号
                if (StringUtils.isEmpty(line) || !PHONE_PATTERN.matcher(line).matches()) {
                    continue;
                }

                Phone phone = new Phone();
                phone.setPhone(line);
                // 保存到Session
                session.save(phone);
                count++;
                batchCount++;

                // 每BATCH_SIZE条提交一次
                if (batchCount % BATCH_SIZE == 0) {
                    session.flush();
                    session.clear();
                    batchCount = 0;
                }
            }
            // 提交剩余的数据
            if (batchCount > 0) {
                session.flush();
                session.clear();
            }
        }
        return count;
    }
}

5. Hibernate批量插入优化配置

要让Hibernate的批量插入生效,得在hibernate.cfg.xml里加这些配置:

<!-- 批量操作的大小,和Service里的BATCH_SIZE对应 -->
<property name="hibernate.jdbc.batch_size">1000</property>
<!-- 开启插入排序,优化批量插入效率 -->
<property name="hibernate.order_inserts">true</property>
<!-- 开启更新排序 -->
<property name="hibernate.order_updates">true</property>
<!-- 关闭二级缓存(批量插入不需要缓存,反而占内存) -->
<property name="hibernate.cache.use_second_level_cache">false</property>

6. 额外优化和注意事项

  • 重复手机号处理:如果要避免重复导入,可以在Service里先查询手机号是否存在,或者给MySQL的phone字段加唯一约束,然后用Hibernate的saveOrUpdate,或者捕获SQL异常(比如DuplicateKeyException)跳过重复数据。
  • 文件格式支持:如果是CSV文件,可以用OpenCSV之类的库解析,比手动读行更稳妥。
  • 日志记录:用SLF4J+Logback记录导入日志,比如成功导入多少条,跳过多少条错误数据,方便排查问题。
  • 大文件处理:如果文件特别大(比如几十MB),可以考虑用分片上传,或者后台异步处理(比如用Spring的@Async),避免前端超时。

内容的提问来源于stack exchange,提问作者Mohit Singh

火山引擎 最新活动