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

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만 구분

 

 

static을 사용하면 클래스이름.메소드이름으로 사용할 수 있다. 어디서든 사용 가능하다. 이를 클래스 메소드라 한다.

static을 사용하지 않으면 클래스를 하나의 객체로 인스턴스화 한 다음 객체.메소드이름으로 사용해야한다. 이를 인스턴스 메소드라 한다.

 

인스턴스 메소드 (static X)

이게 무슨 말이냐면, 아까 HomeController에 만들었던 isIntegerString 메소드를 DB 클래스로 옮기고, HomeController에서는 isIntegerString 메소드를 지워보자. 물론, private는 public으로 바꿔준다.

DB.java

public class DB<T> {
	// String이 숫자로 형변환이 가능한지 아닌지를 검사하는 메소드
	public boolean isIntegerString(String numericString) {
		try {
			int result = Integer.parseInt(numericString);
			return true;
		} catch (Exception e) {
			
		}
		return false;
	}
}

 

HomeController

static을 사용하지 않을 경우 HomeController에서 해당 메소드를 사용하기 위해서는 기존 방법과 같다.

@Controller
public class HomeController {
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {

		DB db = new DB<Student>(".../student.db", "student");
		db.isIntegerString(numericString);
		
	}
}

 

클래스 메소드 (static O)

DB.java

public class DB<T> {
	// String이 숫자로 형변환이 가능한지 아닌지를 검사하는 메소드
	public static boolean isIntegerString(String numericString) {
		try {
			int result = Integer.parseInt(numericString);
			return true;
		} catch (Exception e) {
			
		}
		return false;
	}
}

 

HomeController

@Controller
public class HomeController {
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {

		DB.isIntegerString(numericString);
		
	}
    // 여기서는 사용할 수 없다. 클래스 내부에 메소드를 만들고, 그 메소드 안에서 사용 가능하다.
}

이렇게 DB클래스에 있는 메소드에 직접 접근이 가능하다.

따라서, HomeController에 있는 insert 방법 2는 다음과 같이 바꾸면 된다.

	// 데이터 입력하기. if를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
		@RequestMapping(value = "/insert", method = RequestMethod.GET)
		public String insert(Locale locale, Model model, HttpServletRequest request) {
			try {
				request.setCharacterEncoding("UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			
			DB db = new DB<Student>(".../student.db", "student");
			
			if (db.open()) {
				if (request.getParameter("name") != null &&	// 파라미터 이름이 잘못 들어온 경우에 대한 null 체크
					request.getParameter("middleScore") != null &&
					// 클래스 메소드를 사용한 숫자 형변환 검사
					DB.isIntegerString(request.getParameter("middleScore")) &&	// 숫자로 형변환 가능한지를 체크 (파라미터 이름은 정상이나 숫자로 변환이 불가능하거나 빈 값에 대한 예외처리)
					request.getParameter("finalScore") != null &&
					// 인스턴스 메소드를 사용한 숫자 형변환 검사
					db.isIntegerString(request.getParameter("finalScore")) &&
					db.insertData(new Student(request.getParameter("name"),	// 파라미터 가져오기
											  Integer.parseInt(request.getParameter("middleScore")),
											  Integer.parseInt(request.getParameter("finalScore"))))) {
					model.addAttribute("message", "새 데이터를 추가했습니다.");
				} else {
					model.addAttribute("message", "데이터 추가에 실패했습니다.");
				};
				db.close();
			} else {
				model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
			}
			
			return "message";
		}

 

그리고 가독성을 높이기 위해 if문 조건 검사를 따로 분리해보자.

	// 데이터 입력하기. if를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		if (request.getParameter("name") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (request.getParameter("middleScore") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (!DB.isIntegerString(request.getParameter("middleScore"))) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (request.getParameter("finalScore") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (!DB.isIntegerString(request.getParameter("finalScore"))) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(request.getParameter("name"),	// 파라미터 가져오기
										  Integer.parseInt(request.getParameter("middleScore")),
										  Integer.parseInt(request.getParameter("finalScore"))))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

