[Spring-boot]_8차 순수 JDBC, JDBC Template, JPA

2022. 3. 22. 17:50[SpringBoot]_/[Spring-boot]_인프런 강의

728x90
반응형

환경 : spring-boot

DB : H2데이터베이스

 

---

1. 순수 JDBC

과거에는 순수 JDBC로 데이터베이스 연결을 하였다고 한다.

repository 부분에 db 커넥션부터 끝까지 설정이 되어있는 특징이 있다.

DriverManager 을 사용하여 각 드라이버를 로딩 , 해제한다.

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class jdbcMemberRepository implements MemberRepository {
    private final DataSource dataSource;
    public jdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    @Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql,
                    Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1, member.getName());
            pstmt.executeUpdate();
            rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                member.setId(rs.getLong(1));
            } else {
                throw new SQLException("id 조회 실패");
            }
            return member;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findById(Long id) {
        String sql = "select * from member where id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setLong(1, id);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            } else {
                return Optional.empty();
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public List<Member> findAll() {
        String sql = "select * from member";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            List<Member> members = new ArrayList<>();
            while(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                members.add(member);
            }
            return members;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    @Override
    public Optional<Member> findByName(String name) {
        String sql = "select * from member where name = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            if(rs.next()) {
                Member member = new Member();
                member.setId(rs.getLong("id"));
                member.setName(rs.getString("name"));
                return Optional.of(member);
            }
            return Optional.empty();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);
        }
    }
    private Connection getConnection() {
        return DataSourceUtils.getConnection(dataSource);
    }
    private void close(Connection conn, PreparedStatement pstmt, ResultSet rs)
    {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (pstmt != null) {
                pstmt.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                close(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    private void close(Connection conn) throws SQLException {
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}

 

2. JDBC Template

매퍼를 따로 만들고 디비 쿼리도 jdbctemplate.query라는 함수로 작성하여 호출하여 사용할 수 있다.

순수 JDBC에서 발전한 단계이다. 좀더 직관적이고 보기 용이하다.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class JdbcTemplateMemberRepository implements MemberRepository {

    private final JdbcTemplate jdbcTemplate;

    // spring 에서 권장하는
    // 생성자가 하나 있을 경우에 autowired 를 생략할 수 있다.
    public JdbcTemplateMemberRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }
    // 자동으로 인젝션 해준다.


    @Override
    public Member save(Member member) {
        //simplejdbcinsert -> 태이블 명과 제너릭 키 만 있으면 insert문을 만들어준다.
        //그 해당키를 가지고 값을 지정해준다.

        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", member.getName());
        Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
        member.setId(key.longValue());
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(),id);
        return result.stream().findAny();


    }

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(),name);
        return result.stream().findAny();
    }

    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }

    //객체 생성 롤백.
    //사용법은 jdbc 템플릿 참고.

    //객체 매핑. 객체매핑.
    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

JDBC나 JDBC Template를 사용하면 datasource를 설정하고 Jdbc Template를 생성하여 class 를 구현해야 한다.

 

여기서 더 발전하여 xml파일로 쿼리를 따로 분간시킨 것이 MyBatis framework 이다.

 

3. JPA

ORM 의 대표적인 예로 JPA가 있다.

SQL 매핑이 아닌 object relational db Mampping 좀더 객체지향적인 방식입니다.

sql문을 따로 작성하지 않는 대신 메소드를 이용하게 됩니다. 따라서 엄청 간략해집니다.

대신에 해당 메소드를 모르면 사용하기 어렵습니다.

대표적인 인터페이스로는 hibernate가 있습니다.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;

public class JpaMemberRepository implements MemberRepository{

    private final EntityManager em;
    // springboot 가 자동으로 만들어준다. 만들어준것을 인젝션 받으면 된다.
    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }
    // 디비와 통신을 내부에서 처리한다. jpa를 사용하려면 entity 메니저를 받아야한다.

    @Override
    public Member save(Member member) {
        // jpa를 사용하려면 service 단에 @Transactional 를 붙여줘야 한다
        em.persist(member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class , id);
        return Optional.ofNullable(member);
    }

    //pk기반이 아닌것들은 쿼리를 작성해 줘야한다.

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result =em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name",name)
                .getResultList();
        return result.stream().findAny();
    }


    //객체르 대상으로 쿼리를 날린다.
    //select 는 m 객체 자체를 조회한다.
    @Override
    public List<Member> findAll() {
        List<Member> result =em.createQuery("select m from Member m", Member.class)
                .getResultList();
        return result;
    }
}

 

728x90
반응형