Spring JDBC Template Pattern day68

stan·2023년 8월 16일
0

Spring

목록 보기
14/22

"코드 복사하기"를 했다면,
"더 줄일수는 없을까?" (모듈화, 캡슐화,...)를 고민 해봐야 한다

1) 기존 JDBC 코드 작업중...
JDBC로직이 반복되네? (DAO의 selectAll,selectOne,insert,update,delete)
더 줄일수는 없을까?
=> 유사하거나 반복되는 알고리즘을 캡슐화하여 재사용하는 패턴
Template 패턴
코딩 순서가 정해져있는(정형화된) 기술에서 특히 유용하게 활용됨
JDBC, 트랜잭션, Mybatis, JPA, ...(db류에서 활용)
db랑 관련되어있는 애들이 하는 행동이 똑같음
※ 패턴
: MVC, 팩토리(객체생성코드를 캡슐화 하는거; new를 숨겨두는거==결합도가 낮아짐),
싱글톤 (메모리성능향상; 동일한 타입 객체 1개만 남김)

pom.xml

	   <!-- Spring JDBC -->
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
         <version>${org.springframework-version}</version>
      </dependency>
      <!-- DBCP -->
      <dependency>
         <groupId>commons-dbcp</groupId>
         <artifactId>commons-dbcp</artifactId>
         <version>1.4</version>
      </dependency>

2) "JDBCTemplate" 클래스를 적용하여 DAO 구성하기
- jar파일 필요
라이브러리 2개 추가
-> 라이브러리 관리는 pom.xml이 함

3) pom.xml에 jar파일을 2개 추가 DBCP (database connection pool)
= connection들을 대신 관리해주는 주체(객체)
커넥션들을 더이상 직접 관리 안함

4) conn을 DB로부터 확보하는것에서부터 시작
5) JDBCTemplate 클래스는 DataSource 객체를 통해 conn을 확보 및 관리함
6) DataSource 객체를 생성해야함
스프링 컨테이너가 생성하도록 해야함
<bean> 등록을 해야함 (new를 bean을 통해서 함)

 이거 꺼내쓰려고 jar넣은거임 
\<bean class="org.apache.commons.dbcp.BasicDataSource"></bean>

커넥션 너무 많이 하면 톰캣기반이라 (apache라고 써있음) 연결해제가 제대로 되지 않으면 서버실행이 동작 안할수도있다
꺼냈으면 닫아 : 
\<bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource" destroy-method="close"></bean\>

7) DataSource 객체 <bean> 등록시,
setter DI을 함께 설정해야함
\<property name = "driverClassName" value ="oracle,jdbc.driver.OracleDriver"/>
String이라서 여기 value라는 속성이 들어감 (reference대신)

\<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
\<property name="username" value="KIM"/>
\<property name="password" value="1234"/>


BoardDAO2 CUD

CUD는 update (executeUpdate같은거)
R에서는 VO를 받아내야 함
jdbcTemplate.queryForObject를 써야함
객체를 받겠다

public boolean insert(BoardVO bVO) {
		System.out.println("BoardDAO2 로그 insert() 메서드");
		//쿼리문 ??? 데로 인자를 나열
		int rs=jdbcTemplate.update(insert,bVO.getTitle(),bVO.getContent(),bVO.getWriter()); //update의 반환타입이 int임
		if(rs<=0) {
			return false;
		}
		return true;
	}
	public boolean update(BoardVO bVO) {
		System.out.println("BoardDAO2 로그 update() 메서드");
		int rs=0;
		if(bVO.getSearchCondition()!=null && bVO.getSearchCondition().equals("CNT")) {
			rs=jdbcTemplate.update(update_CNT,bVO.getBid());
		}
		else {
			rs=jdbcTemplate.update(update,bVO.getTitle(),bVO.getContent(),bVO.getBid());
		}
		if(rs<=0) {
			return false;
		}
		return true;
	}
	public boolean delete(BoardVO bVO) {
		System.out.println("BoardDAO2 로그 update() 메서드");
		int rs=jdbcTemplate.update(delete,bVO.getBid());
		if(rs<=0) {
			return false;
		}
		return true;
	}      

BoardDAO2 R

update 에서는 인자개수가 정해져있지 않았기때문에 그냥 나열 하면 됬음

queryForObject()는 update와 달리 메서드시그니쳐가정해져있음. 인자가 3개로 고정되어있음
SQL을 실행할때에 필요한 INPUT과, << 이때 input은 배열의 형태로
SQL을 실행완료한후의 OUTPUT을 각각 지정해야함 << output은 객체의 형태로 지정 해 줄것임


	public BoardVO selectOne(BoardVO bVO) {
		System.out.println("BoardDAO2 로그 selectOne() 메서드");
		//jdbcTemplate.queryForObject(selectOne,INPUT,OUTPUT);
		Object[] args= { bVO.getBid() };
									//sql	  sql에 들어갈거  sql수행결과
		//jdbcTemplate.queryForObject(selectOne,args, new BoardRowMapper());
		//이게 bVO기때문에 
		return jdbcTemplate.queryForObject(selectOne,args, new BoardRowMapper());
	}
	public List<BoardVO> selectAll(BoardVO bVO) {
		System.out.println("BoardDAO 로그 selectAll() 메서드");
		//jdbcTemplate.query(selectAll, INPUT, OUTPUT);
		//?물음표가 없으니 input은 생략 (들어갈게 없다)
		//ouput: BoardVO를 만들어야 한다
		//jdbcTemplate.query(selectAll, new BoardRowMapper()); 아웃풋이 list기때문에 return으로 
		if(bVO.getSearchCondition()==null || bVO.getSearchCondition().isEmpty()) {
			return jdbcTemplate.query(selectAll, new BoardRowMapper());
		}
		else if(bVO.getSearchCondition().equals("TITLE")) {
			Object[] args= { bVO.getTitle() };
			return jdbcTemplate.query(selectAll_TITLE, args, new BoardRowMapper());
		}
		else if(bVO.getSearchCondition().equals("WRITER")) {
			Object[] args= { bVO.getWriter() };
			return jdbcTemplate.query(selectAll_WRITER, args, new BoardRowMapper());
		}
		return null;
	}

