Java 프레임워크 만들기 - JSP

자바 마리아디비 문자형 데이터 타입(mediumtext, longtext) PreparedStatement 처리 3 - Java MariaDB String Data Types PreparedStatement

carrotweb 2021. 8. 2. 00:19
728x90
반응형

CLOB(Character large object)은 Oracle에서 아주 긴 문자열 데이터를 저장할 수 있는 데이터 타입이다.

이 타입은 테이블 자체에서 저장되지 않고 별도의 위치에 저장됩니다.

이처럼 MariaDB에서도 아주 긴 문자열 데이터를 저장할 수 있는 MEDIUMTEXT와 LONGTEXT가 있습니다.

MEDIUMTEXT - 중간 크기의 텍스트 (TEXT 보다는 크고 LONGTEXT보다 작아서 중간 크기)

MEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]

최대 길이는 16,777,215입니다. (1 character = 1Byte)

유효 최대 길이는 사용되는 Charset에 따라 달라집니다.

Charset이 UTF8이면 한글은 문자당 최대 3Byte가 필요함으로 한글만 입력할 경우 유효 최대 길이는 5,592,405입니다. 입력된 문자열에 멀티바이트 문자(한글)가 포함된 경우 유효 최대 길이가 짧아지는 것을 고려해야 합니다.

저장할 때 공백은 제거되지 않습니다.

 

CREATE TABLE DAT_TEST14_TB (
	COL_MEDIUMTEXT1 mediumtext
) DEFAULT CHARSET=utf8;
String query = "INSERT INTO DAT_TEST14_TB (COL_MEDIUMTEXT1) VALUES (?)";

 

테스트를 위해 /src/test/resources에 대용량의 파일(test2/text.dat)을 저장합니다.

String resourceName = "test2/text.dat";
ClassLoader classLoader = getClass().getClassLoader();

File file = new File(classLoader.getResource(resourceName).getFile());

 

PreparedStatement에서는 다양한 방법으로 대용량 데이터를 처리할 수 있습니다.

1. 대용량 파일을 Char(문자)로 읽어 들어 String으로 변환하여 저장합니다.

StringBuffer stringBuffer = new StringBuffer();