마찬가지 방식으로 방법 1도 다음과 같이 각각 분리시킬 수 있다.

	// 데이터 입력하기. try를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법. (기능별 분리)
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		// 변수 선언 및 초기화
		String name = null;
		int middleScore = 0;
		int finalScore = 0;
		
		// 데이터 유효성 검사
		try {
			name = request.getParameter("name");
			middleScore = Integer.parseInt(request.getParameter("middleScore"));
			finalScore = Integer.parseInt(request.getParameter("finalScore"));
		} catch (Exception e) {
			e.printStackTrace();
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		
		// DB에 쿼리 전송
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(name, middleScore, finalScore))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};

			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

 

 

 

 

 

위 방법을 정리해보면

방법 1

	// 데이터 입력하기. try를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		// DB에 쿼리 전송 및 데이터 유효성 검사
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			try {
				String name = request.getParameter("name");
				int middleScore = Integer.parseInt(request.getParameter("middleScore"));
				int finalScore = Integer.parseInt(request.getParameter("finalScore"));

				if (db.insertData(new Student(name, middleScore, finalScore))) {
					model.addAttribute("message", "새 데이터를 추가했습니다.");
				} else {
					model.addAttribute("message", "데이터 추가에 실패했습니다.");
				};
			} catch (Exception e) {
				e.printStackTrace();
				model.addAttribute("message", "데이터가 올바르지 않습니다.");
			}

			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

방법 1 (기능별 분리)

	// 데이터 입력하기. try를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법. (기능별 분리)
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		// 변수 선언 및 초기화
		String name = null;
		int middleScore = 0;
		int finalScore = 0;
		
		// 데이터 유효성 검사
		try {
			name = request.getParameter("name");
			middleScore = Integer.parseInt(request.getParameter("middleScore"));
			finalScore = Integer.parseInt(request.getParameter("finalScore"));
		} catch (Exception e) {
			e.printStackTrace();
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		
		// DB에 쿼리 전송
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(name, middleScore, finalScore))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};

			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