selectOne을 수행할때 int,String,등등 뭐가 들어갈지 모르는데 배열을 만들어야함
그래서 최상위클래스 Object[]로 되어있는 배열을 만들거임

객체로 반환 해야해:
new BoardRowMapper << 이 이름을 가진 클래스가 있어야 함
제네릭을 반드시 함께 사용

BoardDAO2 RowMapper

class BoardRowMapper implements RowMapper<BoardVO> {
	@Override
	public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
		BoardVO data=new BoardVO();
		data.setBid(rs.getInt("BID"));
		data.setCnt(rs.getInt("CNT"));
		data.setContent(rs.getString("CONTENT"));
		data.setTitle(rs.getString("TITLE"));
		data.setWriter(rs.getString("WRITER"));
		return data;
	}
}

RowMapper 인터페이스는

  • 어떤 ResultSet(DB)을 어떤 자바객체(POJO)와 매핑해야하는지 강제해주는 역할
    -> DB와 매핑되는 자바객체를 "VO"라고 함
  • VO를 알아야하기때문에 <>제네릭을 사용합니다; <>설정필수

selectAll은 그냥 query() 여러개를 봐야하니까

jdbcTemplate.query(selectAll, new BoardRowMapper());

Q)이거 어떻게 여러개 나올줄 알아요?
A) int rowNum이 내부적으로 암
rowNum 출력 해보면 됨

8) DAO의 클래스의 멤버변수로 JdbcTemplate을 등록
->DI(의존주입)
무언갈 멤버변수로 삼았으면 의존주입 해줘야함

9) DI할 객체가 없음... jdbcTemplate을 new를 안했음 아직
new를 해줘야함; 등록해야함 : applicationContext.xml(루트컨테이너)
이때 dataSource 객체를 setter주입함

	<!-- JdbcTemplate에 DI할 객체 생성 -->
	<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<aop:aspectj-autoproxy />
  • databaseConnectionPool을 활용하는 dataSource를 활용하는 jdbcTemplate을 활용하는 DAO임

10) DAO2를 사용해보자!
DAO는 Service가 씀

@Autowired
//private BoardDAO boardDAO;
private BoardDAO2 boardDAO;

service 레이어를 사용해주고 있기때문에 BoardDAO명만 바꾸면 됨

그리고 이제 BoardDAO2가 서비스레이어를 사용함
Service의 멤버변수 변경
@Repository로 Service에 의존주입할 객체 생성


인제 이거 제거 해도 됨
private final String selectAll="SELECT * FROM BOARD ORDER BY BID DESC";

BoardDAO2

public class BoardDAO2 { //이름 1,2,3,4 라고 붙이는 일은 실무에서 없음
	@Autowired
	private JdbcTemplate jdbcTemplate; //더이상 Conn이나 preparedStatement를 직접 받지 않음

	private final String insert="INSERT INTO BOARD (BID,TITLE,CONTENT,WRITER) VALUES((SELECT NVL(MAX(BID),0)+1 FROM BOARD),?,?,?)";
	private final String selectOne="SELECT * FROM BOARD WHERE BID=?"; // getOne
	private final String selectAll_TITLE="SELECT * FROM BOARD WHERE TITLE LIKE '%'||?||'%' ORDER BY BID DESC";
	private final String selectAll_WRITER="SELECT * FROM BOARD WHERE WRITER LIKE '%'||?||'%' ORDER BY BID DESC";
	private final String update="UPDATE BOARD SET TITLE=?,CONTENT=? WHERE BID=?";
	private final String update_CNT="UPDATE BOARD SET CNT=CNT+1 WHERE BID=?";
	private final String delete="DELETE FROM BOARD WHERE BID=?";

왜냐하면 이 두가지는 똑같다 :

selectAll = SELECT * FROM BOARD ORDER BY BID DESC;

selectAll_TITLE = SELECT * FROM BOARD WHERE TITLE LIKE '%%' ORDER BY BID DESC;

  • '%%' (아무것도 없는것)이건 모두 가지고 있기 때문에

%%사이의 "">>> 아무것도없는 것
이 값을 다 갖고있어서용

  • '%%'는 모든 값과 일치하므로 WHERE 절은 모든 기록을 선택하게 됩니다.
    그러므로 두 번째 쿼리는 사실상 첫 번째 쿼리와 동일한 결과를 생성합니다.

BoardDAO2 selectAll

	public List<BoardVO> selectAll(BoardVO bVO) {
		System.out.println("BoardDAO2 로그 selectAll() 메서드");
		Object[] args= { bVO.getSearchContent() };
		if(bVO.getSearchCondition().equals("TITLE")) {
			return jdbcTemplate.query(selectAll_TITLE, args , new BoardRowMapper());
		}
		else { // else if(bVO.getSearchCondition().equals("WRITER"))
			return jdbcTemplate.query(selectAll_WRITER, args , new BoardRowMapper());
		}
	}
profile
이진 입니다

0개의 댓글