ioc 작성 x
jdbc
jdbc #ex1
Exam0110 -> MariaDB java Client 설치 및 Driver 사용법
// 1. mvnrepository.com - MariaDB Java Client 검색 - 버전 선택 -스크립트 복사
// 2. search.maven.org - search 창에 mariadb jdbc 검색 - mariadb jdbc client 클릭 - 버전 선택
// - 오른쪽 Gradle Groovy DSL - 스크립트 복사
// build.gradle에 붙혀넣기 - powershell 해당 경로 - gradle cleanEclipse - gradle eclipse
// 이클립스 refresh // 프로젝트에 mariadb jdbc driver 라이브러리가 추가되었는지 확인
// JDBC 드라이버 로딩 방법1 // java.sql.Driver 규칙에 따라 정의된 클래스를 로딩한다.
// Driver 구현체 // JDBC의 정보를 알려준다. // DBMS에 연결작업을 수행 //
// Driver 구현체를 생성하여 DriverManager에게 등록해 사용한다. // DriverManager가 Driver 구현체 관리.
// DBMS 연결 요청이 들어오면 해당 DBMS의 Driver 구현체를 찾아 작업을 위임한다.
// java.sql.Driver mariadbDriver = new org.mariadb.jdbc.Driver(); // Driver 구현체의 인스턴스 생성
// DriverManager.registerDriver(mariadbDriver); // Driver 인스턴스를 드라이버 관리자에 등록
// java.sql.Driver driver = DriverManager.getDriver("jdbc:mariadb:");
// DriverManager.getDriver("jdbcUrl"); // DriverManager에 등록된 Driver 인스턴스 확인
// jdbcUrl // jdbc:[DBMS]://서버주소.포트번호/데이터베이스명
Exam0111 -> Driver 예외 발생 // 등록되지 않은 드라이버를 찾을 경우 // No suiterable // 에러 발생
Exam0120 -> JDBC 드라이버 로딩 방법2 // Driver 객체를 생성하면 자동으로 DriverManager에 등록한다.
// new org.mariadb.jdbc.Driver(); // Driver구현체가 로딩될 때 인스턴스가 생성된다.
// DriverManager.registerDriver(mariadbDriver) // 인스턴스 생성 및 register를 할 필요없다.
Exam0130 -> JDBC 드라이버 로딩 방법3 // Driver 구현 클래스 로딩과 자동 등록
// java.sql.Driver 인터페이스를 구현한 클래스를 로딩하면, 자동으로 DriverManager에게 등록
// Class.forName("org.mariadb.jdbc.Driver");
// Class.forName("fully-qualified class name(패키지명을 포함한 클래스명)")
// 장점 // 클래스를 지정하는 대신 클래스 이름을 지정하기 때문에 다른 클래스로 쉽게 바꿀 수 있다.
// 따라서 특정한 Driver 구현체에 종속되지 않게 만들 수 있다.
// 해당 Class에서 static블록으로 생성하기 때문에 한번만 실행된다.
// DriverManager에 Driver 인스턴스는 하나만 등록된다.
Exam0140 -> JDBC 드라이버 로딩 방법4 // Driver 구현체 자동 로딩
// DriverManager 로딩 절차
// 1. jdbc.drivers 시스템 프로퍼티에 지정된 구현체를 찾아 로딩
// jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver
// Driver 구현체는 'system class loader'를 통해 로딩된다.
// 시스템 프로퍼티 설정 방법
// 1) eclipse 실행표시 오른쪽 아래화살표 클릭 - Run Configurations - arguments - VM agruments -
// -Djdbc.drivers=com.eomcs.jdbc.ex1.MyDriver 입력(MyDriver 미리 설정) - run // 자동 부팅 된 것을 확인
// 2) 프로그램 코드에서 설정 // System.setProperty("jdbc.drivers", "com.eomcs.jdbc.ex1.MyDriver");
Exam0141 -> DriverManager 로딩 절차2
// jar 파일 안에 META-INF/services/java.sql.Driver 파일을 찾는다.
// 'service-provider loading' 절차에 따라 이 파일에 등록된 클래스를 로딩
// jar 파일에 해당 정보가 있다면, 따로 java.sql.Driver 구현체를 명시적으로 등록하지 않아도 된다.
// mariadb JDBC 드라이버 jar 파일은 이 정보가 들어 있다. // jar파일 등록하는 방법?
// Exam0110에서 build.gradle에 넣어 실행할때 생기는 그 것이다.
// java.sql.Driver driver = DriverManager.getDriver("jdbc:mariadb:"); // 결론 이렇게 작성하면 자동 등록된다.
Exam0210 -> DBMS에 연결하기
// java.sql.Connection con = null;
// con = DriverManager.getConnection("jdbc:mysql://localhost:3306/studydb", "study", "1111");
// "jdbc:mysql://서버주소:포트/데이터베이스명", "DBMS 사용자 아이디", "DBMS 사용자 암호"
// Driver 구현체는 DBMS와 연결한 후 소켓 정보를 갖고 있는 java.sql.Connection 구현체를 리턴한다.
Exam0220 -> try-with-resources
Exam0310 -> Statement 객체
// java.sql.Connection con = DriverManager.getConnection(
// "jdbc:mysql://localhost:3306/studydb?ser=study&password=1111");
// java.sql.Statement stmt = con.createStatement(); // java.sql.Statement 구현 객체
// SQL문을 DBMS의 형식에 따라 인코딩하여 서버에 전달하는 일을 하는 객체
// Statement 객체 사용법
// 1. DML(Data Manipulation Language) // INSERT/UPDATE/DELETE 등 DML 관련 SQL문 전송
// executeUpdate() // 리턴값 // 변경(insert/update/delete)된 데이터의 개수
// 2. DQL(Data Query Language) // SELECT 등 DQL 관련 SQL문 전송
// executeQuery() // 리턴값 // 서버에서 데이터를 가져오는 일을 할 객체
Exam0320 -> SQL에서 데이터를 JAVA로 가져오는 방법.
// java.sql.ResultSet rs = stmt.executeQuery("select * from x_board order by board_id desc");
// ResultSet 객체 // 서버에서 결과를 가져오는 일을 할 객체
// ResultSet 객체를 사용하여 서버에서 select의 결과 한 레코드(row)를 가져온다.
// boolean isReceived = rs.next(); // 레코드(Recode) // select를 실행한 후 생성된 결과 //
// 가져왔으면 true, 가져올 게 없다면 false
// int, number: getInt() // char, varchar, text: getString() // date, datetime: getDate() // float: getFloat()
// getXxx(컬럼번호) // select 문에 나열한 컬럼의 순서를 지정한다. // 번호는 0부터가 아니라 1부터이다.
// rs.getInt(1), rs.getString(2), rs.getString(3), // 컬럼의 번호를 지정 것은 코드를 읽을 때 불편하다.
Exam0330 -> DBMS에 SQL문 보내기
// getXxx(컬럼명) // rs.getInt("board_id"), rs.getString("title"), rs.getString("contents"),
Exam0340 -> 반복문 사용
Exam0341 -> getString() // 컬럼의 타입과 상관없이 getString()으로 값을 꺼낼 수 있다.
Exam0350 -> 데이터 업데이트 executeUpdate() // ""안에 update를 넣는다.
// DBMS 서버에 update 문을 보낸다. // 리턴 값: 변경된 레코드의 개수
// stmt.executeUpdate( "update x_board set view_count = view_count + 20" + " where board_id > 1");
Exam0360 -> 데이터 삭제 executeUpdate() // ""안에 delete를 넣는다.
// int count = stmt.executeUpdate( "delete from x_board where board_id > 5");
jdbc #ex2
Exam0110 -> list.add() // ""안에 insert 넣는다.
// String sql = String.format( "insert into x_board(title,contents) values('%s','%s')", title, contents);
// int count = stmt.executeUpdate(sql);
Exam0120 -> list.list() jdbc #ex1 Exam0320, Exam0330과 같다.
Exam0130 -> list.detail() // ResultSet rs = stmt.executeQuery( "select * from x_board where board_id = " + no))
Exam0140 -> list.update()
Exam0150 -> list.delete()
Exam0210 -> SQL 삽입 공격
// 입력 문자열에 SQL 명령을 삽입하여 프로그램의 의도와 다르게 데이터를 조작하는 행위.
Exam0220 -> SQL 삽입 공격 대비
// PreparedStatement는 미리 SQL 문장을 준비하여 값을 삽입하는 기법이다.
// SQL 문장을 준비할 때, 값이 들어 갈 자리에 ? 로 표시한다. // ? 를 "in-parameter"라 부른다.
// SQL을 서버에 전달하기 전에 in-parameter 자리에 값을 넣는다.
// PreparedStatement.setXxx(in-parameter 인덱스, 값);
PreparedStatement stmt = con.prepareStatement("update x_board set title = ?, contents = ? where board_id = ?")) {
// stmt.setString(1, title); stmt.setString(2, contents); stmt.setString(3, no);
// int count = stmt.executeUpdate(); // ?에 들어갈 값을 다 채우고 stmt.executeUpdate()를 해야한다.
// in-parameter 인덱스 // ? 문자가 등장하는 순서대로 1부터 번호를 부여한다.
// 작은 따옴표를 그냥 일반 문자로 취급 // 실무에서는 거의 무조건 PreparedStatement만 사용한다.
// Statement vs PreparedStatement
// PreparedStatement가 SQL 문장과 값이 분리되어 있기 때문에 작성하거나 읽기 쉽다
// PreparedStatement는 SQL 문장과 값이 분리되어 다뤄지기 때문에 해킹할 수 없다. // SQL 삽입 공격 대비
// PreparedStatement는 setXxx() 메서드를 호출하여 값을 설정하기 때문에 바이트 배열의 값을 다룰 수 있다.
// PreparedStatement가 executeUpdate() 호출시 SQL 문법을 분석하지 않아, 반복 실행할 때 Statement 보다 빠르다.
Exam0310 -> list.add() // preparedStatment 형식으로 변경.
// PreparedStatement stmt = con.prepareStatement("insert into x_board(title,contents) values(?,?)");)
// PreparedStatement 으로 선언하는데, prepareStatement // 메서드는 d가 안들어간다.
Exam0320 -> list.list() // preparedStatment 형식으로 변경.
Exam0330 -> list.detail() // preparedStatment 형식으로 변경.
Exam0340 -> list.update() // preparedStatment 형식으로 변경.
Exam0350 -> list.delete() // preparedStatment 형식으로 변경.
Exam0410 -> auto increment PK값 리턴 받기 // PreparedStatement 객체를 얻을 때 옵션을 지정한다.
// PreparedStatement stmt = con.prepareStatement("insert into x_board(title,contents)
// values(?,?)", Statement.RETURN_GENERATED_KEYS);)
// prepareStatement(sql문, 자동 생성된 PK 값 리턴 여부) // Statement.RETURN_GENERATED_KEYS
// ResultSet rs = stmt.getGeneratedKeys() // insert 수행 후 자동 생성된 PK 값은 따로 요구
// no = rs.getInt(1); // 자동 생성된 PK 값을 꺼낼 때는 따로 컬럼 이름이 없기 때문에 컬럼 인덱스로 꺼낸다.
Exam0420 -> auto increment PK값 리턴 받기 반복문 사용
Exam0510 -> 트랜잭션 // autocommit = true // autocommit일 경우 오류의 경우에도 commit을 해버린다.
// 원하는데로 컨트롤 할 수 없다. // autocommit을 false로 하고 사용하자.
Exam0520 -> 트랜잭션 다루기 // 여러개의 데이터 변경 작업을 한 단위로 묶은 것. // commit & rollback
// con.setAutoCommit(false); // 커넥션 객체의 오토커밋을 false로 지정한다 // DBMS의 임시 테이블에 보관
// 이후부터 con으로 실행하는 모든 SQL은 commit을 요청하기 전까지 table에 결과를 적용하지 않는다.
// con.commit(); // 필요한 모든 작업을 수행하고 // 서버의 요청한 작업을 처리할 것을 명령
// commit()을 호출하지 않으면 서버에 요청한 데이터 변경 작업은 자동 취소
// con.rollback(); // 서버의 요청한 작업을 취소할 것을 명령함. // 명시적으로 작업을 취소하는 것.
// rollback()을 호출하지 않고,
// 커넥션을 공유하는 상황에서는 이렇게 명시적으로 작업 취소를 명령하는 것이 좋다.
jdbc #ex3
Exam0110 -> list.add() DBMS 작업 분리 // BoardDao생성 // DBMS 작업 전담하게 작업.
// DBMS 작업을 캡슐화하면, 코드가 더욱 직관적으로 바뀐다.
Exam0120 -> list.list() DBMS 작업 분리
Exam0130 -> list.detail() DBMS 작업 분리
Exam0140 -> list.update() DBMS 작업 분리
Exam0150 -> list.delete() DBMS 작업 분리
mybatis
mybatis #ex01
Exam0110 -> Data Persistence Framework // 데이터의 영속성(등록,조회,변경,삭제)을 대신 처리해주는 프레임워크
// SQL Mapper // 직접 SQL 문을 작성 // 각각의 DBMS에 최적화된 SQL을 작성할 수 있다.
// DBMS마다 미미하게 다른 SQL을 작성해야 하는 번거로움이 있다. // 예) Mybatis 등
// OR Mapper // 전용언어 및 문법(Domain-Specific Language;DSL)을 사용하여 작성
// 실행할 때 DBMS에 맞춰서 SQL을 생성하여 실행한다.
// DBMS마다 SQL문을 작성할 필요가 없어 편리하다. // DBMS에 최적화된 SQL을 실행할 수 없다.
// 즉 DBMS의 특징을 최대로 활용할 수 없다. // 예) Hibernate, TopLink 등
// 1. 의존 라이브러리 추가
// Mybatis 도입 // search.maven.org - mybatis검색 - 오른쪽 Gradle Groovy DSL 버전 스크립트 복사 -
// build.gradle 에 추가 - gradle cleanEclipse - gradle eclipse - 라이브러리 추가 확인
// 2. mybatis 설정 파일 준비 // mybatis-config.xml 생성
// https://blog.mybatis.org/ - 오른쪽 MyBatis for Java에 Github Project - mybatis-3 -
// 스크롤 맨 밑 Essentials의 See the docs - Getting started - The configuration XML file 밑 스크립트 복사
// 복사한거 mybatis-config.xml 파일 이름으로 생성
// 3. DB 연결 정보를 담은 프로퍼티(properties) 파일 준비 // jdbc.properties 생성 및 편집
# key=value
jdbc.driver=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mariadb://localhost:3306/studydb
jdbc.username=study
jdbc.password=1111
// jdbc.driver= jdbc.url= jdbc.username= jdbc.password= // 맞는 값을 입력한다.
// 중요한 것은 오타가 발생하면 안되고, 맨 뒤에 공백이 있으면 안된다. // 공백여부 꼭 확인
// 4. mybatis 설정 파일(mybatis-config.xml) 편집 // mybatis-config.xml 편집
// <properties resource="com/eomcs/mybatis/ex01/jdbc.properties"></properties>
// jdbc.properties 파일의 내용을 읽어온다. // 3번 파일의 경로를 넣는다.
// 읽어온 정보는 ${프로퍼티명} 문법을 이용하여 그 값을 사용할 수 있다.
// <environments default="development"> // DBMS에 연결할 때 사용할 정보를 설정
// 여러 개의 연결 정보를 설정해두고 그 중에 사용할 정보를 지정할 수 있다.
// <environment id="development"> // 각각의 연결 정보는 다음과 같이 environment 태그에 설정한다.
// <transactionManager type="JDBC"/> // 트랜잭션 관리 방식을 지정한다.
// <dataSource type="POOLED"> // DB 커넥션 풀에 관련된 정보와 DB 연결 정보를 설정한다.
// 이제 개발자가 DB 커넥션 풀을 다룰 필요가 없다. // mybatis 프레임워크에서 관리한다.
// <property name="driver" value="${jdbc.driver}"/> // <property name="url" value="${jdbc.url}"/>
// <property name="username" value="${jdbc.username}"/>
// <property name="password" value="${jdbc.password}"/>
// ${위의 .properties 파일에 저장된 프로퍼티명}
// <mapper resource="step25/ex5/BoardMapper.xml"/> // 5.번 단계
// SQL문을 모아둔 파일(SQL 맵퍼 파일이라 부른다)을 지정한다.
// 맵퍼 파일의 경로를 지정할 때 classpath 경로를 사용해야 한다. // 경로는 . 대신 /를 사용한다.
// 5. SQL 문장을 작성할 파일 준비 // BoardMapper.xml 생성 및 편집
// 2번 경로 - Exploring Mapped SQL Statements 밑 스크립트 복사 - 복사한거 맞는 파일 이름으로 생성
// <mapper namespace="BoardMapper">
// SQL 맵퍼 파일이나 맵퍼 파일의 경로를, SQL을 사용할 인터페이스나 클래스 경로 그룹명으로 지정한다.
// <select id="selectBoard" resultType="com.eomcs.mybatis.ex01.Board">
<select id="selectBoard2" resultType="com.eomcs.mybatis.ex01.Board">
select
board_id as no,
title,
contents as content,
created_date registeredDate,
view_count viewCount
from x_board
</select>
// 아래의 코드를 selectBoard로 호출하게끔 한다. // 즉 별명을 붙히는 거라고 생각하면 이해가 쉽다.
// resultType은 결과적으로 어떤 형태를 나타나게 하느냐를 이야기 한다.
// DB에는 board_id, contents, created_date, view_count의 형태로 저장이 되어 있는 것을,
// Java에는 Board Class에 정의가 no, content, registeredDate, viewCount 여기에 넣는다는 의미이다.
// 즉 DB에서 Java로 가져올 때 저 데이터가 어떤 것을 대응(의미)하는지를 말한다고 생각하면 된다.
// mybatis 사용
// 1. mybatis 설정 파일을 읽을 InputStream 도구를 준비한다.
// 직접 시스템 파일 경로를 지정한다. // 소스 파일 경로를 지정하지 않는다.
// 컴파일된 후 XML파일이 놓이는 경로를 지정한다.
// 자바 패키지에 작성한 일반 파일은 빌드 디렉토리에 복사된다.
// 예) 프로젝트폴더/bin/main/com/eomcs/mybatis/ex01/mybatis-config.xml
// InputStream inputStream = new FileInputStream("./bin/main/com/eomcs/mybatis/ex01/mybatis-config.xml");
// 2. SqlSessionFactoryBuilder를 만들어 줄 빌더 객체 준비
// SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
// 3. SqlSession 객체를 만들어 줄 팩토리 객체 준비
// mybatis 설정 파일을 가지고 Builder를 이용하여 SqlSessionFactory 객체를 생성한다.
// 설정 파일의 경로를 직접 지정하지 말고, InputStream에 담아 InputStream을 넘겨주게끔 한다.
// SqlSessionFactory factory = factoryBuilder.build(inputStream);
// 4. SQL을 실행시키는 객체 준비
// SqlSession sqlSession = factory.openSession(); // openSession()은 수동 커밋의 객체를 리턴
// openSession(boolean autoCommit) // 자동 커밋으로 SQL을 다루고 싶을때 사용
Exam0111 -> mybatis 설정파일을 직접 지정하지 않는다. // Exam0110은 직접 지정한 경우
// 1. mybatis 설정 파일을 읽을 InputStream 도구를 준비한다.
// mybatis 설정 파일의 경로를 직접 지정하면,
// 애플리케이션 배포 경로가 바뀔 때마다 소스를 변경하고 다시 컴파일 해야 한다.
// InputStream InputStream = new FileInputStream(
// Resources.getResourceAsStream("com/eomcs/mybatis/ex01/mybatis-config.xml");
// Resources 클래스의 메서드를 이용 // 파라미터에 mybatis 설정 파일 경로를 지정 // .대신 /를 사용
// 자바 패키지 경로를 그대로 알아서 찾아간다. // bin/main... 경로를 알아서 찾아감.
// 자바 패키지 경로에서 찾기 때문에 mybatis 설정 파일은 반드시 자바 패키지 경로에 있어야 한다.
// 이후 단계는 Exam0110과 동일하다.
Exam0112 -> mybatis 코드 정리 // Exam0110, Exam0111 주석 지우고 코드 정리한 것.
Exam0120 -> SQL 문 실행
// select 문장 // sqlSession.selectList() // 목록 리턴 // sqlSession.selectOne() // 한 개의 결과 리턴
// insert 문장 // sqlSession.insert() // update 문장 // sqlSession.update() // delete 문장 // sqlSession.delete()
// insert/update/delete인 경우 아무거나 호출해도 된다. // update로 처리가 된다 내부적으로.
// 하지만 일관된 유지보수를 위해 메서드를 구분하여 사용하자.
// List list = sqlSession.selectList("BoardMapper.selectBoard");
// 메서드 사용법 selectList(SQL문 이름, 파라미터값) // SQL문 이름 = 그룹명 + "." + SQL문장 아이디
// 그룹명 // <mapper namespace="그룹명">...</mapper> // BoardMapper.xml
// SQL 문장 아이디 // <select id="SQL문장 아이디">...</select> // BoardMapper.xml
// 파라미터 값 // primitive type 및 모든 자바 객체가 가능 // 여러 개의 값을 전달할 때는 Map에 담아 넘긴다.
// selectList 동작 원리
// 1. resultType에 지정한 클래스의 인스턴스를 생성한다.
// 컬럼 이름과 일치하는 프로퍼티(setter)를 찾아 값을 입력한다.
// 일치하는 프로퍼티가 없다면 값을 입력하지 못한다.
Exam0121 -> mybatis // 컬럼 이름과 프로퍼티 이름을 일치시키기
// BoardMapper.xml // Java 컬럼과 SQL 컬럼 이름 설정방법은 애초에 다르다.
// board_id as no, // as를 사용하여 Java에서의 컬럼명과 SQL에서의 컬럼명을 연결시켜준다. // as 생략가능
Exam0210 ->mybatis // 클래스 별명 지정하기
// mybatis 설정 파일에서 fully-qualified class name 을 사용하는 대신 짧은 이름으로 대체할 수 있다.
// fully-qualified class name // 패키지 명을 포함한 클래스명
// <typeAliases> <typeAlias type="com.eomcs.mybatis.ex01.Board" alias="Board"/> </typeAliases>
// mybatis-config02를 참조하면 된다. // com.eomcs.mybatis.ex01.Board에 Board라는 별명을 붙힌 것이다.
// typeAliases를 설정할 때에는 /가 아닌 .을 사용한다. // 이유는 Java에서 사용하는 경로이기 때문에 .이다.
mybatis #ex02
Exam0110 -> 컬럼 이름과 프로퍼티 이름 // 컬럼 이름과 프로퍼티 이름이 다르면 데이터를 가져오지 못한다.
<select id="selectBoard" resultType="Board">
select
board_id, <!-- Board.setBoard_id() 호출 -->
title, <!-- Board.setTitle() 호출 -->
contents, <!-- Board.setContents() 호출 -->
created_date, <!-- Board.setCreated_date() 호출 -->
view_count <!-- Board.setView_count() 호출 -->
from x_board
</select>
// 위의 SQL문을 mybatis는 내부에서 다음과 같은 코드로 실행할 것이다.
ArrayList<Board> list = new ArrayList<>();
while (rs.next()) {
Board board = new Board();
board.setBoard_id(rs.getNo("board_id")); // 이런 셋터가 없다.
board.setTitle(rs.getString("title")); // 이 셋터는 있다.
board.setContents(rs.getString("contents")); // 이런 셋터가 없다.
board.setCreated_date(rs.getDate("created_date")); // 이런 셋터가 없다.
board.setView_count(rs.getDate("view_count")); // 이런 셋터가 없다.
list.add(board);
}
return list;
Exam0120 -> 컬럼 이름과 프로퍼티 이름 // 컬럼 이름과 프로퍼티 이름이 같으면 데이터를 가져올 수 있다.
Exam0130 -> 컬럼 이름과 프로퍼티 이름 // 컬럼 이름과 프로퍼티 이름을 연결한다.
<mapper namespace="BoardMapper">
<resultMap type="Board" id="BoardMap">
<id column="board_id" property="no"/>
<result column="contents" property="content"/>
<result column="created_date" property="registeredDate"/>
<result column="view_count" property="viewCount"/>
</resultMap>
// <resultMap type="Board" id="BoardMap">
// type // 자바 객체의 클래스명 또는 별명 // id // 연결 정보를 가리키는 식별자
// <id column="bno" property="no"/> <result column="titl" property="title"/>
// <result column="테이블 컬럼명" property="자바 객체의 프로퍼티명"> // primary key 컬럼은 id 태그를 사용
// column명과, property명이 같다면, 생략 가능하다. // 이름이 같으니깐 알아서 가져올 것이기 때문
// <select id="selectBoard" resultMap="BoardMap">
// 위에서 정의한 연결 정보를 사용하고 싶다면, resultMap="연결정보아이디" 를 설정한다.
// 연결 정보를 사용하면, Exam0120처럼 일일히 연결시키지 않아도 된다.
// 원래는 사용시마다, 컬럼을 as로 연결을 했어야 하지만 resultMap을 한번 만들어두면,
// as를 연결할 필요 없이 resultMap을 가져오면 된다.
Exam0210 -> SQL에 파라미터로 값 전달 // int
// SQL 문에 값 삽입하기 // in-parameter 지정하기
// parameterType에 지정된 객체의 프로퍼티 명을 사용하여 값을 삽입한다. // ex) #{프로퍼티명}
// parameterType이 primitive/String/wrapper class인 경우, 아무 이름을 적어도 된다. // #{haha}
// parameterType이 Map 객체인 경우는 map에 저장된 값의 key를 적는다. // #{key}
// SQL을 실행할 때 파라미터로 넘어오는 값의 타입을 지정한다.
// parameterType="값이 들어 있는 객체의 타입명 또는 별명"
// int => _int // Integer => int // String => string // Map => map // HashMap => hashmap // Board => board
// List list = sqlSession.selectList("BoardMapper.selectBoard1", 5); /
// <select id="selectBoard1" resultMap="BoardMap" parameterType="int"> // where board_id > #{ohora}
// BoardMapper04.xml // 파라미터 타입이 int라서 아무거나 입력 가능
Exam0211 -> SQL에 파라미터로 값 전달 // String
// List list = sqlSession.selectList("BoardMapper.selectBoard2", "%ohora%");
// <select id="selectBoard2" resultMap="BoardMap" parameterType="string"> // where title like #{haha}
// BoardMapper04.xml // 파라미터 타입이 String라서 아무거나 입력 가능
Exam0212 -> SQL에 파라미터로 값 전달 // Map
// HashMap<String, Object> params = new HashMap<>();
// List list = sqlSession.selectList("BoardMapper.selectBoard3", params);
// <select id="selectBoard3" resultMap="BoardMap" parameterType="map"> // limit #{startIndex}, #{size}
// BoardMapper04.xml // 파라미터 타입이 Map이라서 저장된 값의 key를 이용한다.
<select id="selectBoard3"
resultMap="BoardMap"
parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
limit #{startIndex}, #{size}
</select>
Exam0220 -> SQL에 파라미터로 값 전달 // #{} 문법 // 오름차순
// <select id="selectBoard1" resultMap="BoardMap" parameterType="string"> // order by #{colname} asc
// BoardMapper05.xml // #{} 문법의 경우 파라미터에 값을 넣을 때 사용한다. //
Exam0221 -> SQL에 파라미터로 값 전달 // ${}문법
// <select id="selectBoard2" resultMap="BoardMap" parameterType="string"> // order by ${colname} asc
// BoardMapper05.xml // ${} 문법의 경우 값을 SQL 문에 그대로 삽입한다. //
Exam0222 -> SQL에 파라미터로 값 전달 // ${}문법의 단점 // SQL 삽입 공격
// <select id="selectBoard3" resultMap="BoardMap" parameterType="string"> // ${sql}
// SQL Mapper에서는 ${} 문법으로 SQL문을 받는다.
// 단, 사용자가 입력한 값 그대로 파라미터에 넘긴다면 SQL 삽입 공격에 노출 될 수 있다.
// 사용자가 입력한 값을 그대로 전달하지 않고, 개발자가 지정한 값을 전달한다는 전제 하에는 사용할 수 있다.
Exam0230 -> SQL에 파라미터로 값 전달 // 일반 객체
// <insert id="insertBoard" parameterType="Board"> insert into x_board(title,content,created_date)
// values(#{title},#{content},now()) </insert> // '를 붙히면 안된다!
// title과 content가 string이라고 해서 'title' 'content' 처럼 '를 붙히지 않는다. // 값으로 인식, 값으로 들어감.
// parameterType="클래스명 또는 별명" // 일반 객체를 파라미터로 받을 수 있다. //
// 일반 객체에서 값을 꺼내려면 프로퍼티(getter)명을 지정해야 한다. // getTitle(), getContent()
// sqlSession.commit(); // 작업이 끝난 후 commit 필수 // mybatis에서는 autocommit이 기본으로 false이다.
// autocommit? //
// insert/update/delete 과 같이 데이터를 변경하는 작업은
// 위험하기 때문에 DBMS의 임시 메모리에 그 작업 결과를 보관한다.
// 클라이언트에서 최종적으로 변경을 허락해야만 진짜 테이블에 값을 반영한다
// commit 명령을 내리지 않으면 insert/update/delete을 테이블에 반영하지 않는다. // close() 할 때 취소된다.
// commit // 임시 메모리에 저장된 작업 결과를 실제 테이블에 반영시키는 명령
// rollback // 임시 메모리에 저장된 작업 결과를 취소하는 명령
Exam0240 -> INSERT 실행 후 자동 증가된 PK값 가져오기
<insert id="insertBoard" parameterType="Board"
useGeneratedKeys="true" keyColumn="board_id" keyProperty="no">
insert into x_board(title,contents,created_date)
values(#{title},#{content},now())
</insert>
// keyColumn 중 "board_id" 값을 keyProperty 중 "no"값에 넣는다는 의미이다.
// no의 경우 auto-increase로 DBMS에 넣는 즉시 생성되는데, Java에는 no값이 없는 상태이다.
// useGeneratedKey를 설정하면, 자동으로 DBMS에서 Java의 noSetter를 호출해 값을 리턴해준다.
// Java에서 별도로 코드 작업할 필요가 없다 // as 설정하면 알아서 Java Setter 호출하는 걸로 생각하자.
Exam0250 -> Update SQL 실행하기
// Insert와 크게 다르지 않다. // 차이 점이라면 update이기 때문에 no값을 이미 알고 있다는 점.
// 따라서 useGeneratedKey를 설정하면 안된다는 점의 차이 정도?
Exam0260 -> DeleteSQL 실행하기
// 먼저 자식 테이블의 데이터를 지운다.
int count = sqlSession.delete("BoardMapper.deleteBoardFile", 3);
System.out.println(count);
// 그런 후 부모 테이블의 데이터를 지운다.
count = sqlSession.delete("BoardMapper.deleteBoard", 3);
System.out.println(count);
// delete의 경우 DBMS에 UK가 설정되어 있기 때문에 꼭 자식테이블의 데이터를 먼저 지워줘야 한다.
<mapper namespace="BoardMapper">
<delete id="deleteBoard" parameterType="int">
delete from x_board
where board_id=#{value}
</delete>
<delete id="deleteBoardFile" parameterType="int">
delete from x_board_file
where board_id=#{value}
</delete>
</mapper>
// 당연히 자식테이블의 데이터를 지우는 코드도 Mapper에 존재해야 한다.
// 같은 Mapper 파일에 있을 필요는 없다. // 다른 Mapper파일에 존재한다면,
// 자식 테이블의 데이터를 지우는 Mapper의 id를 먼저 호출 후 지우고,
// 부모 테이블의 데이터를 지우는 Mapper의 id를 후에 호출하면 된다.
mybatis #ex03
Exam0110 -> Dynamic SQL 다루기 // 조건문 사용 전
Exam0120 -> Dynamic SQL 다루기 // If조건문 사용 후
// Dynamic SQL // 조건에 따라 SQL을 달리 생성하는 것
<select id="select3" resultMap="BoardMap" parameterType="int">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<if test="no != null">
where board_id = #{no}
</if>
</select>
// if조건문 // <if test="조건">SQL문</if> // "no != null"일 때 where문을 추가하는 구조이다.
Exam0130 -> 파라미터로 Map을 넘겨받았을 때
<select id="select4" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<if test="item == 1">
where board_id = #{keyword}
</if>
<if test="item == 2">
where title like concat('%', #{keyword}, '%')
</if>
<if test="item == 3">
where contents like concat('%', #{keyword}, '%')
</if>
</select>
// 파라미터로 넘겨받은 map의 item 값에 따라 if문을 별도로 조정할 수 있다.
// concat? // concat은 문자열을 합쳐주는 역할을 한다. // concat('%', #{keyword}, '%') // %keyword값% //
Exam0140 -> if조건문에 or를 사용할 때 // 실행 오류
<select id="select5" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
where
<if test="no != null">
board_id = #{no}
</if>
<if test="title != null">
or title like concat('%', #{title}, '%')
</if>
<if test="content != null">
or contents like concat('%', #{content}, '%')
</if>
</select>
// 위 데이터의 문제점은 no != null이 false일때, 문제가 발생한다. // 가장 처음 조건문이 나타나지 않을 때
// 앞에 가장 처음에 나오는 조건문이 존재하지 않기 때문에, or 조건자체가 오류가 되어버리는 것이다.
Exam0150 -> if조건문에 or를 사용할 때 // 실행 오류 해결 방법 1 // 의미없는 문구 추가
<select id="select6" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
where 1=0
<if test="no != null">
or board_id = #{no}
</if>
<if test="title != null">
or title like concat('%', #{title}, '%')
</if>
<if test="content != null">
or contents like concat('%', #{content}, '%')
</if>
</select>
// 첫번째 if문이 나타나지 않을때를 대비하여 아예 1=0이라는 조건에 영향을 끼치지 않는 조건문을 넣는다.
Exam0160 -> if조건문에 or를 사용할 때 // 실행 오류 해결 방법 2 // <where> 사용
<select id="select7" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<where>
<if test="no != null">
board_id = #{no}
</if>
<if test="title != null">
or title like concat('%', #{title}, '%')
</if>
<if test="content != null">
or contents like concat('%', #{content}, '%')
</if>
</where>
</select>
// <where>을 사용하면, 첫번째 if문이 false여도 상관이 없다. // 첫 문장이 or가 붙어있어도 상관 없다.
Exam0170 ->if조건문에 or를 사용할 때 // 실행 오류 해결 방법 3 // trim 사용
<select id="select8" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<trim prefix="where" prefixOverrides="OR | AND">
<if test="no != null">
board_id = #{no}
</if>
<if test="title != null">
or title like concat('%', #{title}, '%')
</if>
<if test="content != null">
or contents like concat('%', #{content}, '%')
</if>
</trim>
</select>
// trim prefix="where" prefixOverrides="OR | AND"
// or/and 앞에 아무것도 없을 때 or/and 를 자동으로 제거한다.
// 실무에서 Exam0150, Exam0160, Exam0170 셋 다 사용한다. // 선임 개발자마다 다르니 셋 다 알아둘 것.
Exam0210 -> <choose> 사용
HashMap<String, Object> params = new HashMap<>();
if (item.equals("1")) {
params.put("item", "no");
} else if (item.equals("2")) {
params.put("item", "title");
} else {
params.put("item", "content");
}
params.put("keyword", keyword);
List<Board> list = sqlSession.selectList("BoardMapper.select21", params);
// item이 1일때 no를 넣고 // 2일때 title을 넣고 // 그 외는 content를 넣고 // keyword에는 keyword를 넣는다.
<select id="select21" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
where
<choose>
<when test="item == 'no'">
board_id = #{keyword}
</when>
<when test='item == "title"'>
title like concat('%', #{keyword}, '%')
</when>
<otherwise>
contents like concat('%', #{keyword}, '%')
</otherwise>
</choose>
</select>
Exam0220 -> <set> 사용 전
if (params.get("title") != null && params.get("content") != null) {
count = sqlSession.update("BoardMapper.update3", params);
} else if (params.get("title") != null) {
count = sqlSession.update("BoardMapper.update1", params);
} else if (params.get("content") != null) {
count = sqlSession.update("BoardMapper.update2", params);
}
<update id="update1" parameterType="map">
update x_board set
title=#{title}
where board_id=#{no}
</update>
<update id="update2" parameterType="map">
update x_board set
contents=#{content}
where board_id=#{no}
</update>
<update id="update3" parameterType="map">
update x_board set
title=#{title},
contents=#{content}
where board_id=#{no}
</update>
// 제목만 바꿀 경우, 내용만 바꿀 경우, 둘 다 바꿀 경우에 대해 각각의 update SQL을 준비한다.
// Java 코드로도, SQL문에서도 굉장히 복잡하고 비 효율적임을 알 수 있다.
Exam0230 -> <set> 사용 후
int count = sqlSession.update("BoardMapper.update4", params);
<update id="update4" parameterType="map">
update x_board
<set>
<if test="title != null">title=#{title},</if>
<if test="content != null">contents=#{content}</if>
</set>
where board_id=#{no}
</update>
// set을 통해 이용하여 SQL 앞,뒤에 붙은 콤마(,)를 자동으로 제거한다.
// update 1, 2, 3에서는 update할 컬럼이 어떤 게 될 지 모르기 때문에 ,때문에 힘들게 설정했었다.
Exam0240 -> <foreach> 사용 전
System.out.print("조회할 게시물 번호들(예: 1 6 8 10; 최대 5개)? ");
String[] values = keyScan.nextLine().split(" ");
int index = 0;
for (String value : values) {
params.put("no" + index++, value);
}
<select id="select22" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<where>
<if test="no0 != null">
board_id = #{no0}
</if>
<if test="no1 != null">
or board_id = #{no1}
</if>
<if test="no2 != null">
or board_id = #{no2}
</if>
<if test="no3 != null">
or board_id = #{no3}
</if>
<if test="no4 != null">
or board_id = #{no4}
</if>
</where>
</select>
// 번호를 5개만 받아도 if문을 5개를 생성해야 한다는 단점이 있다.
Exam0250 -> <foreach> 사용 후 // collection, item 사용
System.out.print("조회할 게시물 번호들(예: 1 6 8 10; 최대 5개)? ");
String[] values = keyScan.nextLine().split(" ");
ArrayList<Object> noList = new ArrayList<>();
for (String value : values) {
noList.add(value);
}
params.put("noList", noList);
// Exam0240에서는 no를 낱개로 넣었다면, 이번에는 List를 만들어 맵에 담아 넘긴다.
<select id="select23" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<where>
<foreach collection="noList" item="no">
or board_id = #{no}
</foreach>
</where>
</select>
// foreach를 사용하면, 넘겨받은 noList에서 no값을 넣어 확인한다.
// foreach문 변수로는 collection, item, open, close, separator, index를 사용할 수 있다.
// collection // 전달받은 인자. List나 Array 형태만 가능 // item // 전달받은 인자값을 alias 명으로 대체
// open // 해당 구문이 시작될때 삽입할 문자열 // close // 해당 구문이 종료될때 삽입할 문자열
// separator // 반복 되는 사이에 출력할 문자열 // index // 반복되는 구문 번호이다. 0부터 순차적으로 증가
Exam0250 -> <foreach> 사용 후 // collection, item, open, close, separator 사용
<select id="select24" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date, <!-- comments -->
view_count /* comments */
from x_board
<where>
board_id in /* (1, 2, 11, 12) */
<foreach collection="noList" item="no" open="(" separator="," close=")">
#{no}
</foreach>
</where>
</select>
// board_id in (1, 2, 11, 12) // in을 사용하면 배열을 (값, 값, 값)과 같이 만들어 넘길 수 있다.
// 그러나 이 배열을 Java에서 만드는 것이 아니라, SQL문 내에서 만들 수 있다.
// open과 separator와 close를 이용하여 배열 값을 만들어서 in에게 넘길 수 있다는 것이다.
// 중간에 /* 내용 */ 폼폼크랩같이 생긴 애는 주석처리 하는 방법이다.
// /*다음과 */전에 무조건 공백이 있어야만 한다.
Exam0270 -> <bind> 사용 전 // concat 사용
<select id="select25" resultMap="BoardMap" parameterType="map">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<where>
<foreach collection="words" item="word">
or title like concat('%', #{word}, '%')
</foreach>
</where>
</select>
Exam0280 -> <bind> 사용 후
<select id="select26" resultMap="BoardMap" parameterType="map">
<bind name="titlePattern" value="'%' + _parameter.title + '%'"/>
select
board_id,
title,
contents,
created_date,
view_count
from x_board
<where>
title like #{titlePattern}
</where>
</select>
// foreach와 concat을 사용하여 직관적이지 않게 만들어 사용하는 것 보다는
// bind를 이용하여 미리 형식을 만들어두고 해당 name을 두는 방법이 있다.
Exam0310 -> <sql> 사용 후
<sql id="sql1">
select
board_id,
title,
contents,
created_date,
view_count
from x_board
</sql>
<select id="select1" resultMap="BoardMap" parameterType="int">
<include refid="sql1"/>
where board_id = #{value}
</select>
<select id="select2" resultMap="BoardMap" parameterType="int">
<include refid="sql1"/>
</select>
// 반복적으로 사용되는 코드를 미리 sql로 추출하여 사용한다.
// <include refid="sql id값"/> // 형식으로 사용한다.
mybatis #ex04
Exam0110 -> 조인 사용 전 // Board 데이터 따로, List<AttachFile> files 데이터 따로 데이터를 불러와야 한다.
Exam0120 -> 조인 사용 후 // 조인을 사용하면 게시글 객체 안에 첨부파일 객체가 들어 있다.
Board board = sqlSession.selectOne("BoardMapper2.selectBoardWithFile", 1);
System.out.println("[게시글 조회]");
System.out.printf("번호: %d\n", board.getNo());
System.out.printf("제목: %s\n", board.getTitle());
System.out.printf("내용: %s\n", board.getContent());
System.out.printf("등록일: %s\n", board.getRegisteredDate());
System.out.printf("조회수: %d\n", board.getViewCount());
System.out.println();
System.out.println("[첨부파일]");
for (AttachFile file : board.getFiles()) {
System.out.printf("%d, %s\n", file.getNo(), file.getFilePath());
}
<mapper namespace="BoardMapper2">
<resultMap type="Board" id="BoardMap">
<id column="board_id" property="no"/>
<!--
<result column="title" property="title"/>
-->
<result column="contents" property="content"/>
<result column="created_date" property="registeredDate"/>
<result column="view_count" property="viewCount"/>
<collection property="files" ofType="AttachFile">
<id column="board_file_id" property="no"/>
<result column="file_path" property="filePath"/>
<result column="board_id" property="boardNo"/>
</collection>
</resultMap>
<select id="selectBoardWithFile" resultMap="BoardMap" parameterType="int">
select
b.board_id,
b.title,
b.contents,
b.created_date,
b.view_count,
f.board_file_id,
f.file_path
from x_board b
left outer join x_board_file f on b.board_id=f.board_id
where b.board_id = #{no}
</select>
'IT Developer > Bitcamp' 카테고리의 다른 글
비트캠프 프론트엔드 및 벡엔드 개발자 Web (0) | 2020.04.03 |
---|---|
비트캠프 프론트엔드 및 백엔드 개발자 #Project v55~60 (0) | 2020.03.31 |
비트캠프 프론트엔드 및 벡엔드 개발자 DB (0) | 2020.03.26 |
비트캠프 프론트엔드 및 벡엔드 개발자 net, netty, reflect (0) | 2020.03.11 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v43 ~ v54 (0) | 2020.03.10 |
비트캠프 프론트엔드 및 백엔드 개발자 SQL (0) | 2020.02.18 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v26 ~ v42 (0) | 2020.01.21 |
비트캠프 프론트엔드 및 백엔드 개발자 #Project v1 ~ v25 (0) | 2020.01.13 |