pom에 추가해야할 내용들이 몇개 있음 spring-jdbc , spring-tx ,mybatis-spring
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- =============== MyBatis =============== -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
트랜잭션 관리자는 모두 X/Open XA표준에 따라 만들게 되어있다. (spring-tx)
(XA 표준이 뭘까) -> 참고: https://heni.tistory.com/10
-알아본 점
(1)hikariDataSource를 이용한 방법과 MyBatis DataSource를 이용한 방법 이 있다.
둘다 빈에 올리면 DataSource에 어떤것을 주입해야하는지 에러난다.
primary속성으로 기본을 정해줌 or Resource어노테이션으로 정한다.
(2)sqlSessionFactory 객체에 어떤 데이터소스에서 주입할지 정해서 주입하자
(3)mybatis의 namespaces에 추가해주기
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean
id="hikariConfig"
class="com.zaxxer.hikari.HikariConfig">
<description>HikariCP Configuration</description>
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/>
<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@atp20191201_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"/>
<property name="username" value="HR"/>
<property name="password" value="Oracle12345678"/>
<property name="maximumPoolSize" value="10"/>
<property name="minimumIdle" value="2"/>
<property name="idleTimeout" value="10000"/>
<property name="connectionTimeout" value="3000"/>
<property name="connectionTestQuery" value="SELECT 1 FROM dual"/>
<property name="dataSourceJNDI" value="jdbc/HikariCP"/>
<property name="poolName" value="*** HikariDataSource ***"/>
</bean>
<bean
primary="true"
id="hikariDataSource"
class="com.zaxxer.hikari.HikariDataSource"
destroy-method="close">
<description>HikariCP DataSource</description>
<constructor-arg ref="hikariConfig"/>
</bean>
<!-- ==== MyBatis's DataSource Configuration === -->
<bean
primary="false"
id="pooledDataSource"
class="org.apache.ibatis.datasource.pooled.PooledDataSource"
destroy-method="forceCloseAll">
<description>MyBatis Pooled Data Source</description>
<property name="driver" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"/>
<property name="url" value="jdbc:log4jdbc:oracle:thin:@atp20191201_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"/>
<property name="username" value="HR"/>
<property name="password" value="Oracle12345678"/>
<property name="poolMaximumActiveConnections" value="5"/>
<property name="poolMaximumIdleConnections" value="2"/>
<property name="poolPingEnabled" value="true"/>
<property name="poolPingQuery" value="SELECT 1 FROM dual"/>
<property name="loginTimeout" value="1"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="hikariDataSource" />
<!-- <property name="dataSource" ref="pooledDataSource" /> -->
<property name="configLocation" value="classpath:mybatis-config.xml" />
<!-- 위에 마이바티스 설정파일의 위치를 설정하였으면,
아래의 속성은 절대-반드시-네버-에버 설정하지 말것! -->
<!-- <property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml" /> -->
</bean>
<mybatis-spring:scan base-package="org.zerock.myapp.mapper" />
</beans>
mybatis-spring library에 SqlSessionFactoryBean 이라는 이름의 클래스가 들어있다.
스프링의 BeanContainer에 Bean으로 등록 가능하도록 해주는 Bean 클래스이다.
-mapper Interface
1. mybatis 학습시에 다 배웠던 것이다. 부족한 부분은 해당 블로그 글에서 보충
public interface EmployeesMapper {
@Select("SELECT * FROM employees WHERE employee_id > 0")
public abstract List<EmployeeVO> getAllEmployees();
// 마이바티스는, 아래의 "자동실행규칙"을 따르는 경우,
// (1) Mapper Interface의 패키지와 동일한 폴더구조를 생성하라!
// (2) 위(1)의 폴더아래에, Mapper Interface의 타입명과 동일한 이름의 Mapper XML파일을 생성하라!
// (3) 위(2)에서 생성한 Mapper XML 파일의 namespace 속성의 값은, Mapper Interface의 FQCN으로 지정하라!!
// (4) 위(2)에서 생성한 Mapper XML 파일에 등록할 SQL문장의 id 속성의 값은,
// Mapper Interface의 추상메소드 이름과 동일하게 지으라!
// 더이상 namespace + "." + sqlId 로 사상된 SQL문장을 실행시키지 못하는 환경에서,
// 자동으로 수행시킬 SQL문장을 결정하여 실행시킬 수 있습니다.
public abstract List<String> getAllEmployeesNames();
} // end class
- ~~mapper.xml
<?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="FirstMapper">
<select
id="DQL1"
resultType="org.zerock.myapp.domain.EmployeeVO">
SELECT *
FROM employees
WHERE employee_id > #{empid}
</select>
</mapper>
<?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="SecondMapper">
<select
id="DQL2"
resultType="org.zerock.myapp.domain.EmployeeVO">
SELECT *
FROM employees
WHERE email LIKE #{email} AND salary > #{salary}
</select>
</mapper>
- mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<mappers>
<mapper resource="mappers/FirstMapper.xml"/>
<mapper resource="mappers/SecondMapper.xml"/>
</mappers>
</configuration>
(VO는 스키마 보고 알아서 하기)
-완전 기본예제1
@Log4j2
@NoArgsConstructor
// For JUnit 4
//@RunWith(SpringRunner.class)
//@RunWith(SpringJUnit4ClassRunner.class)
// For JUnit 5
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations= "file:src/main/webapp/WEB-INF/spring/root-context.xml")
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class HikariDataSourceTests {
// @Resource(type=javax.sql.DataSource.class)
// @Resource
// @Inject
// @Autowired
@Setter(onMethod_= { @Resource(type=HikariDataSource.class) })
// @Setter(onMethod_= { @Resource })
// @Setter(onMethod_= { @Inject })
// @Setter(onMethod_= { @Autowired }) // @since java 8 and above
private DataSource dataSource;
// 1. 선처리(Pre-processing) 작업:
// 필드에 원하는 타입의 빈(Bean)객체가 잘 주입(DI)되었는지 확인
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
// 필드에 의존성 객체가 잘 주입되었는지 확인
// Objects.requireNonNull(this.dataSource); // 1st. method
// assert this.dataSource != null; // 2nd. method
assertNotNull(this.dataSource); // 3rd. method
log.info("\t+ this.dataSource: {}", this.dataSource);
} // beforeAll
@Test
void dummyTest() {;;}
@Test
@Order(1)
@DisplayName("1. javax.sql.DataSource.getConnection() method test.")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testGetConnection() throws SQLException {
log.trace("testGetConnection() invoked.");
// in jdbc, Connection => PreparedStatement => ResultSet : when closing, ResultSet > PreparedStatement > Connection
// try (Connection; PreparedStatement; ResultSet;) { ... }
// AutoCloseable 한 자원객체임 : 다 쓰고 나면 반드시 close(자원해제) 해줘야 함!!! (***)
// DataSource.getConnection() 메소드 => 무한정 기다림(blocking)
// @Cleanup
Connection conn = this.dataSource.getConnection(); // Connection Pool로부터 빌린 Connection
try (conn) {
Objects.requireNonNull(conn);
log.info("\t+ conn: {}, type: {}", conn, conn.getClass().getName());
} // try-with-resources
} // testGetConnection
}// end class
-예제2 (그냥 mybatis 부분 복습한다는 느낌)
@Log4j2
@NoArgsConstructor
// For JUnit 4
//@RunWith(SpringRunner.class)
//@RunWith(SpringJUnit4ClassRunner.class)
// For JUnit 5
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations="file:src/main/webapp/WEB-INF/spring/root-context.xml")
@TestInstance(Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SqlSessionFactoryTests {
@Setter(onMethod_= {@Autowired})
private SqlSessionFactory sqlSessionFactory;
@BeforeAll
void beforeAll() {
log.trace("beforeAll() invoked.");
assertNotNull(this.sqlSessionFactory);
log.info("\t+ this.sqlSessionFactory: {}", this.sqlSessionFactory);
} // beforeAll
// @Disabled
@Test
@Order(1)
@DisplayName("1. testFirstMapperXML")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testFirstMapperXML() {
log.trace("testFirstMapperXML() invoked.");
@Cleanup
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// Mapped Statement 를 결정하기 위한 요소 2가지:
String namespace = "FirstMapper";
String sqlId = "DQL1";
String sql = namespace + "." + sqlId;
sqlSession.<EmployeeVO>selectList(sql, 130).forEach(log::info);
} // testFirstMapperXML
// @Disabled
@Test
@Order(2)
@DisplayName("2. testSecondMapperXML")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testSecondMapperXML() {
log.trace("testSecondMapperXML() invoked.");
@Cleanup
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// Mapped Statement 를 결정하기 위한 요소 2가지:
String namespace = "SecondMapper";
String sqlId = "DQL2";
String sql = namespace + "." + sqlId;
// SQL문장의 바인드변수들에 줄 값들을,
// (1) Map객체(key가 바인드변수명)를 만들어 주거나
// (2) 자바빈즈객체(property가 바인드변수명)를 만들어 줘야 한다. <------ ***
@Data
class Parameters { // 로컬클래스(지역클래스)
private String email;
private Double salary;
} // end class
Parameters params = new Parameters();
params.setEmail("%A%");
params.setSalary(7000.0);
sqlSession.<EmployeeVO>selectList(sql, params).forEach(log::info);
} // testSecondMapperXML
// @Disabled
@Test
@Order(3)
@DisplayName("3. testGetAllEmployeesInEmployeesMapper")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testGetAllEmployeesInEmployeesMapper() {
log.trace("testGetAllEmployeesInEmployeesMapper() invoked.");
SqlSession sqlSession = this.sqlSessionFactory.openSession();
try (sqlSession) {
EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);
Objects.requireNonNull(mapper);
log.info("\t+ mapper: {}, type: {}", mapper, mapper.getClass().getName());
mapper.getAllEmployees().forEach(log::info);
} // try-with-resources
} // testGetAllEmployeesInEmployeesMapper
// @Disabled
@Test
@Order(4)
@DisplayName("4. testGetAllEmployeesNames")
@Timeout(value=3, unit=TimeUnit.SECONDS)
void testGetAllEmployeesNames() {
log.trace("testGetAllEmployeesNames() invoked.");
SqlSession sqlSession = this.sqlSessionFactory.openSession();
try (sqlSession) {
EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);
assertNotNull(mapper);
log.info("\t+ mapper: {}, type: {}", mapper, mapper.getClass().getName());
mapper.getAllEmployeesNames().forEach(log::info);
} // try-with-resources
} // testGetAllEmployeesNames
} // end class
수업 내용중 중요한것은 context.xml 파일 설정과 어노테이션을 통해 DataSource를 사용 하기위해 주입되는 객체를 중점으로 보면되겠다.