BufferedReader reader = null;
try {
	reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
	char[] buf = new char[1024];
	while ((reader.read(buf, 0, buf.length)) != -1) {
		stringBuffer.append(buf);
	}
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if (reader != null) {
		try {
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

String data1 = stringBuffer.toString().trim();

preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, data1);

 

2. 대용량 파일을 Byte(바이트)로 읽어 들어 String으로 변환하여 저장합니다.

FileInputStream fileInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
	fileInputStream = new FileInputStream(file);
	byteArrayOutputStream = new ByteArrayOutputStream();
	byte[] buffer = new byte[1024];
	int length = 0;
	while ((length = fileInputStream.read(buffer)) != -1) {
		byteArrayOutputStream.write(buffer, 0, length);
	}
} catch (IOException e) {
	e.printStackTrace();
} finally {
	try {
		byteArrayOutputStream.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
	try {
		fileInputStream.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

String data1 = new String(byteArrayOutputStream.toByteArray());

preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, data1);

결과는 테스트나 바이트나 동일합니다.

만약, 이진 데이터로 되어 있는 것을 읽어 저장하고 싶으시다면 MEDIUMTEXT 타입이 아닌 BLOB 타입을 사용하시기 바랍니다. BLOB는 다음에 소개하겠습니다.

이진 데이터로된 파일을 Byte로 읽어 들어 MEDIUMTEXT 타입에 저장하면 다음과 같은 에러가 발생합니다.

java.sql.SQLSyntaxErrorException: (conn=319) Incorrect string value: '\xF0\x95\xBD\xB6\xEF\xBF...' for column `test`.`dat_test14_tb`.`COL_MEDIUMTEXT1` at row 1
Caused by: org.mariadb.jdbc.internal.util.exceptions.MariaDbSqlException: Incorrect string value: '\xF0\x95\xBD\xB6\xEF\xBF...' for column `test`.`dat_test14_tb`.`COL_MEDIUMTEXT1` at row 1
Caused by: java.sql.SQLException: Incorrect string value: '\xF0\x95\xBD\xB6\xEF\xBF...' for column `test`.`dat_test14_tb`.`COL_MEDIUMTEXT1` at row 1

 

그러나 MariaDB에서는 4Byte을 사용하는 Emoji(이모지)는 저장할 수 있게 utf8mb4라는 Charset이 있습니다. 이 Charset은 이용한다면 저장할 수 있습니다.

CREATE TABLE DAT_TEST14_TB (
	COL_MEDIUMTEXT1 mediumtext CHARACTER SET utf8mb4 COLLATE 'utf8mb4_general_ci'
) DEFAULT CHARSET=utf8;

BLOB 타입이 있기 때문에 권장은 하지 않습니다.

3. 대용량 파일을 AsciiStream(스트림)으로 저장합니다.

FileInputStream fileInputStream = null;
try {
	fileInputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
	e.printStackTrace();
}

preparedStatement = connection.prepareStatement(query);
preparedStatement.setAsciiStream(1, fileInputStream);

preparedStatement.executeUpdate() 메소드 실행 후 fileInputStream.close()를 실행하시면 됩니다.

MEDIUMTEXT 타입에 매우 큰 ASCII 값이 입력되면 java.io.InputStream을 이용하는 것이 실용적입니다.

파일 끝에 도달할 때까지 java.io.InputStream에서 데이터를 읽습니다. 그리고 JDBC 드라이버는 ASCII 값을Char 형식으로 변환하여 처리합니다. 위에 설명한 2번과 같이 동작합니다.

다양한 방법이 있겠지만 대용량 파일인 경우 3번을 사용하시는 것을 개인적으로 권장합니다.

4. 대용량 파일을 CharacterStream(스트림)으로 저장합니다.

BufferedReader reader = null;
try {
	reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
} catch (IOException e) {
	e.printStackTrace();
}

preparedStatement = connection.prepareStatement(query);
preparedStatement.setCharacterStream(1, reader);

preparedStatement.executeUpdate() 메소드 실행 후 reader.close()를 실행하시면 됩니다.

위에 설명한 1번과 같이 동작합니다.

5. 대용량 파일을 Clob으로 저장합니다.

BufferedReader reader = null;
try {
	reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
} catch (IOException e) {
	e.printStackTrace();
}

preparedStatement = connection.prepareStatement(query);
preparedStatement.setClob(1, reader);

위에 설명한 4번과 같이 동작합니다.

SELECT 쿼리문으로 가져올때도 다양한 방법으로 대용량 데이터를 가져올 수 있습니다.

1. ResultSet에서 getString()으로 가져옵니다.

String query = "SELECT COL_MEDIUMTEXT1 AS mediumtext1 FROM DAT_TEST14_TB";
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
	String data1 = resultSet.getString("mediumtext1");
	System.out.println("mediumtext1 := \"" + data1 + "\"");
}
resultSet.close();

 

2. ResultSet에서 AsciiStream(스트림)으로 가져옵니다.

String query = "SELECT COL_MEDIUMTEXT1 AS mediumtext1 FROM DAT_TEST14_TB";
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
	InputStream inputStream = resultSet.getAsciiStream("mediumtext1");
    if (inputStream != null) {
    	StringBuffer stringBuffer = new StringBuffer();

    	byte[] buffer = new byte[1024];
		int length = 0;
		try {
			while ((length = inputStream.read(buffer)) != -1) {
				stringBuffer.append(new String(buffer, 0, length));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		String data1 = stringBuffer.toString();
		System.out.println("mediumtext1 := \"" + data1 + "\"");

		try {
			inputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
}
resultSet.close();

InputStream를 이용하여 바로 파일로도 저장할 수 있고 response.getOutputStream()메소드를 이용해 파일로도 첨부할 수 있습니다. 다음에 파일 다운로드를 설명하겠습니다.

3. ResultSet에서 CharacterStream(스트림)으로 가져옵니다.

String query = "SELECT COL_MEDIUMTEXT1 AS mediumtext1 FROM DAT_TEST14_TB";
PreparedStatement preparedStatement = connection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
	Reader reader = resultSet.getCharacterStream("mediumtext1");
	if (reader != null) {
		StringBuffer stringBuffer = new StringBuffer();

    	char[] buffer = new char[1024];
		int length = 0;
		try {
			while ((length = reader.read(buffer)) != -1) {
				stringBuffer.append(new String(buffer, 0, length));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		String data1 = stringBuffer.toString();
		System.out.println("mediumtext1 := \"" + data1 + "\"");

		try {
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
resultSet.close();

 

4. ResultSet에서 Clob의 CharacterStream(스트림)으로 가져옵니다.

Reader reader = resultSet.getClob("mediumtext1").getCharacterStream();

처리방식은 3번과 동일합니다.

LONGTEXT - 큰 크기의 텍스트

LONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]

최대 길이는 4,294,967,295입니다. (1 character = 1Byte)

유효 최대 길이는 사용되는 Charset에 따라 달라집니다.

Charset이 UTF8이면 한글은 문자당 최대 3Byte가 필요함으로 한글만 입력할 경우 유효 최대 길이는 1,431,655,765입니다. 입력된 문자열에 멀티바이트 문자(한글)가 포함된 경우 유효 최대 길이가 짧아지는 것을 고려해야 합니다.

저장할 때 공백은 제거되지 않습니다.

 

CREATE TABLE DAT_TEST15_TB (
	COL_LONGTEXT1 longtext
) DEFAULT CHARSET=utf8;
String query = "INSERT INTO DAT_TEST15_TB (COL_LONGTEXT1) VALUES (?)";

 

처리방식은 MEDIUMTEXT와 동일합니다.

728x90
반응형