[6/29 TIL] SPRING(Embedded Database, NamedParameterJdbcTemplate, DataAccessException, 트랜잭션)

yumyeonghan·2023년 6월 30일
0

🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃

Embedded Database

  • 애플리케이션과 함께 동작하는 데이터베이스 시스템
  • 사용자가 별도의 데이터베이스 서버를 설치하거나 설정할 필요가 없음
  • 외부 환경이 테스트의 성공 여부를 결정하는것을 방지
  • 테스트는 실제 데이터베이스 말고, 임베디드 데이터베이스를 사용해서 작성하는 것이 CI 관점에서 좋음
  • H2를 사용하거나, 테스트 로직에 특정 데이터베이스 방언이 많다면 Embedded Mysql같이 특정 데이터베이스의 임베디드 버전을 사용

NamedParameterJdbcTemplate

  • 기존 JdbcTemplate의 플레이스 홀더("?")를 이름 기반으로 설정 가능
  • 바인딩 순서를 신경쓰지 않아도 되는 이름 기반의 파라미터 바인딩 지원

예시 코드

public class NamedParameterJDBCTemplateCrudExample {

    public static void main(String[] args) {
        // JDBC 드라이버 로드
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return;
        }

        // 데이터베이스 연결 설정
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/sampledb");
        dataSource.setUsername("username");
        dataSource.setPassword("password");

        // NamedParameterJdbcTemplate 생성
        NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);

        // 데이터 삽입
        UUID id = UUID.randomUUID();
        String name = "유명한";
        int age = 25;
        insertData(jdbcTemplate, id, name, age);

        // 데이터 검색 (단일 행)
        UUID searchId = UUID.fromString("your-id");
        UserData userData = retrieveDataById(jdbcTemplate, searchId);
        if (userData != null) {
            System.out.println("Data Found:");
            System.out.println("ID: " + userData.getId() + ", Name: " + userData.getName() + ", Age: " + userData.getAge());
        } else {
            System.out.println("Data Not Found");
        }

        // 데이터 검색
        System.out.println("All Data:");
        retrieveData(jdbcTemplate);

        // 데이터 갱신
        String newName = "김명한";
        int newAge = 30;
        updateData(jdbcTemplate, id, newName, newAge);

        // 데이터 삭제
        deleteData(jdbcTemplate, id);

        // 최종 데이터 검색
        retrieveData(jdbcTemplate);
    }

    // 데이터 삽입
    private static void insertData(NamedParameterJdbcTemplate jdbcTemplate, UUID id, String name, int age) {
        String sql = "INSERT INTO users (id, name, age) VALUES (:id, :name, :age)";
        Map<String, Object> params = new HashMap<>();
        params.put("id", toBytes(id));
        params.put("name", name);
        params.put("age", age);
        jdbcTemplate.update(sql, params);
    }

    // 데이터 검색 (단일 행)
    private static UserData retrieveDataById(NamedParameterJdbcTemplate jdbcTemplate, UUID id) {
        String sql = "SELECT * FROM users WHERE id = :id";
        Map<String, Object> params = new HashMap<>();
        params.put("id", toBytes(id));
        UserData userData = jdbcTemplate.queryForObject(sql, params, (ResultSet rs, int rowNum) -> {
            UUID userId = toUUID(rs.getBytes("id"));
            String name = rs.getString("name");
            int age = rs.getInt("age");
            return new UserData(userId, name, age);
        });
        return userData;
    }

    // 데이터 검색
    private static void retrieveData(NamedParameterJdbcTemplate jdbcTemplate) {
        String sql = "SELECT * FROM users";
        jdbcTemplate.query(sql, (ResultSet rs) -> {
            while (rs.next()) {
                UUID id = toUUID(rs.getBytes("id"));
                String name = rs.getString("name");
                int age = rs.getInt("age");
            }
        });
    }

    // 데이터 갱신
    private static void updateData(NamedParameterJdbcTemplate jdbcTemplate, UUID id, String name, int age) {
        String sql = "UPDATE users SET name = :name, age = :age WHERE id = :id";
        Map<String, Object> params = new HashMap<>();
        params.put("id", toBytes(id));
        params.put("name", name);
        params.put("age", age);
        jdbcTemplate.update(sql, params);
    }

    // 데이터 삭제
    private static void deleteData(NamedParameterJdbcTemplate jdbcTemplate, UUID id) {
        String sql = "DELETE FROM users WHERE id = :id";
        Map<String, Object> params = new HashMap<>();
        params.put("id", toBytes(id));
        jdbcTemplate.update(sql, params);
    }

    // byte[]을 UUID로 변환
    private static UUID toUUID(byte[] bytes) {
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        return new UUID(byteBuffer.getLong(), byteBuffer.getLong());
    }

    // UUID를 byte[]로 변환
    private static byte[] toBytes(UUID uuid) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);
        byteBuffer.putLong(uuid.getMostSignificantBits());
        byteBuffer.putLong(uuid.getLeastSignificantBits());
        return byteBuffer.array();
    }
}

DataAccessException

그림 출처

  • 스프링 프레임워크에서 제공하며, 데이터 접근중에 발생할 수 있는 예외이고, RuntimeException을 상속 받음
  • 데이터베이스 액세스 작업 중 발생하는 SQLException을 래핑하여 전달해서 데이터베이스와 관련된 예외 처리를 표준화 함
  • 다양한 데이터 액세스 기술(JDBC, JPA, Hibernate)이나 DBMS(Oracle, MySQL)에서 발생하는 예외들을 DataAccessException 하위 예외로 두어 쉽게 처리할 수 있음

트랜잭션

그림 출처

  • 원자성 (Atomicity): 모든 작업은 전부 수행되거나 전혀 수행되지 않아야 함
  • 일관성 (Consistency): 트랜잭션 전후에 데이터베이스가 일관된 상태를 유지해야 함
  • 격리성 (Isolation): 하나의 트랜잭션은 다른 트랜잭션의 연산에 영향을 미치지 않아야 함
  • 지속성 (Durability): 트랜잭션을 성공적으로 완료한 후에는 해당 트랜잭션에 의한 모든 변경이 영구적으로 저장되어야 함

예시 코드

	public void insertDataWithTransaction(UUID id, String name, int age) {
    	String sql = "INSERT INTO users (id, name, age) VALUES (:id, :name, :age)";
        Map<String, Object> params = new HashMap<>();
        params.put("id", toBytes(id));
        params.put("name", name);
        params.put("age", age);

        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

        try {
            jdbcTemplate.update(sql, params);
            transactionManager.commit(transactionStatus);
        } catch (Exception ex) {
            transactionManager.rollback(transactionStatus);
            throw ex;
        }
    }
  • 직접 트랜잭션 매니저를 통해 트랜잭션 정의
  • try-catch 블록을 사용하여 트랜잭션의 성공 여부에 따라 커밋 또는 롤백을 수행
profile
웹 개발에 관심 있습니다.

0개의 댓글