지난번 정리한걸 가져와보면...
2020/03/25 - [개발자/Java] - Java (자바) 함수 & 단축키 모음
Java (자바) 함수 & 단축키 모음
단축키 Option + Space (윈도우에서는 Ctrl + Space) : 자동완성 Command + Shift + 'O' (윈도우에서는 Ctrl + Shift + 'O') : 자동 임포트 Command + '/' (윈도우에서는 Ctrl + '/' ) : 주석 Command + 'F' :..
greendreamtrre.tistory.com
쿼리 보내기
String query =
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
int result = preparedStatement.executeUpdate(); 또는 ResultSet resultSet = preparedStatement.executeQuery();
String query =
Statement statement = this.connection.createStatement();
int result = statement.executeUpdate(query); 또는 ResultSet resultSet = statement.executeQuery(query);
1) PreparedStatement와 Statement는 query를 쓰는 위치가 다르다.
2) int result 는 쿼리의 실행 결과 return값이 없어도 되지만, ResultSet resultSet 는 쿼리의 실행 결과 return값이 반드시 필요하다. ResultSet은 쿼리의 실행 결과를 ResultSet이라는 객체로 받는다. 따라서, INSERT, UPDATE와 같은 return값이 없는 쿼리의 경우는 int result 를 써야하고, SELECT와 같이 return값이 있는 쿼리의 경우는 ResultSet resultSet 를 써서 return값을 객체로 받아야 다시 HomeController에 return을 할 수 있다.
우선 기존의 Statement를 PreparedStatement로 바꾸던것을 생각해보자
2020/06/07 - [개발자/Java] - Java (자바) Spring 기초 - 회원가입 페이지 만들기 (Users CRUD)
Java (자바) Spring 기초 - 회원가입 페이지 만들기 (Users CRUD)
1. Sqlite DB 테이블 생성하고 CSS 파일 만들기 CSS는 src > main > webapp > resources 아래에 넣는다. 2. 기본 폼 HomeController.java package com.KOPO.Users; import java.text.DateFormat; import java.uti..
greendreamtrre.tistory.com
Statement를 PreparedStatement로 바꿔보자
// signupAction을 Statement에서 PreparedStatement로 바꿔보자.
public int insertData(String name, String id, String password) throws SQLException {
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES (?, ?, ?);";
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // PreparedStatement를 이용해 접속 엔진을 연다.
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
int result = preparedStatement.executeUpdate(); // 엔진을 이용해 쿼리를 보낸다.
preparedStatement.close(); // 접속 엔진을 닫는다.
return result;
}
Statement는 쿼리문을 그대로 한 줄로 넣고,
PreparedStatement는 쿼리문에서 파라미터를 따로 분리한다.
이전 글 중간쯤에 있는 내용이다. 넘어온 데이터가 (String, String, String)이기 때문에 전부 preparedStatement.setString(1, name); 이런식으로 작성되었으나 int면 Int로 받고, double이면 Double로 받아야한다.
1. 우선 클래스를 읽어오고, 클래스의 내용을 읽어오고, 해당 클래스의 변수명과 변수의 타입을 읽어온다.
Class<?> dataClass = t.getClass(); // Student.java
Field[] dataClassFields = dataClass.getDeclaredFields(); // Student.java의 내용을 읽는다.
2. 그 다음 for each를 이용해 위에서 Field[] 자료형에 저장한 dataClassFields 변수로부터 field(each)로 읽어
기존의 String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);";
에서 (name, middleScore, finalScore)에 해당하는 값을 분리, fieldString이라는 변수에 담는다. (for each문 안에 있는 Step 2.)
// insert 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean insertData(T t) {
Class<?> dataClass = t.getClass(); // Student.java
Field[] dataClassFields = dataClass.getDeclaredFields(); // Student.java의 내용을 읽는다.
String fieldString = ""; // 변수명을 저장 (name, middleScore, finalScore)...
// for each를 이용해
for (Field field: dataClassFields) { // Student.java의 내용을 한 줄씩 읽는다.
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName(); // dataClassFields에서 각각 field(each)로 읽어온다... name, middleScore, finalScore
String fieldType = field.getType().toString(); // 위와 마찬가지로 field(each)로 읽어온다... String, int, int
if (fieldName.matches("idx")) { // idx는 insert때는 auto increment로 필요가 없으니 넘어간다.
continue;
}
// Step 2. fieldString 만들기
if (!fieldString.isEmpty()) { // 최초 인입 이후 두 번째 for문 돌 때 콤마를 추가해준다. (name, middleScore, finalScore)...
fieldString = fieldString + ","; // fieldString 자기 자신에게 ","를 하나씩 추가해준다.
}
fieldString = fieldString + fieldName; // 최초 인입 for문부터 fieldString 자기 자신에게 fieldName(name, middleScore, finalScore)를 하나씩 추가해준다.
}
return false;
}
3. 위 2번 과정과 마찬가지의 방법을 이용해
기존의 String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);";
에서 (?, ?, ?)에 들어갈 값과 데이터 타입을 분리, ArrayList 타입의 preparedValue에 저장(1)하고, 그 개수만큼 (?, ?, ?)을 valueString이라는 변수에 담는다.(2)
그러면 PreparedStatement가 컴파일 해놓은 쿼리문에 ArrayList<PreparedValue> preparedValue에 저장된 값과 데이터 타입을 불러와
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
for each문이 끝난 후 쿼리를 보내는 모듈에서 이 부분을 구현해내면 되는 것이다.
1 ) 우선, 이를 위해 해당 메소드 안에서만 사용할 이너 클래스를 만들어준다.
// Inner Class 만들기
class PreparedValue {
int type = 0;
int intValue = 0;
double floatValue = 0;
String stringValue = "";
PreparedValue(int intValue) {
this.type = 1;
this.intValue = intValue;
}
PreparedValue(double floatValue) {
this.type = 2;
this.floatValue = floatValue;
}
PreparedValue(String stringValue) {
this.type = 3;
this.stringValue = stringValue;
}
}
2 ) 그 다음 위 2번 과정과 마찬가지로 for each문을 이용해 preparedValue 변수에 값과 값의 타입을 담고, valueString에 값의 수만큼 "?"를 담는다. (for each 문 안에 있는 Step 3.)
// insert 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean insertData(T t) {
Class<?> dataClass = t.getClass(); // Student.java
Field[] dataClassFields = dataClass.getDeclaredFields(); // Student.java의 내용을 읽는다.
String fieldString = ""; // 변수명을 저장 (name, middleScore, finalScore)...
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
int type = 0;
int intValue = 0;
double floatValue = 0;
String stringValue = "";
PreparedValue(int intValue) {
this.type = 1;
this.intValue = intValue;
}
PreparedValue(double floatValue) {
this.type = 2;
this.floatValue = floatValue;
}
PreparedValue(String stringValue) {
this.type = 3;
this.stringValue = stringValue;
}
}
// 값을 저장할 배열을 변수로 선언
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field: dataClassFields) { // Student.java의 내용을 한 줄씩 읽는다.
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName(); // dataClassFields에서 각각 field(each)로 읽어온다... name, middleScore, finalScore
String fieldType = field.getType().toString(); // 위와 마찬가지로 field(each)로 읽어온다... String, int, int
if (fieldName.matches("idx")) { // idx는 insert때는 auto increment로 필요가 없으니 넘어간다.
continue;
}
// Step 3. valueString 만들기
if (!valueString.isEmpty()) { // 최초 인입 이후 두 번째 for문 돌 때 콤마를 추가해준다. ("홍길동", 100, 80)...
valueString = valueString + ","; // 더한 결과는 값1, 값2, 값3... 의 형태가 된다.
}
// 최초 인입 for문부터 preparedValue라는 ArrayList에 데이터 타입과 값을 담고,
// valueString 자기 자신에게는 그냥 아무 의미 없는 나중에 preparedStatement에 값을 넣기 위해 개수만 맞 ?, ?, ?를 하나씩 추가해준다.
try {
// preparedValue라는 ArrayList에 데이터 타입과 값을 담는다. (preparedStatement.setString(1, name);, preparedStatement.setInt(2, middleScore); 이 부분에 해당하는 값과 데이터 타입을 담는다.)
if (fieldType.matches("(int|long|short)")) {
preparedValue.add(new PreparedValue(field.getInt(t)));
} else if (fieldType.matches("(float|double)")) {
preparedValue.add(new PreparedValue(field.getDouble(t)));
} else if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(field.get(t).toString()));
}
valueString = valueString + "?"; // 기존의 String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);"; 에서 ??? 이걸 넣어주기 위한 로직.
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return false;
}
4. 이제 for each문을 하나의 모듈로 보고 다음 모듈에 PreparedStatement를 구현한다.
즉, 이걸 구현할거다.
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
이는 다음과 같이 구현할 수 있다.
// preparedValue라는 변수명을 가진 ArrayList에는 각각의 값이 객체로 들어가있다. << due to : preparedValue.add(new PreparedValue(field.getInt(t))) >>
// 이 객체를 하나씩 꺼내와 이너클래스의 타입을 이용해 int면 setInt로 i + 1 번째에 preparedValue로부터 get 한 값을 넣는다.
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).type == 1) {
preparedStatement.setInt(i + 1, preparedValue.get(i).intValue);
} else if (preparedValue.get(i).type == 2) {
preparedStatement.setDouble(i + 1, preparedValue.get(i).floatValue);
} else if (preparedValue.get(i).type == 3) {
preparedStatement.setString(i + 1, preparedValue.get(i).stringValue);
}
}
따라서 쿼리를 보내는 이 부분은
// signupAction을 Statement에서 PreparedStatement로 바꿔보자.
public int insertData(String name, String id, String password) throws SQLException {
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES (?, ?, ?);";
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // PreparedStatement를 이용해 접속 엔진을 연다.
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
int result = preparedStatement.executeUpdate(); // 엔진을 이용해 쿼리를 보낸다.
preparedStatement.close(); // 접속 엔진을 닫는다.
return result;
}
아래와 같다.
// 위에서 구한 fieldString(name, middleScore, finalScore...)와 valueString("홍길동", 100, 80)을 넣어 쿼리문을 만든다.
String query = "INSERT INTO " + this.tableName + " (" + fieldString + ") VALUES(" + valueString + ")";
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
// preparedValue라는 변수명을 가진 ArrayList에는 각각의 값이 객체로 들어가있다. << due to : preparedValue.add(new PreparedValue(field.getInt(t))) >>
// 이 객체를 하나씩 꺼내와 이너클래스의 타입을 이용해 int면 setInt로 i + 1 번째에 preparedValue로부터 get 한 값을 넣는다.
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).type == 1) {
preparedStatement.setInt(i + 1, preparedValue.get(i).intValue);
} else if (preparedValue.get(i).type == 2) {
preparedStatement.setDouble(i + 1, preparedValue.get(i).floatValue);
} else if (preparedValue.get(i).type == 3) {
preparedStatement.setString(i + 1, preparedValue.get(i).stringValue);
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
5. 해당 메소드가 boolean 타입이기 때문에 true, false를 추가해 아래와 같이 완성한다.
// insert 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean insertData(T t) {
Class<?> dataClass = t.getClass(); // Student.java
Field[] dataClassFields = dataClass.getDeclaredFields(); // Student.java의 내용을 읽는다.
String fieldString = ""; // 변수명을 저장 (name, middleScore, finalScore)...
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
int type = 0;
int intValue = 0;
double floatValue = 0;
String stringValue = "";
PreparedValue(int intValue) {
this.type = 1;
this.intValue = intValue;
}
PreparedValue(double floatValue) {
this.type = 2;
this.floatValue = floatValue;
}
PreparedValue(String stringValue) {
this.type = 3;
this.stringValue = stringValue;
}
}
// 값을 저장할 배열을 변수로 선언
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field: dataClassFields) { // Student.java의 내용을 한 줄씩 읽는다.
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName(); // dataClassFields에서 각각 field(each)로 읽어온다... name, middleScore, finalScore
String fieldType = field.getType().toString(); // 위와 마찬가지로 field(each)로 읽어온다... String, int, int
if (fieldName.matches("idx")) { // idx는 insert때는 auto increment로 필요가 없으니 넘어간다.
continue;
}
// Step 2. fieldString 만들기
if (!fieldString.isEmpty()) { // 최초 인입 이후 두 번째 for문 돌 때 콤마를 추가해준다. (name, middleScore, finalScore)...
fieldString = fieldString + ","; // fieldString 자기 자신에게 ","를 하나씩 추가해준다.
}
fieldString = fieldString + fieldName; // 최초 인입 for문부터 fieldString 자기 자신에게 fieldName(name, middleScore, finalScore)를 하나씩 추가해준다.
// Step 3. valueString 만들기
if (!valueString.isEmpty()) { // 최초 인입 이후 두 번째 for문 돌 때 콤마를 추가해준다. ("홍길동", 100, 80)...
valueString = valueString + ","; // 더한 결과는 값1, 값2, 값3... 의 형태가 된다.
}
// 최초 인입 for문부터 preparedValue라는 ArrayList에 데이터 타입과 값을 담고,
// valueString 자기 자신에게는 그냥 아무 의미 없는 나중에 preparedStatement에 값을 넣기 위해 개수만 맞 ?, ?, ?를 하나씩 추가해준다.
try {
// preparedValue라는 ArrayList에 데이터 타입과 값을 담는다. (preparedStatement.setString(1, name);, preparedStatement.setInt(2, middleScore); 이 부분에 해당하는 값과 데이터 타입을 담는다.)
if (fieldType.matches("(int|long|short)")) {
preparedValue.add(new PreparedValue(field.getInt(t)));
} else if (fieldType.matches("(float|double)")) {
preparedValue.add(new PreparedValue(field.getDouble(t)));
} else if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(field.get(t).toString()));
}
valueString = valueString + "?"; // 기존의 String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);"; 에서 ??? 이걸 넣어주기 위한 로직.
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 위에서 구한 fieldString(name, middleScore, finalScore...)와 valueString("홍길동", 100, 80)을 넣어 쿼리문을 만든다.
String query = "INSERT INTO " + this.tableName + " (" + fieldString + ") VALUES(" + valueString + ")";
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
// preparedValue라는 변수명을 가진 ArrayList에는 각각의 값이 객체로 들어가있다. << due to : preparedValue.add(new PreparedValue(field.getInt(t))) >>
// 이 객체를 하나씩 꺼내와 이너클래스의 타입을 이용해 int면 setInt로 i + 1 번째에 preparedValue로부터 get 한 값을 넣는다.
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).type == 1) {
preparedStatement.setInt(i + 1, preparedValue.get(i).intValue);
} else if (preparedValue.get(i).type == 2) {
preparedStatement.setDouble(i + 1, preparedValue.get(i).floatValue);
} else if (preparedValue.get(i).type == 3) {
preparedStatement.setString(i + 1, preparedValue.get(i).stringValue);
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
return true;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
테스트용 URL
http://localhost:8099/student/insert?name=테스트1&middleScore=20&finalScore=20 // 정상인 경우
http://localhost:8099/student/insert?name=테스트2&middleScore=20&finalStore=20 // 파라미터 이름이 잘못 들어온 경우에 대한 null 체크
http://localhost:8099/student/insert?name=테스트3&middleScore=20a&finalScore=20 // 파라미터 이름은 정상이나 숫자가 들어와야 하는 곳에 문자가 들어오는 경우에 대한 예외처리
http://localhost:8099/student/insert?name=테스트3&middleScore=20&finalScore= // 파라미터 이름은 정상이나 숫자가 들어와야 하는 곳에 빈 값이 들어온 경우에 대한 예외처리
6. update도 바꿔주자. (위 insert 와 조금 다른 방법으로 만드니 비교할 것)
(그리고 각 단계별로 한 번씩 더 축소한 코드는 파란색 문장 아래 있음)
1 ) 이너 클래스 생성하기
// Inner Class 만들기
class PreparedValue {
int type;
Object value;
public PreparedValue(int type, Object value) {
this.type = type;
this.value = value;
}
}
아래 2 ) 에서 보면 알겠지만 String의 경우만 특별히 분리해주면 되기 때문에 이렇게 한 번 더 줄일 수 있다.
// Inner Class 만들기
class PreparedValue {
boolean isString;
Object value;
PreparedValue(boolean isString, Object value) {
this.isString = isString;
this.value = value;
}
}
2 ) for each 문 돌기
// update 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean updateData(T t) {
Class<?> dataClass = t.getClass();
Field[] dataClassFields = dataClass.getDeclaredFields();
String whereString = ""; // where 조건을 저장 idx=7
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
int type;
Object value;
public PreparedValue(int type, Object value) {
this.type = type;
this.value = value;
}
}
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field : dataClassFields) {
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName();
String fieldType = field.getType().toString();
// Step 2. valueString 만들기 (insert에서는 이 과정을 Step 3.에서 했었다.)
if (!valueString.isEmpty()) {
valueString = valueString + ",";
}
try {
// idx는 value와 구분한다. idx일 때는 valueString = valueString + fieldName + "=?"; 는 실행이 되면 안 되므로 continue;
if (fieldName.matches("idx")) {
whereString = "idx=" + field.get(t);
continue;
}
if (fieldType.matches("(int|long|short)")) {
preparedValue.add(new PreparedValue(1, field.get(t)));
} else if (fieldType.matches("(float|double)")) {
preparedValue.add(new PreparedValue(2, field.get(t)));
} else if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(3, field.get(t)));
}
valueString = valueString + fieldName + "=?"; // 기존의 String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;";
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
for each 문 역시 이렇게 줄일 수 있다.
// update 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean updateData(T t) {
Class<?> dataClass = t.getClass();
Field[] dataClassFields = dataClass.getDeclaredFields();
String whereString = ""; // where 조건을 저장 idx=7
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
boolean isString;
Object value;
PreparedValue(boolean isString, Object value) {
this.isString = isString;
this.value = value;
}
}
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field : dataClassFields) {
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName();
String fieldType = field.getType().toString();
// Step 2. valueString 만들기 (insert에서는 이 과정을 Step 3.에서 했었다.)
if (!valueString.isEmpty()) {
valueString = valueString + ",";
}
try {
// idx는 value와 구분한다. idx일 때는 valueString = valueString + fieldName + "=?"; 는 실행이 되면 안 되므로 continue;
if (fieldName.matches("idx")) {
whereString = "idx=" + field.get(t);
continue;
}
if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(true, field.get(t)));
} else {
preparedValue.add(new PreparedValue(false, field.get(t)));
}
valueString = valueString + fieldName + "=?"; // 기존의 String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;";
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
3 ) PreparedStatement 구현
String query = "UPDATE " + this.tableName + " SET " + valueString + " WHERE " + whereString;
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).type == 1) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
} else if (preparedValue.get(i).type == 2) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
} else if (preparedValue.get(i).type == 3) {
preparedStatement.setString(i + 1, preparedValue.get(i).value.toString());
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
마찬가지로 PreparedStatement도 이너 클래스에 맞게 줄여준다.
String query = "UPDATE " + this.tableName + " SET " + valueString + " WHERE " + whereString;
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).isString) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value.toString());
} else {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
4 ) 하나로 완성하기
// update 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean updateData(T t) {
Class<?> dataClass = t.getClass();
Field[] dataClassFields = dataClass.getDeclaredFields();
String whereString = ""; // where 조건을 저장 idx=7
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
int type;
Object value;
public PreparedValue(int type, Object value) {
this.type = type;
this.value = value;
}
}
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field : dataClassFields) {
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName();
String fieldType = field.getType().toString();
// Step 2. valueString 만들기 (insert에서는 이 과정을 Step 3.에서 했었다.)
if (!valueString.isEmpty()) {
valueString = valueString + ",";
}
try {
// idx는 value와 구분한다. idx일 때는 valueString = valueString + fieldName + "=?"; 는 실행이 되면 안 되므로 continue;
if (fieldName.matches("idx")) {
whereString = "idx=" + field.get(t);
continue;
}
if (fieldType.matches("(int|long|short)")) {
preparedValue.add(new PreparedValue(1, field.get(t)));
} else if (fieldType.matches("(float|double)")) {
preparedValue.add(new PreparedValue(2, field.get(t)));
} else if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(3, field.get(t)));
}
valueString = valueString + fieldName + "=?"; // 기존의 String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;";
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
String query = "UPDATE " + this.tableName + " SET " + valueString + " WHERE " + whereString;
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).type == 1) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
} else if (preparedValue.get(i).type == 2) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
} else if (preparedValue.get(i).type == 3) {
preparedStatement.setString(i + 1, preparedValue.get(i).value.toString());
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
return true;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
코드 길이 줄인 버전
// update 쿼리 실행 (Inner Class를 활용한 preparedStatement)
public boolean updateData(T t) {
Class<?> dataClass = t.getClass();
Field[] dataClassFields = dataClass.getDeclaredFields();
String whereString = ""; // where 조건을 저장 idx=7
String valueString = ""; // 변수값을 저장 ("홍길동", 100, 80)...
// Inner Class 만들기
class PreparedValue {
boolean isString;
Object value;
PreparedValue(boolean isString, Object value) {
this.isString = isString;
this.value = value;
}
}
ArrayList<PreparedValue> preparedValue = new ArrayList<PreparedValue>();
// for each를 이용해
for (Field field : dataClassFields) {
// Step 1. 각 라인을 each로 읽어온다.
String fieldName = field.getName();
String fieldType = field.getType().toString();
// Step 2. valueString 만들기 (insert에서는 이 과정을 Step 3.에서 했었다.)
if (!valueString.isEmpty()) {
valueString = valueString + ",";
}
try {
// idx는 value와 구분한다. idx일 때는 valueString = valueString + fieldName + "=?"; 는 실행이 되면 안 되므로 continue;
if (fieldName.matches("idx")) {
whereString = "idx=" + field.get(t);
continue;
}
if (fieldType.matches(".*String")) {
preparedValue.add(new PreparedValue(true, field.get(t)));
} else {
preparedValue.add(new PreparedValue(false, field.get(t)));
}
valueString = valueString + fieldName + "=?"; // 기존의 String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;";
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
String query = "UPDATE " + this.tableName + " SET " + valueString + " WHERE " + whereString;
try {
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // 쿼리문을 컴파일
for (int i = 0; i < preparedValue.size(); i++) {
if (preparedValue.get(i).isString) {
preparedStatement.setObject(i + 1, preparedValue.get(i).value.toString());
} else {
preparedStatement.setObject(i + 1, preparedValue.get(i).value);
}
}
preparedStatement.executeUpdate();
preparedStatement.close();
return true;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
테스트용 URL
http://localhost:8099/student/update?idx=19&name=공룡&middleScore=10&finalScore=13
★ insert와 update에서 조금 다른 방식으로 구현했다. 무엇이 다를까?
(웹 브라우저 가로 픽셀이 넓어야 정상으로 보임... 대략 720p 급이면 보일듯)
insert | update | ||||||||||
|
|
|
|||||||||
int type 인스턴스로 클래스 내에서 미리 지정 | Object로 받고 String만 구분한다 | ||||||||||
|
|
|
|||||||||
이너 클래스의 생성자에 맞게 타입을 지정해 객체 생성 | String만 구분한다 | ||||||||||
|
String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);"; | String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;"; | |||||||||
쿼리 차이 비교하기 | 쿼리 차이 비교하기 | ||||||||||
|
|
|
|||||||||
이미 형태까지 모두 지정되었기 때문에 이너클래스를 보고 인스턴스를 직관적으로 가져온다 | String만 구분 |
'개발자 > Java' 카테고리의 다른 글
IntelliJ (인텔리제이) Run/Debug Configurations 실행 환경 (0) | 2020.09.11 |
---|---|
IntelliJ (인텔리제이) Atom 세팅 (테마 변경) 및 단축키 (0) | 2020.09.08 |
Java (자바) CRUD 정리 3 static ★클래스 메소드, 인스턴스 메소드★ (0) | 2020.09.06 |
Java (자바) CRUD 정리 2 Insert/Update(객체데이터, fieldString, Generic, null체크, 숫자체크) (0) | 2020.09.03 |
Java (자바) CRUD 정리 1 Create Table(객체데이터, fieldString, Generic) (0) | 2020.09.02 |