2021-05-09 TIL

이창호·2022년 5월 9일
0

SpringBoot Part4 - JPA 시작

목표

  • 스프링 웹 어플리케이션의 데이터 저장 계층에 대해 이해한다.
  • JPA에 대해 이해한다.
  • 스프링데이터 JPA에 대해 이해한다.
  • 스프링 데이터 JPA를 이용한 REST API 서버를 구축한다.

JPA(Java Persistence Applcation)이란?

설명에 앞서 JDBC에 대해 간략히 알아보자.

  • JAVA App <-> JDBC API <-> JDBC Driver <-> (RDB)
  • 아래 코드는 Connection 획득&반납, Statement 질의&반납, Resultset을 통해 결과 받아오기를 하는 코드다.
@Slf4j
public class SQLTest {
    static final String JDBC_DRIVER = "org.h2.Driver";
    static final String DB_URL = "jdbc:h2:~/test";
    static final String USER = "sa";
    static final String PASS = "";

    static final String DROP_TABLE_SQL = "DROP TABLE customers IF EXISTS";
    static final String CREATE_TABLE_SQL = "CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))";
    static final String INSERT_SQL = "INSERT INTO customers(id, first_name, last_name) VALUES(1, 'changho', 'lee')";

    @Test
    void sql_sample() {
        try {
            Class.forName(JDBC_DRIVER);
            Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);
            log.info("GET CONNECTION");

            Statement statement = connection.createStatement();
            statement.executeUpdate(DROP_TABLE_SQL);
            statement.executeUpdate(CREATE_TABLE_SQL);
            log.info("CREATED TABLE");

            statement.executeUpdate(INSERT_SQL);
            log.info("INSERTED CUSTOMER INFORMATION");

            ResultSet resultSet = statement.executeQuery("SELECT * FROM customers WHERE id = 1");

            while (resultSet.next()) {
                String fullName = resultSet.getString("first_name") + " " + resultSet.getString("last_name");
                log.info("CUSTOMER FULL_NAME: {}", fullName);
            }

            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

jdbc를 사용하면 더 간편하다.

  • 이제 connection, statement를 코드로 가져오고 반납하지 않아도 된다. 자동으로 해준다.
@Slf4j
@SpringBootTest
public class JDBCTest {
    static final String DROP_TABLE_SQL = "DROP TABLE customers IF EXISTS";
    static final String CREATE_TABLE_SQL = "CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))";
    static final String INSERT_SQL = "INSERT INTO customers(id, first_name, last_name) VALUES(1, 'changho', 'lee')";

    static final String SELECT_SQL = "SELECT * FROM customers WHERE id = 1";

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void jdbcTemplate_sample() {
        jdbcTemplate.update(DROP_TABLE_SQL);
        jdbcTemplate.update(CREATE_TABLE_SQL);
        log.info("CREATED TABLE USING JDBC TEMPLATE");

        jdbcTemplate.update(INSERT_SQL);
        log.info("INSERTED CUSTOMER INFORMATION USING JDBC TEMPLATE");

        String fullName = jdbcTemplate.queryForObject(
                SELECT_SQL,
                (resultSet, i) -> resultSet.getString("first_name") + " "
                        + resultSet.getString("last_name")
        );
        log.info("CUSTOMER FULL_NAME: {}", fullName);
    }
}

jdbc 대신 쿼리 매퍼인 mybatis를 써보자

  • jdbc의 경우 resultSet을 통해 쿼리결과를 매핑했다.
  • mybatis는 객체에 자동으로 매핑 할 수 있다. (type-aliases-package 사용)

왜 JPA가 필요할까?

  • sql -> JDBC -> mybatis 순으로 좀 더 편리하게 DB에 접근 할 수 있게 되었다.
  • 여기서 mybatis보다 더 간편해진 방법이 바로 JPA다.

JPA 설정

  • JPA는 인터페이스다. 그렇기 때문에 구현체를 정해야 한다. 요즘엔 hibernate를 많이 쓴다하니 hibernate로 해본다.
  • DataSource 설정으로 어떤 DB를 사용할지 결정한다.
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:~/test");
        dataSource.setUsername("sa");
        dataSource.setPassword("");

        return dataSource;
    }
  • JpaVendorAdapter로 JPA 인터페이스의 구현체 중 어떤 것을 사용할지 결정한다.
    @Bean
    public JpaVendorAdapter jpaVendorAdapter(JpaProperties jpaProperties) {
        AbstractJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
        jpaVendorAdapter.setShowSql(jpaProperties.isShowSql());
        jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
        jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl());

        return jpaVendorAdapter;
    }
  • EntityManagerFactoryBean으로 RDB의 Table과 매핑된 Entity들을 관리하도록 설정한다.
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource,
                                                                           JpaVendorAdapter jpaVendorAdapter,
                                                                           JpaProperties jpaProperties) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.exmaple.jpasettingpractice");
        em.setJpaVendorAdapter(jpaVendorAdapter);

        Properties properties = new Properties();
        properties.putAll(jpaProperties.getProperties());
        em.setJpaProperties(properties);
        return em;
    }
  • TransactionManager로 트랜잭션을 관리하도록 설정한다.
    @Bean
    public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean.getObject());

        return transactionManager;
    }
profile
이타적인 기회주의자

0개의 댓글