작성 순서 및 이유
Table 생성
DTO 클래스 작성
Mapper.xml, Mapper.java 작성
Mapper.xml: SQL 쿼리를 작성하는 XML 파일로, MyBatis에서 실행할 SQL을 정의한다.
Mapper.java: Mapper 인터페이스로, XML 파일과 바인딩되어 쿼리 메서드를 선언한다.
이름을 같게 하는 이유
바인딩의 일관성
유지보수성
UserMapper.java와 UserMapper.xml이 서로 바인딩되어 있어야 어떤 인터페이스와 어떤 XML이 연결되는지 혼동하지 않고 쉽게 파악할 수 있다.MyBatis에서 자동 바인딩
DAO 인터페이스와 클래스 작성
Service 인터페이스와 클래스 작성
Service 인터페이스
Service 클래스
Controller 클래스 작성
의존성을 높이는 것이 아닌, 낮추는 목적이다. 인터페이스와 클래스를 나누는 이유는 의존성을 낮춰서 코드의 유연성과 확장성을 높이는 것이다.
의존성 주입(DI, Dependency Injection)
Spring에서는 인터페이스를 통해 의존성 주입을 사용하여 구현체를 변경할 때 코드 수정을 최소화할 수 있다.
확장성과 유지보구성
인터페이스를 통해 코드가 특정 구현체에 의존하지 않도로고 만들면, 구현체 변경 시 코드 수정이 필요 없는 경우가 많아 유지보수가 용이해진다.
log4jdbc-log4j2-jdbc4 라이브러리를 프로젝트에 빌드 처리 - 메이븐 : pom.xml
Spring Bean Configuration File(root-context.xml)에서 DataSource 관련 클래스를 Spring Bean으로 등록한 bean 엘리먼트에서 driverClassName 필드와 url 필드에 저장된 값 변경
[src/main/resources] 폴더에 [log4jdbc.log4j2.properties] 파일 작성
=> MyBatis 프레임워크에서 발생되는 로그 이벤트를 Spring 프레임워크의 로그 구현체에게 제공하기 위한 SpyLogDelegator 클래스를 설정하기 위한 파일
SpyLogDelegator 객체에 의해 발생된 로그 이벤트를 Spring 프레임워크의 로그 구현체로 기록되도록 환경설정파일(log4j.xml) 변경 - logger 엘리먼트 추가
<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc11 -->
<!-- => Oracle DBMS Server에 접속할 수 있는 Driver 기능을 제공하기 위한 라이브러리 -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId>
<version>23.5.0.24.07</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<!-- => Spring DAO 관련 기능을 제공하기 위한 라이브러리 - DataSource 관련 클래스 포함 -->
<!-- => 라이브러리 의존 관계에 의해 spring-tx 라이브러리(트렌젝션 처리)도 자동으로 빌드 처리 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<!-- => Mybatis 프레임워크를 사용하기 위한 라이브러리 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.2</version>
</dependency>
MyBatis 프레임워크의 환경설정파일(mybatis-config.xml - setting 엘리먼트) 작성
=> [src/main/webapp] 폴더에 환경설정파일을 작성해야 스프링 컨테이너(WebApplicationContext 객체)가 환경설정파일을 제공받아 SqlSessionFactory 객체 생성 가능
Spring Bean Configuration File(root-context.xml)에 DataSource 관련 클래스, SqlSessionFactory 관련 클래스, SqlSession 관련 클래스, TransactionManager 관련 클래스를 Spring Bean으로 등록
매퍼 파일을 사용해 DAO 클래스 작성
이름 널? 유형
-------- -------- -------------
NO NOT NULL NUMBER(4)
NAME VARCHAR2(50)
PHONE VARCHAR2(20)
ADDRESS VARCHAR2(100)
BIRTHDAY DATE
항상 테이블 먼저 설계 및 생성
테이블의 행을 Java 클래스로 표현해 객체로 생성하기 위한 클래스
데이터 처리 클래스(DAO 클래스 또는 Service 클래스)의 메소드에서 매개변수로 값을 전달받거나 값을 변환하기 위해 작성하는 클래스
Controller 클래스의 요청 처리 메소드의 매개변수로 전달값이 저장된 Command 객체로 제공받기 위해 사용 - 전달값의 이름과 필드의 이름이 같도록 작성해야됨
MyBatis 프레임워크를 사용할 경우 검색행의 컬럼값이 저장하기 위해 컬럼명과 같은 이름으로 필드를 작성해 자동 매핑되도록 설정해야한다.
@Data // Setter 메소드, Getter 메서드 등등 제공
@Builder // 객체 생성시 객체 필드에 필요한 값만 저장하기 위한 메서드
public class Student {
private int no;
private String name;
private String phone;
private String address;
private String birthday;
}
Mapper.xml에서 사용되는 <mapper> 엘리먼트와 namespace 속성은 MyBatis에서 SQL 문과 Java 코드 간의 연결을 설정하는 데 중요한 역할을 한다. 이 요소들은 MyBatis가 SQL 쿼리를 올바르게 찾아서 실행하고, Java 인터페이스 메서드와 연동될 수 있도록 도와준다.
<mapper> 엘리먼트
MyBatis에서 SQL 문을 정의하는 XML 파일의 루트 엘리먼트이다. <mapper> 엘리먼트는 XML 문서의 시작점으로, 그 내부에 SQL 쿼리를 정의하는 <select>, <insert>, <update>, <delete> 등의 요소를 포함한다.
<mapper> 엘리먼트는 반드시 namespace 속성을 포함해야 하며, XML 파일의 최상단에 위치한다.
namespace 속성
namespace 속성은 해당 Mapper XML 파일이 어떤 Java 인터페이스와 연결되는지를 명시합니다. 쉽게 말해, 이 XML 파일의 SQL 쿼리들이 어떤 Java 인터페이스의 메서드들과 매핑되는지를 정의하는 것이다.<mapper namespace="xyz.itwill09.mapper.StudentMapper">
<insert id="insertStudent">
insert into student values(#{no}, #{name}, #{phone}, #{address}, #{birthday})
</insert>
<select id="selectStudentList" resultType="Student">
select no, name, phone, address, birthday from student order by no
</select>
</mapper>
resultType 속성은 MyBatis의 <select> 엘리먼트에서 SQL 쿼리의 결과를 어떤 Java 객체로 매핑할지 지정하는 데 사용된다. 이 속성은 쿼리의 결과가 Java 객체로 자동으로 변환될 수 있도록 도와주고, 주로 DTO 클래스나 기본 Java 타입을 지정한다.
resultType으로 지정된 클래스의 필드 이름을 기준으로 자동으로 매핑한다. 예를 들어 SQL 쿼리에서 no, name, phone과 같은 컬럼을 반환하면, MyBatis는 이를 Student 클래스의 no, name, phone 필드에 자동으로 매핑한다.resultType이 간단한 자동 매핑을 제공한다면, resultMap은 복잡한 매핑 요구사항을 처리할 수 있다.
resultMap은 SQL 쿼리 결과를 Java 객체로 매핑할 때, 컬럼과 객체 필드 간의 매핑을 상세하게 설정할 수 있다. 컬럼 이름과 객체 필드 이름이 다를 때나, 복합 객체를 매핑할 때 유용하다.public interface StudentMapper {
// Student 객체를 입력받아 해당 객체의 데이터를 데이터베이스에 삽입하는 역할
int insertStudent(Student student);
// 데이터베이스에서 모든 학생 정보를 조회하여 반환하는 역할, Student 객체가 담긴 리스트가 반환
List<Student> selectStudentList();
}
DAO 클래스(Repository 클래스)
저장매체에 행을 삽입하거나 저장된 행을 변경, 삭제, 검색하는 기능을 제공하기 위한 클래스
DAO 클래스가 교체되어도 의존관계로 설정된 Service 클래스에 영향을 최소화 하기 위해 인터페이스를 받아 구현체로 작성하는 것을 권장 - 유지보수의 효율성 증가
DBMS 서버를 저장매체로 사용해 SQL 명령을 전달하여 행의 삽입, 변경, 삭제, 검색 처리
=> DAO 클래스의 메소드는 DBMS 서버에 접속해 하나의 SQL 명령을 전달하여 실행하고 실행결과를 Java 객체(값)으로 반환되도록 작성 - JDBC
public interface StudentDAO {
int insertStudent(Student student);
List<Student> selectStudentList();
}
DAO 클래스는 Service 클래스에서 객체로 제공받아 사용할 수 있도록 Spring Bean으로 등록
DAO클래스는 @Repository 어노테이션을 사용하여 Spring Bean으로 등록 처리
@Repository 어노테이션을 스프링 컨테이너가 처리하기 위해서는 반드시 클래스가 작성된 패키지 Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
@Repository
//final 제한자를 사용한 필드만 초기화 처리하는 생성자를 만들어 주는 어노테이션
@RequiredArgsConstructor
public class StudentDAOImpl implements StudentDAO {
//DAO 클래스의 메소드에서는 매퍼에 등록된 SQL 명령을 제공받아 DBMS 서버에 전달하여 실행하고
//실행결과를 Java 객체로 반환받기 위해 SqlSession 객체 필요
// => SqlSession 객체가 저장될 수 있는 필드를 작성해 스프링 컨테이너로부터 객체를 제공받아
//필드에 저장되도록 의존성 주입
//필드에 @Autowired 어노테이션을 사용해 객체가 저장되도록 의존성 주입
// => 필드 레벨의 의존성 주입
//@Autowired
//private SqlSession sqlSession;
//@Autowired 어노테이션을 사용한 생성자로 필드에 객체가 저장되도록 의존성 주입
// => 생성자 레벨의 의존성 주입 : 순환참조 방지
// => 생성자를 하나만 작성된 경우 @Autowired 어노테이션 생략 가능
private final SqlSession sqlSession;
@Override
public int insertStudent(Student student) {
return sqlSession.getMapper(StudentMapper.class).insertStudent(student);
}
@Override
public List<Student> selectStudentList() {
return sqlSession.getMapper(StudentMapper.class).selectStudentList();
}
}
sqlSession 객체는 MyBatis에서 SQL 명령을 실행하고, 데이터베이스와 상호작용하는 핵심 역할을 한다. sqlSession.getMapper(StudentMapper.class)를 사용하여 StudentMapper 인터페이스의 구현체를 얻어오고, 그 구현체의 메서드를 호출하여 SQL 명령을 실행한다.
public interface StudentService {
void addStudent(Student student);
List<Student> getStudentList();
}
Service 클래스
클라이언트 요청에 대한 데이터 처리 기능을 제공하기 위한 클래스
Service 클래스의 메소드는 데이터 처리 기능에 필요한 명령을 DAO 객체의 메소드를 호출해 작성
Service 클래스가 교체되어도 의존관계로 설정된 Controller 클래스에 영향을 최소화 하기 위해 인터페이스를 받아 구현체로 작성하는 것을 권장 - 유지보수 효율성 증가
Service 클래스는 Controller 클래스에서 객체로 제공받아 사용할 수 있도록 Spring Bean으로 등록
Service 클래스는 @Service 어노테이션을 사용하여 Spring Bean으로 등록 처리
@Service 클래스는 @Service 어노테이션을 사용하여 Spring Bean으로 등록 처리
@Service 클래스는 어노테이션을 스프링 컨테이너가 처리하기 위해서는 반드시 클래스가 작성된 패키지를 Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
@Service
@RequiredArgsConstructor
public class StudentServiceImpl implements StudentService {
//Service 클래스의 메소드에서는 DAO 클래스의 메소드를 호출할 수 있도록 DAO 객체 필요
// => DAO 객체가 저장될 수 있는 필드를 작성해 스프링 컨테이너로부터 객체를 제공받아
//필드에 저장되도록 의존성 주입 - 생성자 레벨의 의존성 주입
private final StudentDAO studentDAO;
@Override
public void addStudent(Student student) {
studentDAO.insertStudent(student);
}
@Override
public List<Student> getStudentList() {
return studentDAO.selectStudentList();
}
}
Controller 클래스
클라이언트의 요청을 처리하는 기능을 제공하기 위한 클래스
Controller 클래스의 요청 처리 메소드에서는 데이터 처리에 필요한 명령으로 Service 객체의 메소드를 호출해 작성
Controller 클래스는 Front Controller에서 객체로 제공받아 사용할 수 있도록 Spring Bean으로 등록
Controller 클래스는 @Controller 어노테이션을 사용하여 Spring Bean으로 등록 처리
@Controller 어노테이션을 스프링 컨테이너가 처리하기 위해서는 반드시 클래스가 작성된 패키지를 Spring Bean Configuration File(servlet-context.xml)의 component-scan 엘리먼트로 검색되도록 설정
@Controller
@RequiredArgsConstructor
@RequestMapping("/student")
public class StudentController {
//Controller 클래스의 메소드에서는 Service 클래스의 메소드를 호출할 수 있도록 Service 객체 필요
// => Service 객체가 저장될 수 있는 필드를 작성해 스프링 컨테이너로부터 객체를 제공받아
//필드에 저장되도록 의존성 주입 - 생성자 레벨의 의존성 주입
private final StudentService studentService;
//학생정보를 입력받기 위한 JSP 문서의 뷰이름을 반환하는 요청 처리 메소드
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String add() {
return "student/student_add";
}
//학생정보를 전달받아 STUDENT 테이블에 행으로 삽입하고 학생목록을 출력하는 페이지를 요청할
//수 있는 URL 주소로 응답하는 요청 처리 메소드
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(@ModelAttribute Student student, Model model) {
try {
studentService.addStudent(student);
} catch (Exception e) {
model.addAttribute("message", "학생번호가 중복되거나 입력값에 문제가 있습니다.");
return "student/student_add";
}
return "redirect:/student/display";//리다이렉트 이동
}
//STUDENT 테이블에 저장된 모든 행을 검색해 반환받은 학생목록을 Request Scope 속성값으로
//저장하고 학생목록을 출력하는 JSP 문서의 뷰이름을 반환하는 요청 처리 메소드
@RequestMapping("/display")
public String display(Model model) {
model.addAttribute("studentList", studentService.getStudentList());
return "student/student_display";
}
}