ํฐ์คํ ๋ฆฌ ๋ทฐ
MyBatis์ ResultHandler๋ฅผ ํตํด POI SXSSF๋ก ๋์ฉ๋ ์์ ํ์ผ ๋ง๋ค๊ธฐ
์ฃผ๋ฌ 2021. 12. 24. 23:11โ MyBatis์ ResultHandler๋ฅผ ํตํด POI SXSSF๋ก ๋์ฉ๋ ์์ ํ์ผ ๋ง๋ค๊ธฐ
๊ฐ๋ฐํ๊ฒฝ
- SpringBoot 2.6.2
- JAVA 1.8
- Gradle 7.3.2
- IntelliJ
์ ์ฐ ๋ฐ์ดํฐ๋ฅผ ์์ ํ์ผ๋ก ๋ง๋๋ ๊ณผ์ ์์ ์ ์ ๊ฑด์๋ฅผ ๋ง๋ค๋๋ ์ด์๊ฐ ์์์ง๋ง ๋ช์ญ๋ง ๊ฑด ๋๋ ๋ช๋ฐฑ๋ง๊ฑด์ ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ์ ํธ์ถํ๊ฒ ๋๋ฉด์ OOM์ ๋ง๋๊ฒ ๋์ด ์์ ํ์ผ๋ก ์ ์ฅ์ ํ ์๊ฐ ์์์ต๋๋ค. MyBatis๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด MyBatis์์ ์ ๊ณตํ๋ ResultHandler๋ฅผ ์ด์ฉํ๋ฉด ๋ช๋ฐฑ๋ง๊ฑด์ ๋ฐ์ดํฐ๋ฅผ ํ๋ฒ์ ํธ์ถํ์ง ์๊ณ ๋ ์ฝ๋๋ณ๋ก ๋ค๋ฃฐ์ ์์ต๋๋ค.
https://mybatis.org/mybatis-3/ko/java-api.html#SqlSession (์๋ ResultHandler ์ฐธ๊ณ )
๋ค์์ ๊ฐ์ง๊ณ ์จ ๋ฐ์ดํฐ์ ์ ์ฅ์ Apache POI SXSSF ๋ฐฉ์์ ์ด์ฉํ์ฌ ์์ ํ์ผ์ ์ ์ฅํ์์ต๋๋ค. SXSSF๋ Streaming๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๊ณ์ ๊ฐ์ง๊ณ ์์ง ์๊ณ ์์ ํ์ผ์ ๊ธฐ๋กํ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋น์๋ด๋ ๋ฐฉ์์ ๋๋ค. ์์ํ์ผ์ ๊ธฐ๋ณธ ๊ฒฝ๋ก๋ ๋ฆฌ๋ ์ค ์๋ฒ ๊ธฐ์ค์ผ๋ก /var/tmp/poi??? ๋งฅ๋ถ๊ธฐ์ค ํฐ๋ฏธ๋์์ echo $TMPDIR๋ฅผ ์ณ์ ์์๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ์ poi??? ๋๋ ํ ๋ฆฌ ๋ค์ด๊ฐ์ ํ์ธํด๋ณด๋ฉด ๋ฉ๋๋ค. ์์ํ์ผ ์ฉ๋์ ๋ฐ์ดํฐ ์์ถ์ต์ ์ ์์ฃผ์์ ๊ฒฝ์ฐ 100๋ง๊ฑด๋น 2G๊ฐ ์ ๋์๊ณ ์์ถ์ต์ ์ ์ฃผ์์ ๊ฒฝ์ฐ 256M๊ฐ ์ ๋์์ต๋๋ค. ์์ถํ๋ฉด ์์ ๋ง๋ค์ด์ง๋ ์๋๊ฐ ๋๋ฆด๊ฒ ๊ฒ๊ฐ์์ง๋ง ๊ฑฐ์ ์ฐจ์ด ์์์ต๋๋ค. ์๋ฒ ๊ธฐ์ค์ผ๋ก ์ฉ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ํ์ผ์ด ๋ง๋ค์ด์ง๋ ๊ฒฝ๋ก๋ ์ง์ ํ์์ต๋๋ค.
https://poi.apache.org/components/spreadsheet/
POI ์ฐ๊ธฐ ํ์ธ
HSSF | ์์ ์ด์ ๋ฒ์ ผ(.xls) | 65535๋ผ์ธ๊น์ง ๊ฐ๋ฅ |
XSSF | ์์ 2007 ์ด์๋ฒ์ ผ(.xls) | 65535๋ผ์ธ ์ด์ ๊ฐ๋ฅ |
SXSSF | XSSF์ Streaming Usermodel API | 65535๋ผ์ธ ์ด์ ๊ฐ๋ฅ |
์์ ์ํธ ์ต๋ ํ์ ํ์ธ
xls | 65535 ํ |
xlsx | 1,048,576 ํ |
MyBatis์ ResultHandler ์ ํ
TestDao
@RequiredArgsConstructor
@Repository
public class TestDAOImpl implements TestDAO {
private final SqlSessionTemplate sqlSessionTemplate;
@Override
public boolean createExcelByTmpTableMap(TmpTableSchCmd tmpTableSchCmd, TestExcelHandler testExcelHandler) {
sqlSessionTemplate.select("com.sample.batch.dao.TestDAO.selectMapOfTmpTable", tmpTableSchCmd, testExcelHandler);
testExcelHandler.createExcelByTmpFile();
return testExcelHandler.isSuccess();
}
}
์ฐ์ MyBatis์์ ResultHandler๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ sqlsessionTemplate์ selectํจ์์ ๋ค์ด๊ฐ ์ดํด๋ณด๋ฉด ์์ ๊ฐ์ด ResultHandler๋ผ๋ ๊ฒ์ ๋งค๊ฐ๋ณ์ ๋ฐ๋ ๊ฒ์ ํ์ธ ํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ์ฐ๋ฆฌ๋ ResultHandler๋ฅผ ๊ตฌํํ ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ์ ๋ฌํ๋ฉด ๋ฉ๋๋ค. (์น์ ํ ์ฃผ์์ผ๋ก ResultHandler๋ฅผ ์ฌ์ฉํ์ฌ ํค์ ํ๋ผ๋ฏธํฐ ๋ฌธ์ ๋งคํ๋ ๋จ์ผ ํ์ ์กฐํํฉ๋๋ค) ์ฌ๊ธฐ์ ํ๋ฒ๋ ResultHandler์ ํด๋์ค๋ ๋ฐ๋ผ ๋ค์ด๊ฐ ๋ณด๊ฒ ์ต๋๋ค.
interface๋ก ์ ์ธ๋์ด์๊ณ generic ํ์ ์ผ๋ก ๋์ด์๋๊ฑธ ๋ณด๋ MyBatis์ resultType์ ์ ํ ๋ ๊ฐ์ฒด๊ฐ ๋ฆฌํด๋ ๊ฒ์ผ๋ก ์์ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ResultHandler๋ฅผ ๊ตฌํํ๋ฉด MyBatis์์ ResultHandler๋ถ๋ถ์ ์ญํ ์ ๋์ ๋๋ค
์ฆ selectํจ์์ ResultHandler๋ฅผ ๊ตฌํํ class๋ฅผ ๋ฃ์ด์ฃผ๋ฉด handlerResult์ resultType์ object๊ฐ ๋ฆฌํด๋ฉ๋๋ค
@Slf4j
public abstract class ExcelResultHandler implements ResultHandler {
protected SXSSFWorkbook sxssfWorkbook;
protected SXSSFSheet sxssfSheet;
// row = xls:65,535 xlsx:1,048,576
protected final int MAX_SHEET_ROW = 1000000;
private FlushControl flushControl;
protected int sheetRowCnt = 1;
protected ExcelFileInfo excelFileInfo;
public ExcelResultHandler(ExcelFileInfo excelFileInfo, FlushControl flushControl) {
this.excelFileInfo = excelFileInfo;
this.flushControl = flushControl;
// 0. ์ํฌ๋ถ ์์ฑ
this.sxssfWorkbook = new SXSSFWorkbook(flushControl.getRowAccessWindowSize()); // rowAccessWindowSize๊ฐ์ ๋ก์ฐ๋ง ๋ฉ๋ชจ๋ฆฌ ๋ณด์ , ๋๋จธ์ง ๋์คํฌ๋ก ๋ด๋ณด๋ธ๋ค.
sxssfWorkbook.setCompressTempFiles(true); // ์์ํ์ผ ์์ถ์ฌ๋ถ
}
... ์ค๊ฐ ์๋ต
/**
* rowCnt๋ 1๋ถํฐ ์์ํจ
*/
public abstract void createExcelBody(int rowCnt, Object dbData);
@Override
public void handleResult(ResultContext resultContext) {
createExcelBody(resultContext.getResultCount(), resultContext.getResultObject());
}
ResultHandler๋ฅผ ๋ฐ๋ก class์ ๋ฐ์์ ๊ตฌํํด์ค๋ ๋์ง๋ง abstract๋ก ๋ฐ์์ ๊ธฐ๋ณธ์ ์ธ ๊ณตํต ๋ถ๋ถ (POI ์ํฌ๋ถ์์ฑ, ์ํธ์์ฑ, ํค๋์์ฑ) ๋ฑ์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ ์ ์๋๋ก ํ์๊ณ @Override๋์ด์๋ handlerResult์ createExcelBody์ ํจ์๋ง ์ถ์ํด๋์ค๋ก ํ์ฌ ์์๋ฐ๋ class๊ฐ ๊ตฌํํ๊ฒ ํ์์ต๋๋ค.
@Slf4j
public class TestExcelHandler extends ExcelResultHandler {
public TestExcelHandler(ExcelFileInfo excelFileInfo, FlushControl flushControl) {
super(excelFileInfo, flushControl);
}
@Override
public void createExcelBody(int rowCnt, Object dbData) {
//์ํฌ๋ถ์์ฑ -> ์ํธ์์ฑ -> ์ ํ์ดํ์์ฑ
if(rowCnt % MAX_SHEET_ROW == 1) {
createSXSSFSheet((rowCnt/MAX_SHEET_ROW)+1);
//HashMap ์๋ถ๋ถ์ resultType์ผ๋ก castingํ์ธ์ ์ ๋ map
createCellTitle((HashMap<String, Object>)dbData);
//์๋ก์ด ์ํธ์ ROW๋ 1๋ถํฐ ์์
sheetRowCnt = 1;
}
//ROW ์์ฑ
createDataCell((HashMap<String, Object>)dbData);
//...์ค๊ฐ์ค๊ฐ์ flush
flushMem(rowCnt);
}
}
POI ์์ํ์ผ ์ฉ๋ ์์ถ ๋ฐ ๊ฒฝ๋ก ์์
// 0. ์ํฌ๋ถ ์์ฑ
this.sxssfWorkbook = new SXSSFWorkbook(flushControl.getRowAccessWindowSize()); // rowAccessWindowSize๊ฐ์ ๋ก์ฐ๋ง ๋ฉ๋ชจ๋ฆฌ ๋ณด์ , ๋๋จธ์ง ๋์คํฌ๋ก ๋ด๋ณด๋ธ๋ค.
sxssfWorkbook.setCompressTempFiles(true); // ์์ํ์ผ ์์ถ์ฌ๋ถ
POI ์์ํ์ผ์ ์์ถ์ setCompressTempFiles(true)๋ก ์ค์ ํ๋ฉด gz์ผ๋ก ์์ถ์ด ๋ฉ๋๋ค
๋ญ ์ด๊ฒ๋ ํจ์ ๋ฐ๋ผ๊ฐ๋ค ๋ณด๋ฉด ๊ทธ๋ ๊ฒ ์ ์๋์ด ์์ต๋๋ค ^^;;
์์ํ์ผ๊ฒฝ๋ก๋ new SXSSFWorkbook์ ํด๋์ค๋ฅผ ๋ฐ๋ผ์ ์ถ์ ํ๋ค๋ณด๋ฉด ์๋์ ๊ฐ์ด TempFIle.createTempFile ์ด๋ผ๋ ํจ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
TempFIle.createTempFile์ ๋ ์ซ์๊ฐ๋ณด๋ฉด ์๋์ ๊ฐ์ผ๋ฉฐ
strategy๋ฅผ ์ซ์๊ฐ๋ณด๋ฉด ์๋์ ๊ฐ์ด ๊ธฐ๋ณธ์ผ๋ก DefaultTempFileCreationStrategy๋ฅผ ๊ตฌํํ ํด๋์ค๋ค์
DefaultTempFileCreationStrategy๋ฅผ ๋ณด๋ฉด ์์ฑ์์ ํ์ผ์ ๋๋ ํ ๋ฆฌ๊ฐ ๋ณด์ ๋๋ค
TempFile์ ๊ทธ ์๋๋ฅผ ๋ณด๋ฉด strategy๋ฅผ setTempFileCreationStrategy(TempFileCreationStrategy strategy)๊ฐ ์๋ค์
์ด์ ํผ์ฆ์ ๋ง์ถฐ ๋ณด๊ฒ ์ต๋๋ค.
TempFile์ .setTempFileCreationStrategy ํจ์์ TempFileCreationStrategy๋ฅผ ๊ตฌํํ ํด๋์ค DefaultTempFileCreationStrategy๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๊ฒ ๋ค์ ๋ฐ๋ผ์ DefaultTempFileCreationStrategy๋ฅผ ์์ฑํ ๋ File Dir๋ฅผ ๋ด๊ฐ ์์ฑํด ์ง์ ํด์ฃผ๊ณ DefaultTempFileCreationStrategy๋ฅผ ์์ฑํด์ setํด์ฃผ๋ฉด ํด๊ฒฐ ๋๊ฒ ๋ค์
/**
* org.apache.poi tmpํ์ผ ๊ฒฝ๋ก ์์
*/
private void setTmpPath() {
TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(new File(excelProperties.getTmpDirPath())));
}
์ด์ setTmpPathํจ์๋ฅผ POI ์ํฌ๋ถ์ ์์ฑํ๊ธฐ์ ์ ๋ฏธ๋ฆฌ ์คํ์ ํ๋ฉด ์์ํ์ผ์ ๋ด๊ฐ ์ง์ ํ ๊ฒฝ๋ก์ ๋ง๋ค์ด์ง๋๋ค
POI SXSSF ์๋ or ์๋ flush
์๋์์ ๋งํ๋ฏ์ด SXSSF๋ Streaming๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ๊ณ์ ๊ฐ์ง๊ณ ์์ง ์๊ณ ์ค์ ๋ ๊ฐ์ ๋์ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ๋ค๊ณ ์๋ ๋ฐ์ดํฐ๋ฅผ ์์ ํ์ผ์ ๊ธฐ๋กํ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋น์๋ด๋ ๋ฐฉ์์ ๋๋ค. ์ด๋ ๊ฒ ๋ฐ๋ณตํ์ฌ ๋ง์ง๋ง๊น์ง ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์์ํ์ผ์ ๋ง์ง๋ง์๋ ์ค์ ํ ์์ ํ์ผ๋ก ๋ง๋ค์ด์ค๋๋ค. ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋น์๋ผ๋ ์๋์ผ๋ก ์ ํ ์ ํ ์ ์๊ณ ์๋๋ฉด ์๋์ผ๋ก ์ค์ ํ ์ ์์ต๋๋ค.
//์๋ ์์ฑํ ๋ ์ง์ ํ ๊ฐ(100)์ ๋๋ฌํ๋ฉด flush
this.sxssfWorkbook = new SXSSFWorkbook(100);
//์๋์ผ๋๋ -1์ด๋ฉด ๋ฌด์ ํ ์ก์ธ์ค์ด๊ณ ์ค๊ฐ์ค๊ฐ์ ์๋์ผ๋ก flushํด์ผํฉ๋๋ค.
this.sxssfWorkbook = new SXSSFWorkbook(-1);
/**
* rowAccessWindowSize -1์ด๋ฉด ์๋ flush์๋
*/
protected void flushMem(int rowCnt) {
if(flushControl.getRowAccessWindowSize() == -1 && rowCnt % flushControl.getManualFlushSize() == 1) {
try {
//100 ์์ด๋ฉด flush
((SXSSFSheet)sxssfSheet).flushRows(100);
log.info("[Excel] flushMem {}", rowCnt);
} catch (IOException e) {
e.printStackTrace();
}
}
}
์์ ๋ด์ฉ์ https://poi.apache.org/components/spreadsheet/how-to.html#sxssf ์ฌ๊ธฐ์ ์ ๋์์์ต๋๋ค
ํ ์คํธ๋ก ๋ง๋ค์ด๋ณธ ์์ค
'๐บ Develop > ๐ด Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring MDC๋ฅผ ์ฌ์ฉํด ์๋ณ๊ฐ๋ฅํ ๋ก๊ทธ ๋จ๊ธฐ๊ธฐ (0) | 2023.07.17 |
---|---|
Springboot graceful shutdown ์ ์ฉํ๊ธฐ (0) | 2023.07.14 |
Springboot ์ฝ์ ๋ฐฐ๋ ๋ณ๊ฒฝ (0) | 2023.03.05 |
[Springboot] Jasypt ์ค์น ๋ฐ ์ค์ (0) | 2022.10.11 |
Gradle Multi Module ํ๋ก์ ํธ ์ ํ ๋ฐ ๋น๋ (0) | 2021.12.16 |
- Total
- Today
- Yesterday
- git gmail
- ๋งฅ ์ฑ ์ถ์ฒ
- ๋งฅ๋ฆฐ์ด ์ฑ ์ถ์ฒ
- ๋์์ธํจํด ์ฅ๋จ์
- ๋์์ธํจํด ๋จ์
- git user.gmail
- ๊ฐ์ฒด ์งํฅ ์ค๊ณ ์์น
- git name
- ๊ฐ์ฒด์งํฅ์ค๊ณ solid
- ๋์์ธํจํด ์ฅ์
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |