///////
Search

별 찍기, java-db연동_곽철민

알고리즘

문제 1. 별 찍기

* ** *** **** ***** 의 모양이 출력되도록 코드를 작성하시오.
Java
복사
package lecture.lecture_1017; import java.util.Scanner; public class RightTriangle { public void getRightTriangle(int n){ for(int i =0;i<n;i++){ for(int j =0;j<=i;j++){ System.out.print("*"); } System.out.println(); } } public static void main(String[] args) { RightTriangle rt = new RightTriangle(); Scanner sc = new Scanner(System.in); System.out.printf("개수를 입력하세요 : "); int n = sc.nextInt(); rt.getRightTriangle(n); } }
Java
복사

문제 2. 생성자를 활용하여 다른 문자도 직각 삼각형의 모양으로 출력되도록 해보자.

package lecture.lecture_1017; import java.util.Scanner; public class RightTriangle { private String letter = "*"; public RightTriangle(){} public RightTriangle(String letter){ this.letter = letter; } public void getRightTriangle(int n){ for(int i =0;i<n;i++){ for(int j =0;j<=i;j++){ System.out.print(letter); } System.out.println(); } } public static void main(String[] args) { RightTriangle rt = new RightTriangle("#"); Scanner sc = new Scanner(System.in); System.out.printf("개수를 입력하세요 : "); int n = sc.nextInt(); rt.getRightTriangle(n); } }
Java
복사
위의 로직은 생성자로 문자를 초기화 하도록 하였습니다.
위 코드의 경우, “#” 를 객체 생성 시 전달해주어, #이 직각삼각형 모형으로 출력 되도록 하였습니다.
생성자 오버로딩
위 코드의 경우, 생성자는 기본 생성자와, String 타입의 인자를 받는 생성자까지 총 2가지 존재합니다.
생성자 오버로딩은, 매개변수의 타입과 개수가 다른 이름이 같은 생성자가 여러개 존재하는 것을 의미합니다.
생성자 오버로딩은 파라미터가 존재하는 생성자와 기본값일 떄를 모두 사용하기 위해 사용합니다.
따라서, 위의 코드는 생성자 오버로딩을 통해 “*”문자 뿐만 아니라, “#”, “!”등 원하는 문자를 출력할 수 있도록 String type의 문자열을 하나 받아 객체 생성 시 letter 값을 초기화 해주고 있음을 알 수 있습니다.

문제 3. 피라미드 별찍기

아래의 모양을 도출하는 코드를 작성하시오.

설계 문서 작성하기

1. 별의 입력은 홀수개만 입력 되도록 설계 i는 2k+1 일 경우에만 출력 (k=0,1,2,3….n) 2. 공백은 전체 길이 만큼 반복을 진행하며, 별이 출력되는 공간이 아닌 곳에 출력되도록 설계한다.
BASIC
복사
package lecture.lecture_1017; public class Pyramid { public void printPyramid(int n){ for(int i =0; i<n;i++){ //전체 길이 만큼 반복 for(int j=0;j<n-i;j++){ //공백 출력(전체 길이-i)만큼 계속 반복 System.out.print(" "); } for(int k=0; k<i*2+1; k++){ System.out.print("*"); } System.out.println(" "); } } public static void main(String[] args) { Pyramid p = new Pyramid(); p.printPyramid(5); } }
Java
복사
동작 원리

자바 11 이상 Repeat() 메소드

repeat()

자바 11에서 새로 추가된 String 메서드 문자열을 파라미터의 주어진 횟수만큼 반복
내부적으로 Arrays.fill() 및 System.arraycopy() 메서드를 호출하여 새로운 문자열을 생성해줍니다.

예제 코드

package lecture.lecture_1017; public class RepeatExample { public static void main(String[] args) { String str = "*"; System.out.println(str.repeat(50)); } }
Java
복사

실행 결과

Java로 DB 연동

mysql dependency 추가
가장 최신 버전인 8.0.30 버전을 클릭합니다.
우리 프로젝트는 Gradle로 Build 했으므로, Gradle을 클릭하여 mysql 관련 라이브러리 연동 코드를 복사하여 가져와 줍니다.

인텔리제이에 붙혀 넣기

build,gradle → dependencies 부분에 추가
plugins { id 'java' } group 'org.example' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' // https://mvnrepository.com/artifact/mysql/mysql-connector-java implementation group: 'mysql', name: 'mysql-connector-java', version: '8.0.30' } test { useJUnitPlatform() }
BASIC
복사
가져온 이후에는 위에 코끼리 비슷한 모양을 클릭하여 작성한 코드가 적용되도록 꼭 한번 build 해주어야 합니다.
이제 우리는 자바에서 데이터베이스를 사용할 수 있습니다.

User VO 생성하기

VO(Value Object)
값 오브젝트로, 값을 위해 쓰입니다.
read-Only 특징을 가집니다.
DTO와 비슷한 특성을 가지지만, DTO는 setter를 가지고 있어 값이 변할 수 있습니다.
Value Object는 관계 데이터 베이스의 레코드에 대응되는 자바 클래스 입니다.
형태는 db 레코드를 구성하는 필드들을 Value Object (Value 클래스)의 속성으로 설정 하고,
해당 변수에 접근할 수 있는 Getter, Setter 메소드의 조합으로 클래스가 형성 됩니다.
package org.example.domain; public class User { private String id; private String name; private String password; public User(String id, String name, String password) { this.id = id; this.name = name; this.password = password; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Java
복사
우리는 User 테이블을 생성하고, 이 값들을 활용할 것이므로 VO로서 User 클래스를 하나 생성해줍니다.
이 클래스는 id, password, name 필드를 가집니다.

데이터베이스에 INSERT 역할을 하는 add() 메소드 작성하기

우리는 그동안 데이터베이스에 데이터를 INSERT 하기 위해 sql 문 형식의 내용이 저장된 파일을 생성한 뒤, workbench에서 그 파일을 찾아와 데이터 삽입 작업을 수행했습니다.
그러나 우리는 이제, 자바 위에서 Insert 해줄 수 있습니다.
아래 코드에서는 add() 메소드가 insert() 작업을 수행합니다.
package org.example.dao; import org.example.FileManager; import org.example.domain.User; import org.example.parser.SqlParser; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Map; public class UserDao { private final String jdbcDriver = "com.mysql.cj.jdbc.Driver"; //보안을 위해 설정한 환경변수 값들을 map으로 가져오기 private Map<String, String> env = System.getenv(); private String dbHost = env.get("DB_HOST"); private String dbUser = env.get("DB_USER"); private String dbPassword = env.get("DB_PASSWORD"); public void add(){ try{ //jdbc 로드 Class.forName(jdbcDriver); //1. 커넥션 생성 Connection c = DriverManager.getConnection(dbHost, dbUser, dbPassword); //2. 쿼리 작성 PreparedStatement ps = c.prepareStatement("INSERT INTO users values(?,?,?)"); ps.setString(1, "2"); ps.setString(2, "김길동"); ps.setString(3, "password"); //3. 쿼리 실행 ps.executeUpdate(); //4. 자원 반납 ps.close(); c.close(); }catch (Exception e) { throw new RuntimeException(e); } } }
Java
복사
1. JDBC 로드
데이터베이스에 접속 하려면 해당 데이터베이스의 JDBC의 드라이버를 로드해야 합니다.
Class.forName(), 메소드를 호출하여 해당 클래스(데이터베이스 공급 업체에서 구현한 클래스)를 로드 해주는 방법을 사용합니다.
우리의 경우 mysql을 활용.
2. 데이터베이스 연결
드라이버가 로드되면 해당 데이터베이스의 JDBC 드라이버를 이용해 프로그램을 작성할 수 있는 상태가 된 것을 의미합니다.
실제 데이터베이스와 연결하려면 Connection 클래스의 인스턴스가 필요
우리는 DriverManager.getConnection()을 통해 이 인스턴스를 가져올 수 있습니다.
DriverManager. getConnection(JDBC_URL, “아이디”, “비밀번호”)
우리는, JDBC_URL 값과 데이터베이스 스키마의 아이디와 비밀번호를 환경 변수를 활용해 설정해주었습니다.
모든 코드 정보를 git에 올려주어야 하는데, db 관련 정보가 노출되면 해킹 될 우려가 있습니다.
그러므로, 노출되지 않도록, System.getenv()를 활용해줍니다.
이를 활용하여, jdbc_url과 아이디, 비밀번호 값들을 설정해줍니다.
3. Statement 생성
Statement
데이터베이스 연결로부터 SQL문을 수행할 수 있도록 해주는 클래스
executeQuery()
SELECT문을 수행할 때 사용
반환 값은 ResultSet 클래스의 인스턴스로, 해당 SELECT문의 결과에 해당하는 데이터에 접근할 수 있는 방법을 제공.
executeUpdate()
UPDATE, DELETE와 같은 문을 수행할 때 사용
반환 값은 int로, 처리된 데이터의 수를 반환.
우리 코드에서는 Connection 클래스의 prepareStatement() 메소드를 활용합니다.
PreparedStatement
SQL을 미리 만들어두고 변수를 따로 입력하는 방식
효용성이나 유지보수 측면에서 유리한 구조
Statement 클래스를 상속받기 때문에 Statement 클래스 메서드를 모두 사용
SQL문에서 변수가 와야할 위치에는 물음표만 적어두고, 물음표 자리에는 setXXX() 메소드로 값을 설정합니다.
예제 코드
PreparedStatement ps = c.prepareStatement("INSERT INTO users values(?,?,?)"); ps.setString(1, "2"); ps.setString(2, "김길동"); ps.setString(3, "password");
Java
복사
4. 사용한 자원 반납
PreparedStatement, Statement 모두 사용하지 않을 때는 닫아주어야 합니다.
close() 메소드를 통해 자원을 닫아줄 수 있습니다.

데이터의 조회 역할을 수행하는 Select 구현하기

package org.example.dao; import org.example.FileManager; import org.example.domain.User; import org.example.parser.SqlParser; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Map; public class UserDao { private final String jdbcDriver = "com.mysql.cj.jdbc.Driver"; //보안을 위해 설정한 환경변수 값들을 map으로 가져오기 private Map<String, String> env = System.getenv(); private String dbHost = env.get("DB_HOST"); private String dbUser = env.get("DB_USER"); private String dbPassword = env.get("DB_PASSWORD"); public User getUser(String id){ User user; try{ Class.forName(jdbcDriver); Connection c = DriverManager.getConnection(dbHost, dbUser, dbPassword); PreparedStatement pstmt = c.prepareStatement("select * from users where id = ?"); pstmt.setString(1, id); ResultSet rs = pstmt.executeQuery(); rs.next(); //하나 읽어오기 user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password")); rs.close(); pstmt.close(); c.close(); }catch (Exception e){ throw new RuntimeException(e); } return user; } }
Java
복사
JDBC 로드, 데이터베이스 연결(DriverManager.getConnection), PreparedStatement로 SQL문 작성하기의 과정은 위와 동일
select 즉 데이터 조회를 하려면, executeQuery()를 수행해주어야 합니다.
executeQuery()는 ResultSet 타입의 인스턴스를 반환합니다.
ResultSet
조회한 결과 값에 순차적으로 접근할 수 있는 커서를 다루게 합니다.
DB 내부적으로 수행된 SQL문의 처리 결과를 JDBC에서 쉽게 관리하도록 해줌.
커서를 최초 데이터 위치로 이동 시키기 위해 ResultSet이 반환하는 값을 활용하기 전에 rs.next() 메서드를 한 번 호출해준다.
읽어온 내용에서 getString() 메서드를 통해 로우에서 각 칼럼 값을 가져와줄 수 있다.

findAll() - 모든 유저 정보를 가져오기

package org.example.dao; import org.example.FileManager; import org.example.domain.User; import org.example.parser.SqlParser; import java.sql.*; import java.util.ArrayList; import java.util.List; import java.util.Map; public class UserDao { private final String jdbcDriver = "com.mysql.cj.jdbc.Driver"; //보안을 위해 설정한 환경변수 값들을 map으로 가져오기 private Map<String, String> env = System.getenv(); private String dbHost = env.get("DB_HOST"); private String dbUser = env.get("DB_USER"); private String dbPassword = env.get("DB_PASSWORD"); public List<User> findAll(){ List<User> userList = new ArrayList<>(); try { Class.forName(jdbcDriver); Connection c = DriverManager.getConnection(dbHost, dbUser, dbPassword); Statement statement = c.createStatement(); ResultSet rs = statement.executeQuery("select * from users"); while (rs.next()) { User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password")); userList.add(user); } rs.close(); statement.close(); c.close(); }catch(Exception e){ throw new RuntimeException(e); } return userList; } public static void main(String[] args) { UserDao userDao = new UserDao(); List<User> allofUser = userDao.findAll(); for (User user : allofUser) { System.out.println(user.getName()); } } }
Java
복사
JDBC 로드, 데이터베이스 연결, 쿼리문 생성은 위와 동일.
핵심은 while(rs.next()) 와 같이 로직을 작성하는 부분입니다.
이는, 더이상 로우(레코드)가 없을 때 까지 루프를 돌면서 각 로우의 컬럼 값들을 User의 id, name, password의 인자값으로 설정해주고, User 인스턴스를 생성해줍니다.
모든 유저 정보를 가져와주기 위해 List를 활용해줍니다.
while 루프를 돌면서 로우가 있는 동안, list에 생성된 User 타입의 인스턴스를 add() 메소드를 통해 추가해 줍니다.
메인 메소드에서 findAll() 메소드를 호출해 유저가 담긴 리스트를 얻고, for-each 문을 통해 모든 유저의 이름이 출력 되도록 하였습니다.