💡 개념 간단 정리
log4jdbc
sql은 스프링 부트가 아닌 오라클에서 실행되기 때문에 추가 작업 없이는 로깅이 불가능하다. log4jdbc 라이브러리를 이용해 sql문의 내용이 어떻게 처리되었는지 확인할 수 있다.
DAO (Data Access Object)
DB의 데이터에 접근하기 위한 객체로, 직접 DB에 접근해 데이터를 삽입, 삭제, 조회 등의 기능을 수행한다. MyBatis는 Connection Pool을 제공하기 때문에 DAO를 별도로 만들지는 않는다.
VO (Value Object)
특정한 값을 담고, 그 값 자체를 의미하는 불변 클래스이다. (Read-Only)
Entity
실제 DB의 테이블과 일대일 매핑되는 클래스로, DB의 테이블 내에 존재하는 컬럼만을 필드로 가질 수 있다. 상속을 받을 수 없고, 구현체일 수 없다.
DataSource
DataSource는 자바에서 Connection Pool을 지원하기 위한 인터페이스이다. 스프링 부트가 Connection Pool과 DataSource를 자동 설정해 빈으로 관리한다.
우선 수정 및 추가하게 될 파일을 먼저 살펴 보자. (🤗 표시 참고)
1. DB에 table 생성하기
scott 접속 후 table을 생성해 준다.
create table board (
bno number(6),
title varchar2(100 char),
content clob,
nickname varchar2(10 char),
password varchar2(60 char),
read_cnt number(6),
write_time date,
constraint board_pk_bno primary key(bno)
);
복붙은 이 코드로.......
2. 시퀀스 생성하기
create sequence board_seq;
3. src/main/resource - application.properties 파일 수정하기
server.port=8081
spring.datasource.driverClassName=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.username=scott
spring.datasource.password=1234
👾 java.sql.SQLRecoverableException 오류가 있었다.
⚒️ localhost를 ip 주소로 바꾸니 해결됐다 ^^......
4. src/main/resource - log4jdbc.log4j2.properties
, logback-spring.xml
복붙해 주기
log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
```
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern> %d{HH:mm:ss.SSS} %highlight(%-5level) %magenta(%-4relative) --- [ %thread{10} ] %cyan(%logger{40}) : %msg%n </pattern>
</encoder>
</appender>
<logger name="com.icia" level="info" />
<logger name="org.springframework" level="info" />
<logger name="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" level="trace" />
<logger name="jdbc.connection" level="warn"/>
<logger name="jdbc.resultsettable" level="info"/>
<logger name="jdbc.audit" level="warn"/>
<logger name="jdbc.sqltiming" level="warn"/>
<logger name="jdbc.resultset" level="warn"/>
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
```
5. pom.xml
에 log4jdbc 설정 추가해 주기
<dependencies>
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
6. src/main/java - com.example.demo - MyBatisConfig
클래스 생성
package com.example.demo;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@MapperScan("com.example.demo.dao")
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setTypeAliasesPackage("com.example.demo.entity");
return sqlSessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSession(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
마이바티스가 스프링 빈이 될 수 있도록 설정을 제공한다. (DAO 인터페이스의 패키지, VO 클래스의 위치)
7. src/main/java - com.example.demo.entity - Board
클래스 생성
package com.example.demo.entity;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(chain=true)
public class Board {
private Integer bno;
private String title;
private String content;
private String nickname;
private String password;
private LocalDateTime writeTime;
private Integer readCnt;
public Board(String title, String content, String nickname, String password) {
this.title = title;
this.content = content;
this.nickname = nickname;
this.password = password;
this.writeTime = LocalDateTime.now();
this.readCnt = 0;
}
}
8. src/main/java - com.example.demo.dao - BoardDao
생성
package com.example.demo.dao;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
import org.apache.ibatis.annotations.Update;
import com.example.demo.entity.Board;
public interface BoardDao {
@SelectKey(statement = "select board_seq.nextval from dual", keyProperty = "bno", resultType = Integer.class, before = true)
@Insert("insert into board(bno,title,content,nickname,password,read_cnt, write_time) values(board_seq.nextval, #{title}, #{content}, #{nickname}, #{password}, #{readCnt}, #{writeTime})")
public Integer save(Board board);
@Select("select bno,title,content,nickname,password,read_cnt as readCnt,write_time as writeTime from board where bno=#{bno}")
public Board findById(Integer bno);
@Select("select bno,title,content,nickname,password,read_cnt as readCnt,write_time as writeTime from board")
public List<Board> findAll();
@Update("update board set title=#{title}, content=#{content} where bno=#{bno} and rownum=1")
public Integer update(String title, String content, Integer bno);
// 조회수 증가
@Update("update board set read_cnt=read_cnt+1 where bno=#{bno} and rownum=1")
public Integer increaseReadCnt(Integer bno);
@Delete("delete from board where bno=#{bno} and rownum=1")
public Integer deleteById(Integer bno);
}
9. src/test/java - com.example.demo - BoardDaoTest
생성
package com.example.demo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.dao.BoardDao;
import com.example.demo.entity.Board;
@SpringBootTest
public class BoardDaoTest {
@Autowired
private BoardDao dao;
@Test
public void initTest() {
assertNotNull(dao);
}
@Test
public void findAllTest() {
assertEquals(0, dao.findAll().size());
}
@Test
public void findByIdTest() {
assertNull(dao.findById(120));
}
@Test
public void insertWithSelectKey() {
Board board = new Board("예제", "내용", "summer", "1234");
dao.save(board);
dao.findById(board.getBno());
}
@Test
public void updateTest() {
int result = dao.update("변경", "변경 내용", 1);
assertEquals(1, result);
}
@Test
public void increaseReadCntTest() {
dao.increaseReadCnt(1);
Board result = dao.findById(1);
System.out.println(result);
}
@Transactional
@Test
public void deleteByIdTest() {
dao.deleteById(1);
}
}
테스트해 본다.