지난번 정리한걸 가져와보면...

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
1
Inner Class
cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
    }
}
cs
1
2
3
4
5
6
7
8
9
class PreparedValue {
    boolean isString;
    Object value;
    
    PreparedValue(boolean isString, Object value) {
        this.isString = isString;
        this.value = value;                
    }
}
cs
int type 인스턴스로 클래스 내에서 미리 지정  Object로 받고 String만 구분한다
1
for each
cs
1
2
3
4
5
6
7
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()));
}
cs
1
2
3
4
5
if (fieldType.matches(".*String")) {
    preparedValue.add(new PreparedValue(true, field.get(t)));
else {
    preparedValue.add(new PreparedValue(false, field.get(t)));
}
cs
이너 클래스의 생성자에 맞게 타입을 지정해 객체 생성 String만 구분한다
1
Query
cs
String query = "INSERT INTO " + this.dbTableName + " (name, middleScore, finalScore) VALUES (?, ?, ?);"; String query = "UPDATE " + this.dbTableName + " SET name=?, middleScore=?, finalScore=? WHERE idx=?;";
쿼리 차이 비교하기 쿼리 차이 비교하기
1
PreparedStatement
cs
1
2
3
4
5
6
7
8
9
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);
    }
}
cs
1
2
3
4
5
6
7
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);
    }
}
cs
이미 형태까지 모두 지정되었기 때문에 이너클래스를 보고 인스턴스를 직관적으로 가져온다 String만 구분

 

 

+ Recent posts