여러 명이 Model(S-D-DB)에 접속하기 위해서는 여러 스레드가 있어야 하고, 스레드마다 커넥션을 가지고 접속을 하게 된다. 그러나 DB도 받을 수 있는 커넥션의 한계가 있다.
🌳→🌿 DB를 복제하여 분산처리
해결방안으로 DB 분산처리가 있는데 분산처리의 단점이 있다.
단점 1 : 분산처리시 DB끼리 동기화가 되어야 한다.
단점 2 : 특정 DB에서 데이터 변경시(10000->20000) 데이터를 응답하고 그 뒤 동기화가 실행되는데 동기화가 완료되기도 전에 새로운 요청이 와 미처 동기화되지 못한 데이터값 (10000)을 응답할 수 있다. (동기화시 시간차 발생)
👉 이러한 문제를 관리하기 위해 DB끼리의 트랜젝션 처리를 해야 한다.
Spring 프레임워크에서는 요청을 받을 때마다 conn을 만드는 동기적 방식(conn이 끝없이 만들어진다)이 아닌, 미리 DB가 처리할 수 있는 수의 conn 객체를 IOC 컨테이너에 만들어 놓는 방식을 사용한다.
conn 객체를 넘겨주는 시점 : 서버(아파치)의 8080port를 통과하고 문지기 필터를 통과한 뒤 Spring의 DS 진입 전에 이루어진다.
conn 객체 반환 시점 : DS를 빠져나올 때 이루어진다. (물론 Controller나 Service에서 conn 연결이 끊어지도록 관리할 수도 있다.)
Pooling 기술
클라이언트는 항상 두 가지 메모리 저장소 = 자신만의 두 가지 객체를 가진다 : request, conn
conn은 DS를 빠져나올 때 객체를 돌려주고 request는 아파치 서버에서 빠져나갈 때까지 존재한다는 차이점이 있지만, 사용자가 conn 사용이 끝나면 conn을 삭제하지 않고 반환받아 보관하여 재사용하고 request 또한 객체를 미리 생성해놓고 서버를 빠져나가기 전까지 계속 데이터를 유지하여 역할을 수행한다.
이렇게 Pool에 미리 객체를 만들어 놓고 사용하는 기법이라 하여 Pooling 기술이라 부른다.
dependencies {
implementation group: 'javax.servlet', name: 'jstl', version: '1.2'
implementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.65'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
라이브러리에 MyBattis 라이브러리를 추가한다.
'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
package site.metacoding.red.config;
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;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
@MapperScan(basePackages = "site.metacoding.red.domain")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
config 설정파일(여기서는 MyBatisConfig파일)은 Mapping된 xml파일을 읽어 Java object로 new 설정해주는 파일이다.
@Configuration
Java의 설정 파일 위에 붙는 어노테이션.
@Configuration이 아닌 @Controller를 붙이면 DS가 관리하게 되므로 @Configuration을 사용해야 한다.
@MapperScan
Mapping xml파일을 스캔하는 어노테이션. basePackages의 경로에 있는 DAO와 xml의 쿼리문을 Mapping해주기 때문에 basePackages의 경로는 실제 DAO가 있는 패키지로 설정해주어야 한다.classpath : Java에서 classpath는 라이브러리를 관리하는 모든 폴더를 다 찾는 역할을 한다. Spring에서 classpath는 (src/main/resources)이다.
classpath:mapper/*.xml
으로 설정되었으므로 라이브러리를 관리하는 폴더 중 mapper에서 확장자가 xml인 파일만 스캔한다.
DAO가 interface로 만들어 질 경우, interface이므로 new를 하지 못한다. 이 때 MyBatis문법으로 구현한다.
MyBatisConfig는 xml파일의 쿼리문을 구현할 객체를 만들어 new 한다. 이 때, 이 객체는 xml파일의 namespace경로에서 부모가 될 DAO를 읽고 이 부모를 상속한다. 이 객체 안에 xml의 쿼리문이 들어가는 것이다.
new된 DAO구현체는 IOC 컨테이너에 띄워진다.
요약 : xml을 보고 interface인 DAO의 구현체를 쿼리문에 따라 자동으로 만들어 new 하며 IOC 컨테이너에 띄운다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="site.metacoding.red.domain.users.UsersDao">
<select id="update">
UPDATE users
SET username = #{username},
password = #{password},
email = #{email}
WHERE id = #{id}
</select>
<insert id="insert">
INSERT INTO users(id, username, password, email, createdAt)
VALUES(users_seq.nextval, #{username}, #{password}, #{email}, sysdate)
</insert>
<select id="findById" resultType="site.metacoding.red.domain.users.Users">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="findAll" resultType="site.metacoding.red.domain.users.Users">
SELECT * FROM users ORDER BY id DESC
</select>
<delete id="delete">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
namespace="경로"
MyBatisConfig에서 부모로 상속할 DAO의 경로를 적는 곳이자 xml 쿼리문이 적용되는 DAO의 경로를 적는 곳.
id=" "
: DAO의 함수명과 xml 쿼리문의 id명이 같아야 한다.
resultType=" "
: return 타입과 비슷하다고 보면 된다. 경로로 지정된 곳에 값을 넣어준다. (Users users에 값을 넣어준다.)