게시판 목록 만들기와 페이징

 

 

1. 게시물 목록 페이징

: boardList.jsp로 띄우기 전에 우리가 원하는대로 페이징이 됐는지 TDD로 테스트 함

 

 

 

 

 

 2. LIMIT[offset,] row_count

: 테이블에 들어있는 데이터를 페이지별로 가져오려면 select문의 LIMIT을 사용해야 함

: 리스트 맨 처음부터 얼마나 떨어져 있는가 - offset

: 읽어올 row 의 수 - row_count

 

 


    public PageHandler(int totalCnt, int page, int pageSize){
        this.totalCnt = totalCnt;
        this.page=page;
        this.pageSize=pageSize;

        totalPage = (int) Math.ceil(totalCnt/(double) pageSize); // 페이지가 부족할경우 +1 해야해서 올림으로 처리
        beginPage = page/naviSize * naviSize +1;
        endPage = Math.min(beginPage + naviSize -1, totalPage); // 한 페이지를 다 채우지 못한 경우 네비와 토탈 중 작은 값을 마지막 페이지로 씀. 삼항연산자도 가능
        showPrev = beginPage !=1; //  1일땐 prev가 나올 이유가 없으므로 false로 설정함
        showNext = endPage != totalPage;

    }

    void print(){   // 페이지 네비게이션을 프린트하는 메서드
        System.out.println("page=" + page);
        System.out.print(showPrev ? "[PREV] " : "");
        for(int i=beginPage; i<=endPage;i++){
            System.out.print(i+" ");
        }
        System.out.println(showNext ? " [NEXT]" : "");
    }

이 파트를 하면서 자바 공부할때 풀었던 여러가지 문제들이 다 이런걸 위한 빌드업이었구나...라는 생각이 들었다. (물론 이것도 기초중의 생기초지만)

이걸 어떻게 실제로 써먹지??했던게 이렇게...!

 

 

@Test
public void test(){
    PageHandler ph = new PageHandler(250,1); // 페이지가 25개 나와야 함
    ph.print();
    System.out.println("ph=" + ph);
    assertTrue(ph.beginPage==1);
    assertTrue(ph.endPage==10);
}

@Test
public void test3(){
    PageHandler ph = new PageHandler(255,25); // 페이지가 26개 나와야 함
    ph.print();
    System.out.println("ph=" + ph);
    assertTrue(ph.beginPage==21);
    assertTrue(ph.endPage==26);
}

 

👉 처음과 끝부분에 대한 테스트.

👉 +1로 26페이지를 생성시 totalPage가 int끼리 나누면 올림을 해도 그대로이므로, 형변환 활용. (int)(255/(double)10)

👉 prev/next 메뉴가 잘 뜨는지 확인 필요.

MyBatis로 Dao작성하기

 

1. BoardDao의 작성

1) DB테이블 생성

2)MapperXML & DTO작성 (Mapper XML > 데이터베이스 테이블로부터 데이터 CRUD를 위한 SQL 작성)

3) DAO 인터페이스 작성

4) DAO 인터페이스 구현 & 테스트 (SQL 세션 정의)

 

 

 

2.DTO 

:Data Transfer Object (데이터를 전송할때 쓰는 객체)

: Table에서 public class BoardDto로 넘어올때 (객체에 담을때), 타입과 이름이 다 맞아야 한다.

 

관심사의 분리가 이루어짐 > 데이터를 전달할 객체 필요

 

BoardDao만들때 boadMapper.xml과 일치시켜야 하는 것

1) 아이디가 select로 동일

2) 인풋 타입이 int bno - bno로 맞아야 함

3) result 타입도 메서드의 리턴 타입과 맞아야 함

메서드호출 > sql문 호출 > 결과 리턴

 

 


 

* 자잘한 장벽들

 

 

- 강의를 따라가며 테스트를 진행하던 중에, 1번 게시글을 불러와야하는데 아직 테이블에 넣어둔 값이 없어서 insert문을 써야했으나... 아직 SQL 문법을 제대로 모르는 관계로 구글링. :>

 

select문으로 입력 확인 완

 

- 테스트 돌렸는데 또 에러가 떠서 이게 대체 무슨말인가 했다. 값을 끌어오는데 문제가 생긴것 같은데 강사님 닉네임은 왜...? 패스워드가 예스라는건 무슨말이람...?

 

 

