3 분 소요

스프링에서 MyBatis를 활용하기 위해 Maven 프로젝트의 pom.xml에 다음의 내용이 먼저 세팅되어 있어야 한다.

<!-- database -->
	<dependency>
	    <groupId>com.oracle.database.jdbc</groupId>
	    <artifactId>ojdbc10</artifactId>
	    <version>19.22.0.0</version>
	</dependency>
	
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis</artifactId>
	    <version>3.5.16</version>
	</dependency>

	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis-spring</artifactId>
	    <version>3.0.3</version>
	</dependency>
	
	<dependency>
	    <groupId>com.zaxxer</groupId>
	    <artifactId>HikariCP</artifactId>
	    <version>5.1.0</version>
	</dependency>

📖 Junit을 통해 드라이버 연결 테스트하기

[project] → [Build Path] → [Configure Build Path] → [Libraries] → [Class path에서 Add Library] → [Junit 선택] 이 과정을 통해 Junit 사용 환경을 설정해준다.

pom.xml

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.2</version>
  <scope>test</scope>
</dependency>

<!-- 스프링과 junit 연결을 위한 라이브러리 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.3.34</version>
  <scope>test</scope>
</dependency>

maven 프로젝트를 빌드하면 자동으로 이 내용이 포함되어 있는데, 버전은 본인이 원하는 것을 지정해주면 된다.

src 폴더 밑에 test/java 폴더를 만들어주고, 그 안에서 테스트 코드를 작성한다.

OracleConnectionTest.java

public class OracleConnectionTest {
	@Test
	public void ConnectionTest() throws Exception {
		Class.forName("oracle.jdbc.driver.OracleDriver");
		String url = "jdbc:oracle:thin:@localhost:1521:xe";
		Connection conn = DriverManager.getConnection(url, "user1", "1111");

		System.out.println(conn);
		conn.close();
	}
}

@Test 어노테이션을 통해 테스트할 메서드를 지정할 수 있다.

DBCP를 HikariCP로 준비하고, dataSource가 잘 저장되어 있는지 확인해보자.

root-config.xml

hikariCP dataSource에 대한 빈 객체를 생성해준다.

<bean class="com.zaxxer.hikari.HikariConfig" id="hikariConfig">
	<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
	<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
	<property name="username" value="user1" />
	<property name="password" value="1111" />
</bean>

<bean class="com.zaxxer.hikari.HikariDataSource" id="dataSource">
	<constructor-arg ref="hikariConfig"></constructor-arg>
</bean>

DataSourceTest.java

