spring-jdbc
모듈에 포함되어 있습니다.DataSource
를 의존성으로 주입해주어야 합니다.Java 개발자들이 DB를 더 쉽게 다룰 수 있도록 도와주는 오픈소스 ORM(Obect-Relational Mapping) 프레임워크
DB Query와 프로그래밍 언어 코드를 분리하여 유지보수성과 생산성을 높여줍니다.
cf. Hibernate : Java Persistence API의 스펙을 구현체로, 직접 사용 시 MyBatis와 비교해서 설정이 바뀌는 것 외에 큰 차이가 없습니다. ( = 물론 객체를 다룸으로써 SQL을 자동생성해주는 것에서 차이가 있습니다. )
유연성 : SQL 쿼리를 직접 작성 가능하여 유연하고, 동적 쿼리를 작성할 수 있습니다.
간결성 & 유지보수성 : MyBatis는 SQL 쿼리와 프로그래밍 언어 코드를 분리
하기에, 코드가 간결해지고 SQL변경 시 Mapper XML 파일만 변경해주면 되므로 유지보수에 유리합니다.
성능 : MyBatis는 캐시 기능을 제공하며, 이를 통해 DB 연산 속도를 높일 수 있습니다.
다양한 DB 지원 : 다양한 DB에 대한 연결을 지원합니다.
동적 쿼리는, 어플리케이션 런타임에 조건에 따라 SQL 쿼리를 동적으로 생성하는 것입니다.
DB의 검색 조건이나 결괏값 등이 동적으로 변화할 때 유용하게 사용됩니다.
MyBatis
에서는 동적 쿼리를 작성하기 위해 <if>
, <choose>
, <when>
, <otherwise>
, <foreach>
등의 태그를 사용할 수 있습니다.
아래와 같이 SQL을 생성 시, 프로그래밍 코드 처럼 조건문/반복문을 적용하여 동적으로 필요한 SQL을 생성하여 적용할 수 있습니다.
<!-- select 문 동적쿼리 (조건문) -->
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
SELECT
*
FROM
BLOG
WHERE
state = 'ACTIVE'
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
<!-- select 문 동적쿼리 (반복문) -->
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID IN
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
<!-- update 문 동적쿼리 (조건문) -->
<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author">
update userinfo
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
</set>
where id = #{id}
</update>
1) spring-core
Core Container 안에 Beans, Core, Context, Expression Language 모듈이 있습니다.
Core는 DI, IoC를 담당하는 스프링 내 서브 프레임워크입니다.
Spring Beans가 이를 구현하여 DI, IoC 기능을 구현합니다.
Spring Context는 Core와 Beans를 기반으로 ㅎ여, 정의된 객체에 엑세스할 수 있는 매개물을 제공합니다. Context의 핵심 인터페이스는 ApplicationContext
입니다.
Spring SpEL을 통해 간단한 문법을 통해 앱에 필요한 데이터나 설정 값을 가져올 수 있습니다.
2) spring-jdbc
Spring Framework에서 제공하는 JDBC 모듈
Java EE가 제공하는 JDBC 자체를 사용하는 것보다 좀 더 편리하게 사용 가능 ( ex. jdbcTempalte )
3) mybatis
4) mybatis-spring
MyBatis는 JDBC를 통해 DB 연결을 진행합니다.
이를 위한 DB 연결 정보를 설정하고, 해당 설정은 DataSource라는 커넥션 풀 제공 클래스에서 진행합니다.
이러한 DataSource
를 스프링 빈으로 설정하기 위해, servlet-context.xml
에 다음과 같은 설정을 추가합니다.
<!-- servlet-context.xml -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="it"/>
<property name="password" value="0000"/>
</bean>
mybatis-context.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>
<!-- db.properties -->
<properties resource="oracle.properties" />
<!-- DBCP -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" /> <!-- MANAGED -->
<dataSource type="POOLED">
<property name="driver" value="${lec.driver}" />
<property name="url" value="${lec.url}" />
<property name="username" value="${lec.username}" />
<property name="password" value="${lec.userpw}" />
</dataSource>
</environment>
</environments>
<!-- mapper -->
<mappers>
<mapper resource="board-map-lec08.xml" />
</mappers>
<!-- TypeAliases -->
<typeAliases>
<typeAlias alias="userVO" type="com.lec08.dao.BoardVO" />
</typeAliases>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
</bean>
</configuration>
위의 설정을 해석하면 다음과 같습니다.
`
oracle.properties
라는 이름의 프로퍼티 파일을 참조합니다.<typeAlias alias="userVO" type="com.lec08.dao.BoardVO"/>
BoardVO
라는 클래스 타입을 userVO
라는 이름으로 별칭을 준다.<environments... <transactionManager type="JDBC" />
MyBatis Mapper
는 DB 쿼리 <-> 자바 메서드를 매핑하는 역할
데이터베이스에 접근하기 위한 SQL 쿼리를 작성하고, 이를 실행하는 자바 메서드 정의
XML의 SQL쿼리와 자바 메서드를 연결할 자바 인터페이스 = Mapper
@Mapper // 등록
// 작성
public interface UserMapper {
User getUserById(int id);
void insertUser(User user);
void updateUser(User user);
void deleteUser(int id);
}
Mapper 속 메서드 별로 SQL 쿼리문을 작성해줍니다.
메서드 이름 = XML에서 SQL의 id
와 같습니다.
<!-- user-mapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<!-- 다른 메서드들에 대한 쿼리도 추가 가능 -->
</mapper>
@Service
public class UserService {
private final UserMApper userMapper;
public UserService(UserMapper userMapper){
this.userMapper = userMapper;
}
// User Find
public User getUserById(int id){
return userMApper.getUserById(id);
}
// User Registration
public void insertUser(User user){
userMapper.insertUser(user);
}
}
JNDI
를 통해 순수 자바 코드로 Connection Pool을 사용하려 했지만 실패했습니다.context.xml
에 자원을 등록하고, 우리 웹 어플리케이션의 web.xml
에서 ( = Spring 사용 전, Java EE 기술만 사용 ) Servlet을 통해 리소스를 당겨서 쓸 수 있었습니다.Tomcat
이 바라볼 수 있는 webapp/WEB-INF
경로 안에 context.xml
에 해당 DBCP 설정을 추가해주었습니다./src/main/resources
입니다.EL
표현으로 프로퍼티를 등록하여, DataSource를 가져올 수 있었습니다.Spring
을 사용하기에 해당 커넥션 풀에 대한 설정은 DispatcherServlet에게 설정을 넘겨주는, servlet-context.xml
에 설정해주면 되었습니다.getConnection()
)JDBC 커넥션을 얻는 방법은 여러가지가 있습니다.
기존의 App은 JDBC의 추상에 의존하여 커넥션을 가져왔지만 ( JDBC DriverManager ), 커넥션 풀이라는 강력한 기술을 사용하기 위해 Java는 javax.sql.DataSource
라는 추상화된 데이터 소스 인터페이스를 제공한다.
( 단, DriverManager로 새로운 커넥션을 생성하는 코드는 DataSource를 사용하지 않는다. 위의 그림의 세번째의 그림은 좀 잘못된 그림 같다 )
DataSource
를 통해 DataBase Connection Pool
에 접근 가능합니다. 이 때, 해당 DataSource
를 TransactionManager
가 관리해줍니다.MyBatis의 가장 핵심적인 객체입니다.
SQLSessionFactory는 SQLSession을 만들어냅니다.
개발에서 SQLSession을 통해 DB Connection을 생성하고, 원하는 SQL을 전달하고 결과를 리턴받습니다.
<!-- SQLSessionFactory -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
mybatis-spring
라이브러리 클래스의 SqlSessionFactoryBean
을 통해 SQLSessionFactory를 스프링 빈으로 등록합니다.@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception{
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource());
return (SqlSessionFactory) sqlSessionFactory.getObject();
}
@Configuration + @Bean
)CLOB : DB에 Character형태의 문자열 집합 저장, 각 컬럼당 4GB 차지
BLOB : DB에 Binary형태의 데이터 집합 저장, 각 컬럼당 4GB 차지
게시글 작성 같은 경우, 게시글 데이터가 긴 경우 CONTENT1, CONTENT2, CONTENT3.. 이렇게 나눔
-----------------------------------------------------------------
스프링 DAO(Data Access Object)
- 데이터 액세스 계층의 코드 작성을 단순화하고 일관된 데이터 접근 방법을 제공
- JDBC, ORM(Hibernate, Mybatis, ...), JPA 등의 다양한 데이터 접근 기술 제공
- JdbcTemplate(단순) 제공
- ORM(Object Relation Mapping) : 자바객체-테이블 매핑
- JPA(Java Persistence API) : ORM을 위한 표준 인터페이스
-----------------------------------------------------------------
---------------------------------
DBCP(DB Connection Pool)
---------------------------------
데이터베이스와 연결된 커넥션을 미리 만들어 Pool속에 저장
필요할 때 풀에서 커넥션을 가져다 사용
다 쓴 후에 다시 풀에 반환
---------------------------------
pom.xml에 <repositories> 추가
---------------------------------
<properties>
~~~ 생략 ~~~
</properties>
<repositories>
<repository>
<id>oracle-repo</id>
<url>https://maven.oracle.com</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<dependencies>
~~~ 생략 ~~~
</dependencies>
---------------------------------
pom.xml에 <dependency> 추가
---------------------------------
<!-- DBCP -->
<!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-pool/commons-pool -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<!-- mapper 관련 -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- Mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-ibatis</artifactId>
<version>2.0.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-sqlmap</artifactId>
<version>2.3.4.726</version>
</dependency>
------------------------------------------------
1. DataSource 설정 방법 :: Java/Web 프로젝트인 경우
- /webapp/WEB-INF/web.xml
- tomcat/conf/context.xml
------------------------------------------------
tomcat/conf/context.xml
<!-- 데이터 리소스 설정 -->
<Resource name="MY_tomcat_ds"
auth="Container"
type="javax.sql.DataSource"
maxTotal="30"
maxIdle="5"
maxWaitMillis="10000"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:XE"
username="it"
password="0000"/>
/webapp/WEB-INF/web.xml
<!-- 데이터 리소스 설정 이름 "MY_tomcat_ds"이 일치해야 함 -->
:: context.xml <Resource name="MY_tomcat_ds">
:: web.xml <res-ref-name>MY_tomcat_ds</res-ref-name>
<resource-ref>
<description>My DataSource TEST</description>
<res-ref-name>MY_tomcat_ds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
------------------------------------------------ ***** 해당 방식 사용
2. DataSource 설정 방법 :: Spring 프로젝트인 경우
- /webapp/WEB-INF/web.xml
- /webapp/WEB-INF/spring/lec08-servlet-context.xml
- /src/main/resources/oracle.properties
------------------------------------------------
oracle.properties 파일 설정 정보
lec.driver=oracle.jdbc.driver.OracleDriver
lec.url=jdbc:oracle:thin:@localhost:1521:XE
lec.username=it
lec.userpw=0000
lec08-servlet-context.xml
<!-- datasource : 프로퍼티 파일을 사용한 형태 -->
<context:property-placeholder location="classpath:oracle.properties" />
<bean id="MY_tomcat_ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${lec.driver}" />
<property name="url" value="${lec.url}" />
<property name="username" value="${lec.username}" />
<property name="password" value="${lec.userpw}" />
</bean>
<!-- datasource : 하드코딩한 형태
<bean id="MY_tomcat_ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" />
<property name="username" value="it" />
<property name="password" value="0000" />
</bean> -->
-----------------------------------------------------------------
실행
-----------------------------------------------------------------
CtxCallTest.java
http:localhost:{port}/{context_path}/board_list
#################################################################################
----------------------------------------------------------
*** JdbcTemplate
----------------------------------------------------------
public Post updatePost(Long id, Post updateParam) {
String sql = "UPDATE post SET title=?, content=?, MODIFIED_DATE=? where id=?";
template.update(sql,
updateParam.getTitle(),
updateParam.getContent(),
updateParam.getModifiedDate(),
id);
}
----------------------------------------------------------
*** JPA
----------------------------------------------------------
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
//slect * from user where uname = ?
List<User> findByName(String name);
}
----------------------------------------------------------
*** ORM (Mybatis)
----------------------------------------------------------
package com.lec09.orm;
public class MemberVO {
private int id;
private String name;
private String email;
private Date regdate;
// getters and setters
}
package com.lec09.orm.mapper;
public interface MemberMapper {
void myMethodName__insert(MemberVO mvo);
}
'MemberMapper.xml'
<mapper namespace="com.lec09.mapper.MemberMapper">
<insert id="myMethodName__insert" parameterType="com.lec09.orm.MemberVO">
INSERT INTO member (name, email) VALUES (#{name}, #{email})
</insert>
</mapper>
@Service
public class MemberServiceImpl {
@Autowired
private MemberMapper memberMapper;
public void addMember(MemberVO prmVO) {
memberMapper.myMethodName__insert(prmVO);
}
}
----------------------------------------------------------
*** ORM (Hibernate)
----------------------------------------------------------
@Data
@Table(name = "Member")
@Entity
public class Board implements java.io.Serializable {
@Id
@Column(name = "seq")
@GenenratedValue(strategy = GenerationType.AUTO)
private int seq;
@Column(name = "title", nullable = false)
private String title;
public Board() {}
public Board(String title) {
this.title = title;
}
}
hibernate-board-mapper.xml
@Temporal(TemporalType.TIME)
private Date playTime;
@Temporal(TemporalType.DATE)
private Date added;
<hibernate-mapping>
<class name="com.lec09.orm.BoardVO" table="board">
<meta attribute="class-description">
Represents a single playable track in the music database.
@author Jum Elliott (with help from Hibernate)
</meta>
<id name="seq" type="int" column="seq">
<meta attribute="scope-set">protected</meta>
<generataor class="native">
</id>
<property name="title" type="string" not-null="true"/>
</class>
</hibernate-mapping>
public class ServiceImpl {
SessionFactory sessionFactory = HibernateUtil5.getSessionFactory();
SessionFactory session = sessionFactory.openSession();
Transaction tx = null;
public void svcMethodd() {
try {
tx = session.beginTransaction();
// 쿼리 작성
tx.commit();
} catch (RuntimeException e) {
if (tx != null) tx.rollback();
} finally {
session.close();
}
sessionFactory.close();
}
}
----------------------------------------------------------