일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- BOF
- 정보처리기사 실기
- webhacking
- hacking
- wargame
- PWN
- Buffer Overflow
- 정보보안기사
- 네트워크
- 정보보안기사 실기
- 운영체제
- Payload
- Lord of BOF
- 웹해킹
- 해킹
- Operating System
- 워게임
- Shell code
- Pwnable.kr
- webhacking.kr
- Spring
- system hacking
- pwnable
- stack overflow
- LOB
- OS
- Spring Framework
- Spring MVC
- System
- SQL
- Today
- Total
DongDD's IT
[Spring] Datasource, Spring JDBC, Transaction 본문
[Spring] Datasource, Spring JDBC, Transaction
Data Source
- Application이 database에 접근하기 위한 추상화된 연결 방식
종류
1. Application module이 제공하는 Datasource
- 서드파티가 제공하는 datasource나 스프링 프레임워크가 제공하는 datasource 사용
- DB 정보를 Application이 관리
2. Application Server가 제공하는 Datasource
- JNDI를 이용해 가져와 사용하는 방식
- DB 정보를 Server가 관리(Application과 분리)
JNDI(Java Naming and Directory Interface) : Java가 제공하는 directory, naming service
3. 내장 database를 사용하는 Datasource
- Application 구동 시, datasource 설정과 생성이 자동으로 일어남
Data Source 설정
1. Spring에서 제공하는 DriverManagerDataSource 설정
- Data source 설정 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Configuration @PropertySource("classpath:aa.properties") public class driver { @Value("${database.url}") private String url; @Value("${database.username}") private String username; @Value("${driverClassName}") private String driverClassName; @Bean public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); return dataSource; } } | cs |
- properties 파일에 사용할 정보 저장
- @Value()를 통해 가져옴
- 사용할 datasource를 생성하고 각 정보 설정 후 반환
2. JNDI 설정
- JNDI 설정 코드
1 2 3 4 5 6 7 8 | @Configuration public class jndi { @Bean public DataSource dataSource() throws NamingException { JndiTemplate jndiTemplate = new JndiTemplate(); return jndiTemplate.lookup("java:resource", DataSource.class); } } | cs |
- lookup을 통해 "resource"명을 가진 resource를 찾아옴
3. 내장형 데이터베이스 사용
- Embedded DB 설정 코드
1 2 3 4 5 6 7 8 9 10 11 | @Configuration public class Embedded { @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.DERBY) .setScriptEncoding("UTF-8") .addScripts("/resource/test.sql") .build(); } } | cs |
- Type 설정
- Encoding 설정
- 실행 시, 실행 될 Script 추가
스프링 JDBC
- 반복적으로 수행되는 JDBC 처리를 Spring framework가 제공
→ Connection
→ SQL 문 실행
→ 예외 처리
JdbcTemplate
- SQL로 데이터베이스에 접근할 수 있게 도와주는 클래스
- DI로 주입받아 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Jdbc @Autowired private JdbcTemplate jdbcTemplate; private int id = 10; @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } public String execSql() { String sql = "SELECT * FROM table where id=?"; return jdbcTemplate.queryForObject(sql, String.class, id); } } | cs |
- 사용할 datasource를 지정
- sql문을 작성하고 "?"를 이용해 bind
- queryForObject 메소드를 사용하여 sql문을 실행하여 db에 접속
DAO(Data Access Object)
- 실제로 DB에 접근하기 위한 객체
- DB의 데이터를 조회, 조작하는 기능
- SQL문을 사용하여 DB의 CRUD 기능 제공
1. DAO 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Component public class TestDao { @Autowired JdbcTemplate jdbcTemplate; public String getName() { return jdbcTemplate.queryForObject("SELECT name FROM table", String.class); } public Integer getId() { return jdbcTemplate.queryForObject("SELECT id from table",Integer.class); } } | cs |
- @Component로 TestDao 생성
- method에 JdbcTemplate를 이용하여 쿼리문 실행
2. DAO 사용
1 2 3 4 5 6 7 8 9 | @Autowired TestDao testDao; public void test() { testDao.getId(); testDao.getName(); } | cs |
- DI 컨테이너의 주입을 받은 후, 사용
NamedParameterJdbcTemplate
- JdbcTemplate에서 "?"를 이용해 bind했던 것을 map 또는 SqlParameterSource을 이용해 bind할 수 있음
1. NamedParameterJdbcTemplate DAO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Component public class TestDao1 { @Autowired NamedParameterJdbcTemplate namedParameterJdbcTemplate; public String getName(Integer id) { // MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); // mapSqlParameterSource.addValue("id",id); Map<String, Object> map = new HashMap<>(); map.put("id",id); return namedParameterJdbcTemplate .queryForObject("SELECT name FROM table WHERE id=:id",map,String.class); } } | cs |
- :변수명 으로 bind할 지점 생성
- map으로 원하는 값을 넣어 sql문 실행
2. SQL 결과 확인
1) 결과 row가 한개인 경우
- Map<String, Object>를 이용하여 조회
2) 결과 row가 여러개인 경우
- List<Map<String,Object>>를 이용하여 조회
3) 결과 row가 없는 경우
- queryForObject 메소드 : 빈 List 반환
- 나머지 메소드 : EmptyResultDataAccessException 처리
3. CRUD
1 2 3 | public void setName(String name) { return namedParameterJdbcTemplate.update(sql, param); } | cs |
- insert, update, delete 모두 update 메소드를 사용
POJO로 결과 받기
- POJO(Plain Old Java Object) : 특정 규약, 환경에 영향 받지 않고 다른 객체에 영향(상속, 구현체)을 받지 않는 value성 오브젝트
1. POJO 변환 인터페이스
1) RowMapper
- Result set을 순차적으로 읽으며 POJO 객체로 변환
- 행에 대한 커서 제어는 Spring framework가 수행
- RowMapper implementation
1 2 3 4 5 6 7 8 9 | public class TestRowMapper implements RowMapper<TestPOJO> { @Override public TestPOJO mapRow(ResultSet rs, int rowNum) throws SQLException { return TestPOJO.builder() .user_id(rs.getInt("user_id")) .user_name(rs.getString("user_name")) .build(); } } | cs |
- DB에서 가져온 값을 TestPOJO 객체를 생성하여 넣고 반환
- DAO class 생성
1 2 3 4 5 6 7 8 9 10 11 | @Component public class RowMapperDao { @Autowired JdbcTemplate jdbcTemplate; public TestPOJO getTestPojo(Integer id) { TestRowMapper testRowMapper = new TestRowMapper(); return jdbcTemplate.queryForObject("SELECT * FROM table WHERE user_id=?", testRowMapper,id); } } | cs |
2) ResultSetExtractor
- Result set을 자유롭게 제어하면서 원하는 POJO 형태로 매핑
- ResultSetExtractor Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class TestResultSetExtractor implements ResultSetExtractor<List<TestPOJO>> { @Override public List<TestPOJO> extractData(ResultSet resultSet) throws SQLException { Map<Integer, TestPOJO> map = new LinkedHashMap<>(); TestPOJO testPOJO; while(resultSet.next()) { Integer userId = resultSet.getInt("user_id"); testPOJO = map.get(userId); if(testPOJO == null) { map.put(userId, TestPOJO.builder() .user_id(userId) .user_name(resultSet.getString("user_name")) .build()); } } if(map.size() == 0){ throw new EmptyResultDataAccessException(1); } return new ArrayList<>(map.values()); } } | cs |
- DB에서 가져온 각 row를 List로 변환하여 반환
- DAO class 생성
1 2 3 4 5 6 7 8 9 10 11 | @Component public class ResultSetExtractorDao { @Autowired JdbcTemplate jdbcTemplate; public List<TestPOJO> getTestPojoAll() { TestResultSetExtractor testResultSetExtractor = new TestResultSetExtractor(); return jdbcTemplate.query("SELECT * FROM table",testResultSetExtractor); } } | cs |
3) RowCallbackHandler
- Result set을 반환하기 위한 것이 아니라 다른 처리를 하고 싶을 때 사용
- 반환 값이 없음
- RowCallbackHandler Implementation
1 2 3 4 5 6 7 8 | public class TestRowCallbackHandler implements RowCallbackHandler { @Override public void processRow(ResultSet rs) throws SQLException { // Process // no return; } } | cs |
- 반환 값이 없고 특정 process를 처리할 때 사용
- RowCallbackHandler DAO
1 2 3 4 5 6 7 8 9 10 11 12 | @Component public class RowCallbackHandlerDao { @Autowired JdbcTemplate jdbcTemplate; public void processTestPojo() { TestRowCallbackHandler testRowCallbackHandler = new TestRowCallbackHandler(); jdbcTemplate.query("SELECT * FROM table",testRowCallbackHandler); } } | cs |
데이터 일괄 처리
1. Batch process
- 대량의 데이터 조작 시, SQL문을 각각 실행하지 안하고 배치 형태로 모아서 실행
- batchUpdate 메소드 사용(JdbcTemplate)
2. Stored Procedure
- stored procedure를 사용
- call, execute 메소드 사용(JdbcTemplate)
Transaction
Transcation 관리
- 선언적(declarative) 방법 : annotation을 사용해 관리
- 프로그램적(programmatic), 명시적 방법 : commit, rollback method를 사용해 관리
Spring Transaction
1. 선언적(declarative) 트랜잭션
- 정해진 룰에 따라 트랜잭션 제어
- 트랜잭션의 시작과 커밋, 롤백등의 처리를 비즈니스 로직에 삽입할 필요가 없음
- 메소드에 @Transactional을 추가하여 메소드 시작, 종료 시점에 트랝개션 관리
- 메소드 안에서 예외 발생 시, 자동으로 롤백
1 2 3 4 5 6 7 8 9 10 | @Service public class TestPojoService { @Autowired private RowMapperDao rowMapperDao; @Transactional(readOnly = true) public TestPOJO getTestPojo(Integer userId) { return rowMapperDao.getTestPojo(userId); } } | cs |
- 클래스에 @Transactional을 추가하면 클래스 내부에 모두 적용
- 메소드에 @Transactional을 추가하면 해당 메소드에 적용
- 트랜잭션 관리자 설정 파일
1 2 3 4 5 6 7 8 9 10 11 | @Configuration @EnableTransactionManagement public class TransactionConfig { @Autowired DataSource dataSource; @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(); } } | cs |
2. 명시적(프로그램적) 트랜잭션
- 소스 코드에 직접 롤백, 커밋을 기술하여 사용하는 방법
- 메소드 보다 더 작은 단위를 제어하거나 선언적으로 처리하기 힘든 부분을 제어할 때 사용
1) PlatformTransactionManager
- 트랜잭션 직접 제어
- TransactionDefinition과 TransactionStatus 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @Service public class Programmatic { @Autowired PlatformTransactionManager platformTransactionManager; @Autowired RowMapperDao rowMapperDao; public void insertTestPojo(TestPOJO testPOJO) { DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition(); defaultTransactionDefinition.setName("testTransaction"); defaultTransactionDefinition.setReadOnly(false); defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); defaultTransactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT); TransactionStatus transactionStatus = platformTransactionManager .getTransaction(defaultTransactionDefinition); try{ //insert code } catch (Exception e) { platformTransactionManager.rollback(transactionStatus); throw new DataAccessException("error", e){}; } platformTransactionManager.commit(transactionStatus); } } | cs |
- setName : 트랜잭션 이름 설정
- setReadOnly : ReadOnly 설정
- setPropagationBehavior : 트랜잭션의 전파 방식 설정
- setIsolationLevel : 트랜잭션의 격리 수준 설정
2) TransactionTemplate
- PlatformTransactionManager 보다 구조적으로 트랜잭션 제어 기술
- TransactionCallback 인터페이스가 제공하는 메소드에 트랜잭션 제어 작업 구현
- TransactionTemplate의 execute 메소드에 인수로 전송
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Service public class Programmatic2 { @Autowired TransactionTemplate transactionTemplate; @Autowired RowMapperDao rowMapperDao; public void insertTestPojo(TestPOJO testPOJO) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { //insert code } }); } } | cs |
Transaction Isolation
- 동시에 실행되는 트랜잭션들이 서로에게 영향을 끼치지 않도록 함
- @Transactional(isolation = Isolation.DEFAULT) 또는 TransctionDefinition의 setIsolationLevel 사용
1. Isolation 문제점
1) Dirty Read
- 다른 트랜잭션이 변경한 커밋되지 않은 데이터를 조회하여 가지고 있을 때, 다른 트랜잭션에서 커밋 완료하지 않고 종료하는 경우, 잘못된 데이터를 가지고 있게 됨
2) Unrepeatable Read
- 데이터를 조회하고 있는 상황에서 다른 트랜잭션이 해당 데이터를 변경/삭제하고 커밋하게 되면 잘못된 데이터를 찾게 됨
3) Phantom Read
- 특정 조건을 이용하여 데이터를 조회하고 있을 때, 다른 트랜잭션이 해당 조건에 맞지 않게 데이터를 변경하는 경우에 다시 조회를 하면 해당 데이터를 찾을 수 없게 됨
2. 종류
1) DEFAULT
- Database의 격리 수준을 이용
2) READ_COMMITED
- 커밋되지 않은 변경 데이터를 다른 트랜잭션에서 참조할 수 없음
3) READ_UNCOMMITED
- 커밋되지 않은 변경 데이터를 다른 트랜잭션에서 참조할 수 있음
- 변경 데이터가 롤백된 경우, 롤백 전 데이터를 조회하게 됨
4) REPEATABLE_READ
- 데이터를 조회하고 있는 도중에 다른 트랜잭션이 해당 데이터를 변경하여도 영향받지 않음
- 다른 트랜잭션에서 update 불가능
5) SERIALIZABLE
- 해당 데이터를 조회하고 있는 도중에 다른 트랜잭션이 해당 데이터를 조작할 수 없음
- 다른 트랜잭션에서 insert, update, delete 불가능
Transaction Propagation
- 트랜잭션의 경계에서 트랜잭션에 참여하는 방법 결정
- 새로운 트랜잭션의 시작이나 기존 트랜잭션에 참여하는 것을 결정
- ex) 트랜잭션 관리 메소드에서 다른 트랜잭션 관리 메소드를 실행하는 경우
- @Transactional(propagation = Propagation.MANDATORY) 또는 TransactionDefinition의 setPropagationBehavior 사용
1. 종류
1) MANDATORY
- 기존에 만들어진 트랜잭션에 들어가야 함
- 기존에 만들어진 것이 없다면 예외 발생
2) NESTED
- 기존에 만들어진 트랜잭션이 있다면 해당 범위에 들어감
- 기존에 만들어진 트랜잭션이 없다면 새로운 트랜잭션 생성
- 해당 propagation이 적용된 구간에서 롤백이 발생하면 해당 구간에서만 롤백이 일어남
- 부모 트랜잭션에서 롤백될 경우, 같이 롤백됨
3) NEVER
- 트랜잭션 관리를 하지 않음
- 기존에 만들어진 것이 있다면 예외 발생
4) NOT_SUPPORTED
- 트랜잭션 관리를 하지 않음
- 기존에 만들어진 것이 있다면 기해당 트랜잭션이 끝나기를 기다림
5) REQUIRED
- 기존에 만들어진 트랜잭션이 있다면 해당 범위에 들어감
- 기존에 만들어진 것이 없다면 새로운 트랜잭션 생성
6) REQUIRES_NEW
- 새로운 트랜잭션을 생성함
- 기존에 만들어진 트랜잭션이 있다면 해당 트랜잭션이 끝나기를 기다림
7) SUPPORTS
- 기존에 만들어진 트랜잭션이 있다면 해당 범위에 들어감
- 기존에 만들어진 트랜잭션이 없다면 트랜잭션 관리를 하지 않음
'프로그래밍 > Spring' 카테고리의 다른 글
[Spring] Spring Web Application, Controller, Exception Handler (0) | 2019.04.02 |
---|---|
[Spring] Spring MVC - MVC, IntelliJ 설정, 프로젝트 생성 (0) | 2019.03.28 |
[Spring] Aspect of Programming(AOP), Resource (0) | 2019.03.20 |
[Spring] Dependency Injection(DI) (0) | 2019.03.18 |
[Spring] Spring Framework 개요 (0) | 2019.03.18 |