Load data local infile 是 MySQL 最快最有效率的导入数据的方法,在MySQL Server 8.0中,导入数据与之前的版本有一些差异,下面是跳坑指南。在早期MySQL版本中,服务器默认的 local_infile
是 ON 的,允许直接在客户端加载文件导入,但是在 MySQL Server 8.0 以上版本中,默认是 OFF,所以默认情况下,导入数据会提示错误:
解决办法:
在JDBC中,首先运行以下SQL代码,允许服务器运行 load data local infile
指令:
其次,在客户端的 JDBC 连接字符串,增加 allowLoadLocalInfile=true,例如:
spring.datasource.url=jdbc:mysql://server:3306/db?useUnitcode=true&characterEncoding=utf-8&allowLoadLocalInfile=true
这样,我们就可以运行导入文件的语句了,例如下面是一个文件上传导入的RestAPI Controller示例:
@RequestMapping("/import")
public AjaxResult<Object> importFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return AjaxResult.fail("Upload file is empty");
}
String fileType = file.getContentType();
try {
SqlHelper.execute("set global local_infile = 1;");
File tmpFile = File.createTempFile("apj", ".csv");
file.transferTo(tmpFile);
String sql = String.format("load data local infile '%s' " +
"replace into table 你的表名 " +
"fields terminated by ',' " +
"lines terminated by '\\r\\n' " +
"ignore 1 lines " +
"(id,字段1,字段2,字段3,字段4,字段5)", tmpFile.getPath().replace("\\", "\\\\"));
SqlHelper.execute(sql);
tmpFile.deleteOnExit();
return AjaxResult.ok(fileType);
} catch (IOException e) {
return AjaxResult.fail("Can't create temporary file: " + e.getMessage());
} catch (Exception e) {
return AjaxResult.fail((e.getCause() != null) ? e.getCause().getMessage() : e.getMessage());
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* SQL Helper
*/
@Component
@Slf4j
public class SqlHelper {
private static JdbcTemplate jdbcTemplate;
@Autowired
private SqlHelper(JdbcTemplate jdbcTemplate) {
SqlHelper.jdbcTemplate = jdbcTemplate;
}
/**
* Query for first column of first record
*
* @param sql SQL statement
* @return first column integer of first record
*/
public static Integer queryForInt(String sql) {
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
Object obj = map.values().iterator().next();
if (obj == null) {
return null;
}
return Integer.parseInt(obj.toString());
}
public static String version() {
return queryForString("select version()");
}
/**
* Query for single record with single field
*
* @param sql SQL statement
* @return first column value of first record
*/
public static String queryForString(String sql) {
Map<String, Object> map = jdbcTemplate.queryForMap(sql);
Object obj = map.values().iterator().next();
if (obj == null) {
return null;
}
return obj.toString();
}
/**
* Run SQL and return List Object
* <p>
* Usage:
* <pre>{@code
* RowMapper<Result> mapper = new BeanPropertyRowMapper<>(Result.class);
* List<Result> ret = SqlHelper.query("select * from ete_result", mapper);
* }</pre>
*
* @param sql SQL statement
* @param rowMapper RowMapper
* @param <T> Type
* @return List Object Of T
*/
public static <T> List<T> query(String sql, RowMapper<T> rowMapper) {
return jdbcTemplate.query(sql, rowMapper);
}
public static void execute(String sql) {
jdbcTemplate.execute(sql);
}
}