위에서 본 OracleConnectionTest와는 다르게 스프링 환경에 있는 객체를 확인하는 것이므로 Junit과 Spring을 연결해주는 코드가 필요하다. 이를 어노테이션으로 해결할 수 있다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/*.xml"})
public class DataSourceTest {
	
	@Autowired
	private DataSource dataSource;
	
	@Test
	public void testHikari() throws SQLException {
		Connection conn = dataSource.getConnection();
		assertNotNull(conn);
	}
}

📘 MyBatis

MyBatis는 자바 개발자들이데이터베이스를 쉽게 다룰 수 있도록 도와주는 오픈 소스 ORM(Object-Relational Mapping) 프레임워크이다. MyBatis는데이터베이스 쿼리를 프로그래밍 언어 코드를 분리하여유지보수성과 생산성을 높이는 것이 목적이다.

📖 준비 사항

  1. 해당 라이브러리

    글의 처음에서 다룬 대로 pom.xml에 mybatis와 mybatis-spring 라이브러리를 작성해준다.

  2. Configuration 파일

    ex) mybatis-config.xml

     <?xml version="1.0" encoding="UTF-8"?>
     <!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "https://mybatis.org/dtd/mybatis-3-config.dtd">
     <configuration>
       <typeAliases>
        <typeAlias type="com.mysite.domain.Board" alias="Board"/>
       </typeAliases>
     </configuration>
    
  3. Mapper 파일

    ex) board-mapper.xml

     <?xml version="1.0" encoding="UTF-8"?>
     <!DOCTYPE mapper
       PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
       "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
          
     <mapper namespace="com.mysite.boardMapper">
       <insert id="write" parameterType="Board">
       	insert into simpleBoard(bNo, bTitle, bWriter, bContent)
       	values(seq_bno.nextVal, #{bTitle}, #{bWriter}, #{bContent})
       </insert>
          
       <select id="getList" resultType="Board">
       	select * from simpleBoard order by bNo desc
       </select>
          
       <select id="getBoard" parameterType="int" resultType="Board">
       	select * from simpleBoard where bNo = #{bNo}
       </select>
     </mapper>
    

    Configuration file에 따로 Alias를 지정해주지 않으면, Type을 적을 때 클래스가 저장된 패키지 경로까지 다 적어주어야 한다.

📖 필요한 객체

  1. SqlSessionFactory : SqlSession을 생성해서 제공
  2. SqlSession : Sql 명령을 수행하는데 필요한 메서드 제공
  3. SqlSessionFactoryBean : SqlSessionFactory를 Spring에서 사용하기 위한 Bean 객체
  4. SqlSessionTemplate : Sql 명령을 수행하는데 필요한 메서드 제공(Thread-Safe)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource"></property>
	<property name="configLocation" value="classpath:/mybatis-config.xml"></property>
	<property name="mapperLocations" value="classpath:/*mapper.xml"></property>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" 
	destroy-method="clearCache">
	<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MyBatis 또한 DI pattern을 이용한다. SqlSessionFactoryBean 클래스의 setter 메서드에 위에서 준비한 Configuration 파일과 Mapper 파일들을 넘겨주면 된다.

destroy-method 속성을 지정함으로써 따로 close 메서드를 호출하지 않아도 되게 할 수 있다.

⚠️ 왜 경로에 classpath: pseudo protocol을 붙여줘야 할까?

설정 파일과 mapper 파일 둘 다 이미 classpath에 등록되어 있는 파일들(src/main/resources/ 경로에 위치)이라 그냥 파일 이름만 경로에 넣어주면 될 줄 알았는데, 그렇지 않았다. SqlSessionFactoryBean 클래스의 setter 메서드들은 절대 경로로 경로 값들이 다뤄지도록 코드가 짜여있다는 것을 뒤늦게 알 수 있었다. 참고로, classpath: 를 경로에 추가해 주면 classpath에 있는 모든 경로를 뒤져서 찾아준다.

public void setConfigLocation(Resource configLocation) {
  this.configLocation = configLocation;
}

SqlSessionFactoryBean.java 파일을 보면 경로 값을 받는 변수의 타입이 Resource로 되어 있는 것을 볼 수 있다. Resource.java 파일을 보면 인터페이스임을 알 수 있고, ClassPathResource 클래스에서 해당 인터페이스를 구현했음을 알 수 있었다. 이 클래스의 생성자는 다음과 같다.

public ClassPathResource(String path, @Nullable Class<?> clazz) {
	Assert.notNull(path, "Path must not be null");
	this.path = StringUtils.cleanPath(path);

	String absolutePath = this.path;
	if (clazz != null && !absolutePath.startsWith("/")) {
		absolutePath = ClassUtils.classPackageAsResourcePath(clazz) + "/" + absolutePath;
	}
	else if (absolutePath.startsWith("/")) {
		absolutePath = absolutePath.substring(1);
	}
	this.absolutePath = absolutePath;

	this.classLoader = null;
	this.clazz = clazz;
}

대충 봐서는 코드의 내용을 이해하기 어렵지만, 절대 경로로 값을 받는다는 것 정도는 대충 알 수 있었다…

참고


https://enspring.tistory.com/601

https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java

https://github.com/mybatis/spring/blob/master/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java

댓글남기기