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