### The error may exist in file [C:\Users\USER\IdeaProjects\ch4\target\classes\mapper\boardMapper.xml]
### The error may involve com.fastcampus.ch4.dao.BoardMapper.select
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Access denied for user 'castello'@'localhost' (using password: YES)

 

 

👉 카페에서 검색해보니 mysql 에서 테이블값을 끌어오는 root-context.xml의 문제. 여기에 username과 password를 내 아이디와 패스워드로 변경해줘야 내 테이블 안에 있는 값들이 끌려올 수있다!

 

 

boardDao = com.fastcampus.ch4.dao.BoardDaoImpl@5a9800f8
boardDto = BoardDto{bno=1, title='df', content='asdf', writer='asdf', view_cnt=0, comment_cnt=0, reg_date=Sun Feb 19 16:55:26 KST 2023}

 

▶ Dao도 잘 주입되었고, 게시물 값도 잘 가져온걸 콘솔창에서 확인할 수 있다.

 

 


 

 

3. #{}와 ${}의 차이

#은 값에 대해서만. $는 동적으로 가능.

 

 

 

4. XML의 특수 문자 처리

: XML 내의 특수문자 (<,> &,...) 는 $lt; $gt;로 변환 필요.

: 또는 특수문자가 포함된 쿼리를 <![CDATA[ 와 ]]>로 감싼다.

양쪽 중 하나를 택해서 처리

1. MyBatis란?

: SQL Mapping Framework

:자바코드와 SQL을 맵핑해줌. SQL에서 XML을 분리하여 자바코드로 해당 XML을 쓸 수 있게 되어있다. = 자바코드로부터 SQL문을 분리해서 관리.

: 매개변수 설정과 쿼리 결과를 읽어오는 코드를 제거

: 작성 코드가 줄어서 생산성 향상 & 유지보수 편리. 쉽고 심플!

 

 


 

* 인텔리지에 이때까지 만들어온 뷰/css/컨트롤/DAO 파일을 모두 세팅하고, MySQL에 게시판에 필요한 테이블 만들기

maven repository에서 mybatais관련 두개를 모두 받는다

 

pom.xml에 삽입. 스프링에서 mybatis를 사용하려면 두개 모듈이 모두 필요하다

 


 

2. SqlSesstionFactoryBean과 SqlSessionTemplate

: SqlSessionFactory - SqlSession을 생성해서 제공

: SqlSession - SQL명령을 수행하는데 필요한 메서드 제공. 

ㄴ 위 두개 모두 인터페이스이며 mybatis 모듈에서 제공함

ㄴ SqlSessionFactoryBean - SqlSessionFactory를 스프링에서 사용하기 위한 빈 (구현체, mybatis spring 제공)

ㄴ SqlSessionemplate - SQL명령을 수행하는데 필요한 메서드 제공- thread-safe (구현체, mybatis spring 제공)

 

👉 thread- safe란 :  SQLSessionTemplate를 통해서 여러 DAO를 작성하는데, SQLSessionTemplate을 공유할 수 있음 = 멀티스레드해도 안전하다는 뜻

 

 

: root-context.xml 에 빈으로 등록.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource"/>
   <property name="configLocation"  value="classpath:mybatis-config.xml"/>
   <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>//mapper위치 설정- MySQL의 XML파일이 들어있는 곳

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
   <constructor-arg ref="sqlSessionFactory"/>
</bean>

 

 

 

3. SqlSession의 메서드

  메서드 설명
DB에 영향을 준 row의 수

1) Map으로 진행 
2) User객체
insert into UserInfo values(?,?,?)

int insert(String statement)
int insert(String statement, Object parameter)
insert문 실행
insert된 행의 갯수를 반환
int delete(String statement)
int delete(String statement, Object parameter)
delete문 실행
delete된 행의 갯수를 반환
int update(String statement)
int update(String statement, Object parameter)
update문 실행
update된 행의 갯수를 반환
정보 하나 반환 T selectOne(String statement)
T selectOne(String statement, Object parameter)
하나의 행을 반환하는 select에 사용
parameter로 SQL에
binding될 값 제공
여러행을
리스트 형태로 반환
List<E> selectList(String statement)
List<E> selectList(String statment, Object parameter)
여러 행을 반환하는 select에 사용
parameter로 SQL에
binding될 값 제공
여러행을
맵 형태로 반환
Map<K,V> selectMap(String statement, String keyCol)
Map<K,V> selectMap(String statement, StringkeyCol, Object parameter)
여러 행을 반환하는
select에 사용