방법 2

	// 데이터 입력하기. if를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		// 데이터 유효성 검사
		if (request.getParameter("name") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (request.getParameter("middleScore") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (!DB.isIntegerString(request.getParameter("middleScore"))) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (request.getParameter("finalScore") == null) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		if (!DB.isIntegerString(request.getParameter("finalScore"))) {
			model.addAttribute("message", "데이터가 올바르지 않습니다.");
			return "message";
		}
		
		// DB에 쿼리 전송
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(request.getParameter("name"),	// 파라미터 가져오기
										  Integer.parseInt(request.getParameter("middleScore")),
										  Integer.parseInt(request.getParameter("finalScore"))))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

개인적으로 변수가 많지 않으면 방법 1, 변수가 많아지면 방법1 (기능별 분리)나 방법 2가 좋아보인다.

 

자바스크립트 수업을 했다.

구글차트를 하나만 써봤는데 원하는 차트로 하나 골라서 그리는 것을 하는데 기존에는 서버에서 데이터를 보내줬다면 이번엔 웹상에서 바로 선택한 데이터를 이용해 그리는 것을 했다.

그리고 자바 복스으으으으으읍

'개발자 > TIL' 카테고리의 다른 글

TIL 20.09.08 ~ 20.09.13  (0) 2020.09.13
TIL 20.09.04 ~ 20.09.07  (0) 2020.09.08
TIL 20.09.02  (0) 2020.09.03
TIL 20.09.01  (0) 2020.09.03
TIL 20.08.31  (0) 2020.08.31

자바자바

지난주에 이어 역시 자바를 복습을 하고 있다. 기본적인 것부터...

물론, 기존에는 제네릭을 거의 써본적이 없는데 이제는 제네릭을 써서 좀 더 일반적인 방법으로 하고 있다.

'개발자 > TIL' 카테고리의 다른 글

TIL 20.09.04 ~ 20.09.07  (0) 2020.09.08
TIL 20.09.03  (0) 2020.09.04
TIL 20.09.01  (0) 2020.09.03
TIL 20.08.31  (0) 2020.08.31
TIL 20.08.28  (0) 2020.08.28

SQL 짜는데 100줄 넘는걸 한 번에 다 짜고 있었는데... 이게 반도 못 짠거였다....

서브쿼리로 구간별로 나눠서 만들고 아래로 추가추가 하는 것을 알려주셨다.

음.. 확실히 나중에 점점 더 길어지면 이런식으로 모듈화 해버리는게 유지보수가 좋을 것 같다.

'개발자 > TIL' 카테고리의 다른 글

TIL 20.09.03  (0) 2020.09.04
TIL 20.09.02  (0) 2020.09.03
TIL 20.08.31  (0) 2020.08.31
TIL 20.08.28  (0) 2020.08.28
TIL 20.08.27  (0) 2020.08.27

1. 정리 1의 Create에 DB 생성과 실패에 따른 결과를 받아 테이블 생성/실패 처리 추가하기

HomeController

	// 쿼리를 이용해 테이블 생성하기.
	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
		DB db = new DB<Student>(".../student.db", "student");
//		DB db = new DB<Color>(".../color.db", "color");
//		DB db = new DB<Color>(".../memo.db", "memo");
		
		if (db.open()) {
			if (db.createTable(new Student())) {
//			if (db.createTable(new Color())) {
//			if (db.createTable(new Memo())) {
				model.addAttribute("message", "테이블이 생성되었습니다.");
			} else {
				model.addAttribute("message", "테이블 생성에 실패하였습니다.");
			}
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

DB

	// create table 쿼리 실행
	public boolean createTable(T t) {	// 기존에는 Student student라고 해서 Student라는 객체가 들어온다 명시를 했다. T는 어떤 객체가 들어올지 모른단 뜻. DB를 DB<T>로 Generic(제네릭)을 사용한다.
		Class<?> dataClass = t.getClass();	// t의 Class Object를 가져와 return해서 dataClass라는 변수명에 저장.
		Field[] dataClassFields = dataClass.getDeclaredFields();	// dataClass라는 클래스의 변수들을 array로 return.
		
		String fieldString = "";
		
		for (Field field: dataClassFields) {	//  array로부터 for each 문을 돌면서 하나씩 분해해 쿼리에 넣을 문자열을 만든다.
			if (!fieldString.isEmpty()) {
				fieldString = fieldString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			fieldString = fieldString + fieldName;
			if (fieldName.matches("idx")) {
				fieldString = fieldString + " INTEGER PRIMARY KEY AUTOINCREMENT";
			} else if (fieldType.matches("(int|long)")) {
				fieldString = fieldString + " INTEGER";
			} else if (fieldType.matches("(float|double)")) {
				fieldString = fieldString + " REAL";
			} else if (fieldType.matches(".*String")) {
				fieldString = fieldString + " TEXT";
			}
		}
		
		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

 

 

2. 이번에는 Insert를 만들자.

HomeController

	// 데이터 입력하기.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model,
			@RequestParam("name") String name,
			@RequestParam("middleScore") int middleScore,
			@RequestParam("finalScore") int finalScore) {
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(name, middleScore, finalScore))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

DB

	// insert 쿼리 실행
	public boolean insertData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();
		
		String fieldString = "";
		String valueString = "";
		
		for (Field field: dataClassFields) {
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			if (fieldName.matches("idx")) {
				continue;
			}
			if (!fieldString.isEmpty()) {
				fieldString = fieldString + ",";
			}
			if (!valueString.isEmpty()) {
				valueString = valueString + ",";
			}
			fieldString = fieldString + fieldName;
			
			// DB 컬럼에 넣을 값. 값이 문자열이면 ' ' 로 감싸주고, 그렇지 않으면 그냥 입력.
			try {
				if (fieldType.matches(".*String")) {	// 문자열이면 ' '로 감싸준다.
					valueString = valueString + "'" + field.get(t) + "'";
				} else {								// 아니면 그냥 넣는다.
					valueString = valueString + field.get(t);
				}
			} 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 {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

 

URL에 쿼리를 보내서 입력해보자.

http://localhost:8099/student/insert?name=홍길동&middleScore=100&finalScore=80
http://localhost:8099/student/insert?name=장보고&middleScore=70&finalScore=85
http://localhost:8099/student/insert?name=고길동&middleScore=90&finalScore=55

 

 

3. Update 만들기

HomeController

	// 데이터 수정하기.
	@RequestMapping(value = "/update", method = RequestMethod.GET)
	public String insert(Locale locale, Model model,
			@RequestParam("idx") int idx,
			@RequestParam("name") String name,
			@RequestParam("middleScore") int middleScore,
			@RequestParam("finalScore") int finalScore) {
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(idx, name, middleScore, finalScore))) {
				model.addAttribute("message", "데이터를 수정했습니다.");
			} else {
				model.addAttribute("message", "데이터 수정에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

DB

	// update 쿼리 실행
	public boolean updateData(T t) {
		Class<?> dataClass = t.getClass();
		Field[] dataClassFields = dataClass.getDeclaredFields();
		String setString = "";
		String whereString = "";
		for (Field field : dataClassFields) {
			if (!setString.isEmpty()) {
				setString = setString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			try {
				if (fieldName.matches("idx")) {
					whereString = "idx=" + field.get(t);
				} else if (fieldType.matches(".*String")) {
					setString = setString + fieldName + "=" + "'" + field.get(t) + "'";
				} else {
					setString = setString + fieldName + "=" + field.get(t);
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		String query = "UPDATE " + this.tableName + " SET " + setString + " WHERE " + whereString;
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

 

URL에  쿼리를 보내서 수정해보자.

http://localhost:8099/student/update?idx=1&name=홍길동&middleScore=100&finalScore=100

 

 

4. UTF-8 설정하기 & null 체크하기 & 숫자(numeric) 체크하기

STS 4 Spring Boot에서는 설정 안 해도 자동으로 UTF-8로 되지만 STS 3도 있으니까...

그리고 파라미터가 이름이 잘못 들어오거나 할 경우 400 에러가 뜨는데, 이런 것들이 보안 취약점이 되기 때문에 예외처리를 해준다.

UTF-8 처리

	// 데이터 입력하기.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		String name = request.getParameter("name");
		int middleScore = Integer.parseInt(request.getParameter("middleScore"));
		int finalScore = Integer.parseInt(request.getParameter("finalScore"));
		
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (db.insertData(new Student(name, middleScore, finalScore))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

null 체크하기 (방법 1) try~catch & 숫자(numeric) 체크하기

	// 데이터 입력하기. try를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
						
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			try {
				String name = request.getParameter("name");
				int middleScore = Integer.parseInt(request.getParameter("middleScore"));
				int finalScore = Integer.parseInt(request.getParameter("finalScore"));

				if (db.insertData(new Student(name, middleScore, finalScore))) {
					model.addAttribute("message", "새 데이터를 추가했습니다.");
				} else {
					model.addAttribute("message", "데이터 추가에 실패했습니다.");
				};
			} catch (Exception e) {
				model.addAttribute("message", "데이터가 올바르지 않습니다.");
			}

			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

테스트용 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=	// 파라미터 이름은 정상이나 숫자가 들어와야 하는 곳에 빈 값이 들어온 경우에 대한 예외처리

 

null 체크하기 (방법 2) if & 숫자(numeric) 체크하기

	// 데이터 입력하기. if를 사용한 잘못된 파라미터 체크 & 문자열 숫자 형변환 가능 여부 검사하는 방법.
	@RequestMapping(value = "/insert", method = RequestMethod.GET)
	public String insert(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			if (request.getParameter("name") != null &&	// 파라미터 이름이 잘못 들어온 경우에 대한 null 체크
				request.getParameter("middleScore") != null &&
				isIntegerString(request.getParameter("middleScore")) &&	// 숫자로 형변환 가능한지를 체크 (파라미터 이름은 정상이나 숫자로 변환이 불가능하거나 빈 값에 대한 예외처리)
				request.getParameter("finalScore") != null &&
				isIntegerString(request.getParameter("finalScore")) &&
				db.insertData(new Student(request.getParameter("name"),	// 파라미터 가져오기
										  Integer.parseInt(request.getParameter("middleScore")),
										  Integer.parseInt(request.getParameter("finalScore"))))) {
				model.addAttribute("message", "새 데이터를 추가했습니다.");
			} else {
				model.addAttribute("message", "데이터 추가에 실패했습니다.");
			};
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

파라미터로 들어온 문자열이 숫자로 형변환 가능한지 아닌지를 검사하는 메소드를 만든다.

'java numeric string check' 등으로 검색한다.

	// String이 숫자로 형변환이 가능한지 아닌지를 검사하는 메소드
	private boolean isIntegerString(String numericString) {
		try {
			int result = Integer.parseInt(numericString);
			return true;
		} catch (Exception e) {
			
		}
		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=	// 파라미터 이름은 정상이나 숫자가 들어와야 하는 곳에 빈 값이 들어온 경우에 대한 예외처리

문자와 달리 숫자의 경우 형변환이 가능한지를 확인하는 것으로 숫자로 가능한 문자열인지에 대한 체크와 동시에 파라미터 이름은 정상이나 빈 값으로 들어오는 경우에 대한 예외처리를 동시에 할 수 있다.

발전 과제 : http://localhost:8099/student/insert?name=&middleScore=20&finalScore=20 // 파라미터 이름은 정상이나 문자가 들어와야 하는 곳에 빈 값이 들어온 경우에 대한 예외처리를 방법 1과 2 모두 처리하지 못 했다. replaceAll을 이용하면 처리가 되지만 이 경우 문자열 사이의 공백도 제거해버리기 때문에 사용할 수 없다. trim()으로 해보았으나 아예 "   " 인 경우는 trim이 작동하지 않고 빈 문자열로 그냥 통과해버렸다.

 

update도 마찬가지로 바꿔주자. 위 방법 1을 사용하였으며, 변수를 생성하는 대신 바로 Student에 넣었다.

	// 데이터 수정하기.
	@RequestMapping(value = "/update", method = RequestMethod.GET)
	public String update(Locale locale, Model model, HttpServletRequest request) {
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		DB db = new DB<Student>(".../student.db", "student");
		
		if (db.open()) {
			try {
				if (db.updateData(new Student(Integer.parseInt(request.getParameter("idx")),
											  request.getParameter("name"),
											  Integer.parseInt(request.getParameter("middleScore")),
											  Integer.parseInt(request.getParameter("finalScore"))))) {
					model.addAttribute("message", "새 데이터를 수정했습니다.");
				} else {
					model.addAttribute("message", "데이터 수정에 실패했습니다.");
				};
			} catch (Exception e) {
				e.printStackTrace();
				model.addAttribute("message", "데이터가 올바르지 않습니다.");
			}

			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

테스트용 URL

http://localhost:8099/student/update?idx=19&name=공룡&middleScore=10&finalScore=13

idx에 들어가는 숫자는 DB를 보고 조절한다.

발전 과제 : idx가 존재하지 않는 값을 넣으면... 쿼리는 정상적으로 보내지기 때문에 데이터 수정에 실패했다고 뜨지 않고 수정했다고 뜬다...

 

 

 

방법 1과 방법 2를 기능별 분류하는 등 다양한 표현 코드에 대해서는 정리 3편에서 이어서 한다.

2020/09/06 - [개발자/Java] - Java (자바) CRUD 정리 3 static

 

홈 사용은 안 할건데 기본으로 되어있을 때 한글이 깨지니 그냥 보기 싫으면 이거 추가하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

 

message.jsp 파일 만들기

그냥 결과만 출력할거다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Message</title>
  </head>
  <body>
    <P>${message}</P>
  </body>
</html>

 

DB 클래스 파일 만들기

외부에 있는 DB 서버의 데이터를 사용하기 위함

기존

import java.sql.Connection;

public class DB {
	private String dbFileName;
	private String tableName;
	private Connection connection;
	static {
		try {	// 이 이름을 가진 DB가 없을 수도 있기 때문에 try~catch
			Class.forName("org.sqlite.JDBC");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public DB (String dbFileName, String tableName) {
		this.dbFileName = dbFileName;
		this.tableName = tableName;
	}
	
	public boolean open() {
		SQLiteConfig config = new SQLiteConfig();
		try {
			this.connection = DriverManager.getConnection("jdbc:sqlite:/" + this.dbFileName, config.toProperties());
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void close() {
		if (this.connection != null) {
			try {
				this.connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

 

신규 (generic 사용)

import java.sql.Connection;

public class DB<T> {
	private String dbFileName;
	private String tableName;
	private Connection connection;
	static {
		try {	// 이 이름을 가진 DB가 없을 수도 있기 때문에 try~catch
			Class.forName("org.sqlite.JDBC");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public DB (String dbFileName, String tableName) {
		this.dbFileName = dbFileName;
		this.tableName = tableName;
	}
	
	public boolean open() {
		SQLiteConfig config = new SQLiteConfig();
		try {
			this.connection = DriverManager.getConnection("jdbc:sqlite:/" + this.dbFileName, config.toProperties());
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void close() {
		if (this.connection != null) {
			try {
				this.connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

 

 

Create table 만들기

Student.java 객체 클래스를 만들어준다.

package com.kopo.student;

public class Student {
	int idx;
	String name;
	int middleScore;
	int finalScore;
	
	Student() {
		
	}
	
	public Student(String name, int middleScore, int finalScore) {
		this.name = name;
		this.middleScore = middleScore;
		this.finalScore = finalScore;		
	}
	
	public Student(int idx, String name, int middleScore, int finalScore) {
		this.idx = idx;
		this.name = name;
		this.middleScore = middleScore;
		this.finalScore = finalScore;		
	}
}

 

기존
HomeController

	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
		DB db = new DB("경로 및 DB 파일 이름", "student");
		
		if (db.open()) {
			db.createTable();
			model.addAttribute("message", "create success");
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

DB

	public void createTable() {
		String query = "CREATE TABLE " + this.tableName + " (idx INT PRIMARY KEY, name TEXT, middleScore INT, finalScore INT)";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

 

신규 (객체데이터 사용 & fieldString 사용)
HomeController

	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
		DB db = new DB<Student>("경로 및 DB 파일 이름", "student");
		
		if (db.open()) {
			db.createTable(new Student());
			model.addAttribute("message", "create success");
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

DB

	public void createTable() {
		String fieldString = "";
		fieldString = fieldString + "idx INT PRIMARY KEY";
		fieldString = fieldString + ", name TEXT";
		fieldString = fieldString + ", middleScore INT";
		fieldString = fieldString + ", finalScore INT";
		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

위 방법은 객체데이터가 Student일 때만 사용할 수 있다. 거기에 맞춰서 DB query를 만들었기 때문이다.

 

신규 (generic 사용)
HomeController

위와 동일

DB

	public void createTable(T t) {	// 기존에는 Student student라고 해서 Student라는 객체가 들어온다 명시를 했다. T는 어떤 객체가 들어올지 모른단 뜻. DB를 DB<T>로 Generic(제네릭)을 사용한다.
		Class<?> dataClass = t.getClass();	// t의 Class Object를 가져와 return해서 dataClass라는 변수명에 저장.
		Field[] dataClassFields = dataClass.getDeclaredFields();	// dataClass라는 클래스의 변수들을 array로 return.
		
		String fieldString = "";
		
		for (Field field: dataClassFields) {	//  array로부터 for each 문을 돌면서 하나씩 분해해 쿼리에 넣을 문자열을 만든다.
			if (!fieldString.isEmpty()) {	// 반복문 처음에는 fieldString이 비어 있기 때문에 콤마를 찍지 않는다. 2번째 반복문 부터 시작할 때 콤마를 찍는다. 따라서 마지막 반복문 때는 앞에만 콤마가 찍히고 뒤에는 콤마가 찍히지 않는다.
				fieldString = fieldString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			fieldString = fieldString + fieldName;
			if (fieldName.matches("idx")) {
				fieldString = fieldString + " INTEGER PRIMARY KEY AUTOINCREMENT";
			} else if (fieldType.matches("(int|long)")) {
				fieldString = fieldString + " INTEGER";
			} else if (fieldType.matches("(float|double)")) {
				fieldString = fieldString + " REAL";
			} else if (fieldType.matches(".*String")) {
				fieldString = fieldString + " TEXT";
			}
		}
		
		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	

generic을 사용(여기서는 T)해서 어떤 객체 데이터가 들어오든 만능으로 돌아가도록 반응형으로 만든다!

위와 같은 방법은 'java get class properties' 등으로 검색하면 된다.

 

 

여기까지 정리하면

HomeController

package com.kopo.student;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

@Controller
public class HomeController {
	private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

	// 쿼리를 이용해 테이블 생성하기.
	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
		DB db = new DB<Student>("경로 및 DB 파일 이름", "student");
		
		if (db.open()) {
			db.createTable(new Student());
			model.addAttribute("message", "create success");
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}	
}

 

DB

package com.kopo.student;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import org.sqlite.SQLiteConfig;

public class DB<T> {
	private String dbFileName;
	private String tableName;
	private Connection connection;
	static {
		try {	// 이 이름을 가진 DB가 없을 수도 있기 때문에 try~catch
			Class.forName("org.sqlite.JDBC");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public DB (String dbFileName, String tableName) {
		this.dbFileName = dbFileName;
		this.tableName = tableName;
	}
	
	public boolean open() {
		SQLiteConfig config = new SQLiteConfig();
		try {
			this.connection = DriverManager.getConnection("jdbc:sqlite:/" + this.dbFileName, config.toProperties());
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public void close() {
		if (this.connection != null) {
			try {
				this.connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	public void createTable(T t) {	// 기존에는 Student student라고 해서 Student라는 객체가 들어온다 명시를 했다. T는 어떤 객체가 들어올지 모른단 뜻. DB를 DB<T>로 Generic(제네릭)을 사용한다.
		Class<?> dataClass = t.getClass();	// t의 Class Object를 가져와 return해서 dataClass라는 변수명에 저장.
		Field[] dataClassFields = dataClass.getDeclaredFields();	// dataClass라는 클래스의 변수들을 array로 return.
		
		String fieldString = "";
		
		for (Field field: dataClassFields) {	//  array로부터 for each 문을 돌면서 하나씩 분해해 쿼리에 넣을 문자열을 만든다.
			if (!fieldString.isEmpty()) {
				fieldString = fieldString + ",";
			}
			String fieldName = field.getName();
			String fieldType = field.getType().toString();
			fieldString = fieldString + fieldName;
			if (fieldName.matches("idx")) {
				fieldString = fieldString + " INTEGER PRIMARY KEY AUTOINCREMENT";
			} else if (fieldType.matches("(int|long)")) {
				fieldString = fieldString + " INTEGER";
			} else if (fieldType.matches("(float|double)")) {
				fieldString = fieldString + " REAL";
			} else if (fieldType.matches(".*String")) {
				fieldString = fieldString + " TEXT";
			}
		}
		
		String query = "CREATE TABLE " + this.tableName + " (" + fieldString + ")";
		try {
			Statement statement = this.connection.createStatement();
			statement.executeUpdate(query);
			statement.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

 

message.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Message</title>
  </head>
  <body>
    <P>${message}</P>
  </body>
</html>

 

 

이번에는 위 코드를 활용해 Color 객체데이터랑 Memo 객체데이터를 활용해 DB를 만들어보자.

Color.java

package com.kopo.student;

public class Color {
	int idx;
	String name;
	String code;
	
	Color() {
		
	}
	
	public Color(String name, String code) {
		this.name = name;
		this.code = code;
	}
	
	public Color(int idx, String name, String code) {
		this.idx = idx;
		this.name = name;
		this.code = code;
	}
}

 

HomeController

	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
//		DB db = new DB<Student>(".../student.db", "student");
		DB db = new DB<Color>(".../color.db", "color");	// 여기를 바꿔준다. (DB 파일과 테이블)
		
		if (db.open()) {
//			db.createTable(new Student());
			db.createTable(new Color());	// 여기를 바꿔준다. (createTable에 보낼 객체 데이터)
			model.addAttribute("message", "create success");
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

Memo도 동일하다.

package com.kopo.student;

public class Memo {
	int idx;
	String memo;
	
	Memo() {
		
	}
	
	Memo (String memo) {
		this.memo = memo;
	}
	
	Memo (int idx, String memo) {
		this.idx = idx;
		this.memo = memo;
	}
}

 

HomeController

	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public String create(Locale locale, Model model) {
//		DB db = new DB<Student>(".../student.db", "student");
//		DB db = new DB<Color>(".../color.db", "color");
		DB db = new DB<Color>(".../memo.db", "memo");
		
		if (db.open()) {
//			db.createTable(new Student());
//			db.createTable(new Color());
			db.createTable(new Memo());
			model.addAttribute("message", "create success");
			db.close();
		} else {
			model.addAttribute("message", "DB파일을 사용할 수 없습니다.");
		}
		
		return "message";
	}

 

 

 

포트폴리오 계획을 제출했다... 사실... 내 능력을 뛰어 넘는거라 다 못 할 것 같기는 한데

자바 스프링 다양하게 활용하고  CRUD에 파일업로드, DB 연동을 많이 할 수 있을 것 같아서 되는데까지 해 볼 생각으로 결정했다...

그리고 알고리즘... 어렵다... 왜 이해가 안 될까... ㅠㅠ

'개발자 > TIL' 카테고리의 다른 글

TIL 20.09.02  (0) 2020.09.03
TIL 20.09.01  (0) 2020.09.03
TIL 20.08.28  (0) 2020.08.28
TIL 20.08.27  (0) 2020.08.27
TIL 20.08.26  (0) 2020.08.27

+ Recent posts