JDBC를 사용하는 부류가 있고, iBatis를 사용하는 부류가 있고, 하이버네이트를 사용하는 부류가 있다. 사실 이보다 더 다양한 영속화 기술들이 있지만.. 이 세 부류로 간추려 보려고 한다. 자 이들은 각각 어떻게 코딩을 하고 있을까.

public interface MemberDao {
void add(Member member);
void update(Member member);
Member get(int id);
List<Member> list();
void delete(int id);
}
이런 인터페이스를 각 부류에서 구현한다고 생각해보자.
아참, 전제는 스프링을 사용한다는 것이다.
1. JDBC
@Repository
public class MemberDaoJdbc implements MemberDao{
@Autowired SimpleJdbcTemplate jdbcTemplate;
public void setMemberMapper(RowMapper<Member> memberMapper) {
this.memberMapper = memberMapper;
}
RowMapper<Member> memberMapper = new RowMapper<Member>(){
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member();
member.setId(rs.getInt("id"));
member.setName(rs.getString("name"));
member.setJoined(rs.getDate("joined"));
return member;
}};
public void add(Member member) {
jdbcTemplate.update("insert into member(id, name, joined) values (?, ?, ?)", 
member.getId(), member.getName(), member.getJoined());
}
public void delete(int id) {
jdbcTemplate.update("delete from member where id = ?", id);
}
public Member get(int id) {
return jdbcTemplate.queryForObject("select * from member where id = ?", memberMapper, id);
}
public List<Member> list() {
return jdbcTemplate.query("select * from member", memberMapper);
}
public void update(Member member) {
jdbcTemplate.update(
"update member set name = :name, joined = :joined where id = :id", 
new BeanPropertySqlParameterSource(member));
}
}
대충 이런 코드가 된다. 스프링이 제공해주는 SimplJdbcTemplate은 멀티 쓰레드 환경에서 공유하면서 사용해도 안전한 객체이다. 따라서 빈으로 등록해놓고 주입받아서 쓴다고 한들 문제될 것이 없다. 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testContext.xml")
@Transactional
public class MemberDaoJdbcTest {
@Autowired MemberDaoJdbc memberDao;
@Test
public void di(){
assertThat(memberDao, is(notNullValue()));
}
@Test
public void crud(){
Member member = new Member();
member.setId(1);
member.setName("whiteship");
member.setJoined(new Date());
memberDao.add(member);
assertThat(memberDao.list().size(), is(1));
member.setName("기선");
memberDao.update(member);
assertThat(memberDao.get(1).getName(), is("기선"));
memberDao.delete(1);
assertThat(memberDao.list().size(), is(0));
}
}
테스트는 대충 만들었기 때문에 너그럽게 봐주시길...^_^;;;
2. iBatis
스프링에 iBatis의 SqlMapClient를 만들어 주는 팩토리빈을 등록해야 한다.
   
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="SqlMapConfig.xml" />
</bean>
아이바티스 설정 파일도 추가한다. 저 설정에 보이는 SqlMapConfig.xml이 필요하다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<sqlMap resource="sample/ibatis/Member.xml" />
</sqlMapConfig>
그 다음 Member와 관련있는 SQL을 모아둔 Member.xml 파일을 만든다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Member">
<typeAlias alias="Member" type="sample.domain.Member" />
<delete id="delete" parameterClass="int">
delete from member where id = #id#
</delete>
<insert id="add" parameterClass="Member">
insert into member (id, name, joined) values(#id#, #name#, #joined#)
  </insert>
 
<update id="update" parameterClass="Member">
update member set name = #name#, joined = #joined# where id = #id#
</update>
 
<select id="get" parameterClass="int" resultClass="Member">
select * from member where id = #id#
  </select>
<select id="list" resultClass="Member">
select * from member order by id
  </select>
</sqlMap>
XML 엘리먼트와 어트리뷰트는 별도의 학습이 필요하지 않을만큼 직관적이다.. 이제 DAO 코드를 작성하자.
@Repository
public class MemberDaoIbatis implements MemberDao {
@Autowired SqlMapClientTemplate sqlMapClientTemplate;
public void add(Member member) {
sqlMapClientTemplate.insert("add", member);
}
public void delete(int id) {
sqlMapClientTemplate.delete("delete", id);
}
public Member get(int id) {
return (Member) sqlMapClientTemplate.queryForObject("get", id);
}
@SuppressWarnings("unchecked")
public List<Member> list() {
return sqlMapClientTemplate.queryForList("list");
}
public void update(Member member) {
sqlMapClientTemplate.update("update", member);
}
}
흠 많이 짧아졌다. 그런데.. 전체 코드량은 더 많이 늘은것 같지 않은가? 코드량이 척도의 전부는 아니지만.. 왠지 작업이 줄었다가 보다는 DAO에서 할일을 설정파일로 미뤘다는 느낌이 강하다. 아참 여기서 사용한 SqlMapClientTemplate도 SimleJdbcTemplate과 마찬가지로 빈으로 등록해서 사용해도 별 지장이 없는 쓰레드-안전한 클래스기 때문에 빈으로 등록해놓고 사용했다.
3. Hibernate
이것도 역시 빈을 하나 설정해줘야한다.
 
   <!-- ============================================================= -->
    <!--  Hibernate                                                    -->
    <!-- ============================================================= -->
    <bean id="transactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager"
          p:sessionFactory-ref="sessionFactory"/>
    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="sample.domain" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>
이번건 설정이 제법 길다. 그리고 빈이 하나가 아니라 두갠데.. 그건 나중에 TransactionManager에 대한 글을 올릴 때 설명해야겠다. 아니면 그냥 토비님 책을 보기 바란다. 11장에 자세히 설명해주시고 있다. TransactionManager와 기반 기술의 관계(?)랄까나.. 암튼.. 이제 바로 DAO를 만들 수 있다. 아 아니다 일단 Member 클래스에 애노테이션 부터 추가하자.
@Entity
public class Member {
@Id
int id;
...
}
이렇게 두 군데에 각각 하나씩만 붙여주면 된다. 이제 DAO를 만들자.
@Repository
public class MemberDaoHibernate implements MemberDao{
@Autowired SessionFactory sessionFactory;
public void add(Member member) {
getSession().save(member);
}
public void delete(int id) {
getSession().createQuery("delete from Member where id = ?")
.setInteger(0, id)
.executeUpdate();
}
public Member get(int id) {
return (Member) getCriteria()
.add(Restrictions.eq("id", id))
.uniqueResult();
}
@SuppressWarnings("unchecked")
public List<Member> list() {
return getCriteria().list();
}
public void update(Member member) {
getSession().update(member);
}
private Criteria getCriteria() {
return getSession().createCriteria(Member.class);
}
private Session getSession() {
return sessionFactory.getCurrentSession();
}
}
SQL이 없다. SQL처럼 보이는 HQL(굵은 글씨)만 보인다. 이렇게만 하면 정말 DAO가 구현되는지 의심스럽겠지만 정말 구현이 끝난다. 하지만 하이버네이트 Session과 Criteria API는 약간 학습이 필요하다. 자주 쓰는 API는 몇개 안되기 떄문에 대략 1시간 정도면 익힐 수 있다.
셋 개 다 해본결과.. 난 역시 하이버네이트가 편하다.