keyCol에 Map의 key로 사용할 컬럼 지정

 

 

 

 

 

 

0214 | 스프링의 정석 :: 3-20

서비스 계층의 분리와 Transactional(2) 테스트에서 만들어 썼던 트랜잭션 매니저를 직접 주입해서 사용하기 : 트랜잭션 매니저를 root-context에 빈으로 등록하고, 을 끌어오려고 했는데 강의화면이랑

ala-nueva.tistory.com

 

 

 

3. Required와 Requires_new - REQUIRED
: 트랜잭션의 경계가 어디냐에 따라서 롤백할때 위치가 달라진다

: REQUIRED가 있으면 새로운 트랜잭션을 만들지 않고 기존 트랜잭션을 기준으로 하나의 그룹으로 인식한다.

 

 

* REQUIRED - REQUIRED

@Transactional (propagation = Propagation.REQUIRED)
public void insertA1withTx() throws Exception{
	ADao.insert(1,100); // A1
    insertBWithTx(); // 여기서 insertBWithTx()로 넘어감
    ADao.insert(1,200); // A2
}

@Transactional (propagation = Propagation.REQUIRED) // required는 기존에 Tx가 있으면 새로 Tx를 만들지 않는다.
public void insertBWIthTx() throws Exception{
	BDao.insert(1,100);	// B1
    BDao.insert(1,200); // B2
}    
---------------------------------------------------
// Tx시작 - A1 - B1 - B2 - A2 - Tx끝 으로 중간에 트랜잭션이 생성되지않는다.
// 두개의 트랜잭션이 하나의 트랜잭션인것처럼 하나로 돌아간다.
// 중간에 오류가 생기면 롤백은 가장 최초로 돌아간다. 트랜잭션 시작점이 앞에있기때문.

 

 

*REQUIRED - REQUIRES_NEW

@Transactional (propagation = Propagation.REQUIRED)
public void insertA1withTx() throws Exception{
	ADao.insert(1,100); // A1
    insertBWithTx(); // 여기서 insertBWithTx()로 넘어감
    ADao.insert(1,200); // A2
}

@Transactional (propagation = Propagation.REQUIRES_NEW) // 새로운 트랜잭션이 필요하다는 뜻
public void insertBWIthTx() throws Exception{
	BDao.insert(1,100);	// B1
    BDao.insert(1,200); // B2
}    
---------------------------------------------------
// Tx1시작 - A1 - (Tx2시작- B1 - B2 - Tx2끝) - A2 - Tx1끝 으로 중간에 트랜잭션이 생성되지않는다.
// A2에서 에러가 났을 시 Tx1로 돌아가면서 A1은 성공이 취소되지만, Tx2는 별개의 트랜잭션이므로 커밋된 상태로 남는다.(성공상태)
// propagation속성이 롤백 위치를 정한다

 

서비스 계층의 분리와 Transactional(2)

 

 

테스트에서 만들어 썼던 트랜잭션 매니저를 직접 주입해서 사용하기

 

: 트랜잭션 매니저를 root-context에 빈으로 등록하고, <tx:annotation-driven/>을 끌어오려고 했는데 강의화면이랑 다르게 나는 create namespace declaration이 활성화가 안돼서, 결국 xml 파일 안에 직접 쳤다.

xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" // 트랜잭션 용을 그냥 써넣음

 

... 로 될줄 알았는데 계속 인식안되고 콘솔창에 에러떠서 도저히 실습을 할수가 없었다 ㅠㅠ

이거해결하느라 진도를 못나갈수가 없어서 오늘은 일단 눈으로 먼저 보기로.

 

개괄적인 내용은,

트랜잭션 매니저가 오류시에 어디까지 롤백을 하고 어디까지 출력시키느냐를 테스트하며 보는 것.

 

@Transactional(rollbackFor = Exception.class) // Exception을 rollback. rollbackFor에 지정해줘야함
@Transactional // RuntimeException, Error만 자동 rollback

 

 

2. Transactional의 속성

1) propagation - Tx의 경계(boundary)를 설정하는 방법을 지정

 - REQUIRED : Tx이 진행중이면 참여, 없으면 새로운 Tx시작 (디폴트)

- REQUIRES_NEW: Tx이 진행중이든 아니든, Tx새로 시작 (서로가 완전히 다른 Tx)

- NESTED: Tx이 진행중이면,  Tx안에 내부 Tx로 실행 (Tx 안에 sub transaction생성)

 

 

+ Recent posts