의존성 추가!
1) spring-jdbc

implementation 'org.springframework:spring-jdbc:6.1.10'
*JDBC API (SQL응용) 참고
2) tomcat-jdbc
커넥션 풀 (연결 객체 저장소)
HikariCp
javax.sql.DataSource
+ spring-context
+ lombok
+ spring-test

+ ojdbc11
->runtimeOnly로

ext{
springVersion = '6.1.10'
}
...
dependencies {
implementation "org.springframework:spring-context:$springVersion"
...
implementation "org.springframework:spring-jdbc:$springVersion"
...
testImplementation "org.springframework:spring-test:$springVersion"
...
}
javax.sql.DataSource
Tomcat JDBC의 주요 프로퍼티
org.apache.tomcat.jdbc.pool.DataSource클래스는 커넥션 풀 기능을 제공하는 DataSource 구현 클래스이다.스키마 만들고 시작해야쥐이..~
docker exec -it oracle-xe /bin/bash
bash-4.4$ sqlplus system/oracle
...
SQL>CREATE USER SPRING IDENTIFIED BY oracle QUOTA UNLIMITED ON USERS;
SQL> GRANT CONNECT, RESOURCE TO SPRING;
-- 회원테이블 추가
CREATE TABLE MEMBER(
SEQ NUMBER(11) PRIMARY KEY,
EMAIL VARCHAR2(60) NOT NULL UNIQUE,
PASSWORD VARCHAR2(65) NOT NULL,
USER_NAME VARCHAR2(40) NOT NULL,
REG_DT DATE DEFAULT SYSDATE
);
--시퀀스
CREATE SEQUENCE SEQ_MEMBER;
Datasource
@Configuration
public class AppCtx {
@Bean(destroyMethod = "close")
//스프링 컨테이너가 소멸될때 자원도 같이 해제 된다.
public DataSource dataSource(){
DataSource ds = new DataSource();
/* DB 연결 설정 S*/
//데이터베이스 드라이브 설정
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setUrl("jdbc:oracle:thin:@localhost:1521:XE");
//스키마 설정
ds.setUsername("SPRING");
ds.setPassword("oracle");
/* DB 연결 설정 E*/
/* 커넥션 풀 설정 S*/
ds.setTestWhileIdle(true);//유휴 객체 유효성 체크
ds.setInitialSize(2);
ds.setMaxActive(10);//최대 활성화 개수
ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
//10초에 한번씩 연결객체가 연결 되어있는지 체크
ds.setMinEvictableIdleTimeMillis(36 * 1000); //유휴 객체가 살아있는 시간 설정(1분)
//1분후에 소멸되고 다시 만들어진다.
/* 커넥션 풀 설정 E*/
return ds;
}
}
DB연결설정 테스트
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class) //설정 클래스가 뭔지 체크하고 의존성을 주입할 수 있게 컨테이너가 생성된다.
public class DBConnectionTest {
//datasource 주입
@Autowired
private DataSource dataSource;
@Test
void test1() throws Exception {
//연결객체
Connection con = dataSource.getConnection();
System.out.println(con);
}
}

⚡ 1. 데이터베이스 커넥션 획득: JdbcTemplate은 쿼리를 실행하기 위해 데이터베이스 커넥션이 필요하다. DataSource를 통해 JdbcTemplate은 커넥션 풀에서 커넥션을 가져올 수 있다.
⚡ 2. 커넥션 풀 관리: DataSource는 커넥션 풀을 관리한다. 커넥션 풀은 여러 커넥션을 미리 생성해 두고, 애플리케이션이 필요할 때마다 커넥션을 할당한다. 사용이 끝난 커넥션은 다시 풀로 반환되어 재사용된다.
⚡ 3. 안전한 자원 관리: JdbcTemplate은 데이터베이스 작업 후 커넥션을 안전하게 반환한다. JdbcTemplate을 통해 쿼리를 실행할 때, 커넥션을 직접 관리하지 않아도 된다.

@Test
void test2(){
List<Member> members = jdbcTemplate.query("select * from MEMBER", new RowMapper<Member>() {//slq문으로 조회된 결과를 넘겨주면 Member쪽으로 값을 바꿔준다.
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
return Member.builder()
.seq(rs.getInt("SEQ"))
.email(rs.getString("EMAIL"))
.password(rs.getString("PASSWORD"))
.userName(rs.getString("USER_NAME"))
.regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
.build();
}
});
members.forEach(System.out::println);
}
람다식 형태로🔽🔽🔽
@Test
void test2(){
List<Member> members = jdbcTemplate.query("select * from MEMBER", (rs, num) -> Member.builder()
.seq(rs.getInt("SEQ"))
.email(rs.getString("EMAIL"))
.password(rs.getString("PASSWORD"))
.userName(rs.getString("USER_NAME"))
.regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
.build());
members.forEach(System.out::println);
}
test2실행_ 조회

List query(String sql, Object[] args, RowMapper rowMapper)
List query(String sql, RowMapper rowMapper, Object... args)


반복되는 builder코드 따로 정의해서 쓰자
private Member mapper(ResultSet rs, int num) throws SQLException{
return Member.builder()
.seq(rs.getInt("SEQ"))
.email(rs.getString("EMAIL"))
.password(rs.getString("PASSWORD"))
.userName(rs.getString("USER_NAME"))
.regDt(rs.getTimestamp("REG_DT").toLocalDateTime())
.build();
}

예외처리 넣어주는게 좋음
@Test
void test3(){
String email = "user08@test.org";
try{
Member member = jdbcTemplate.queryForObject("SELECT * FROM MEMBER WHERE EMAIL = ?", this::mapper, email);
System.out.println(member);
}catch (Exception e){
System.out.println("없음");
}
}
🔽쿼리 개수 출력

데이터 변경을 가하는 SQL
-> INSERT, DELETE, UPDATE 반환값 ->
반영된 레코드 수
int update(String sql)
int update(String sql, Object... args)
값 바인딩 ?, ?@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppCtx.class)
public class Ex01 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void test() {
String sql = "INSERT INTO MEMBER (SEQ, EMAIL, PASSWORD, USER_NAME) VALUES (SEQ_MEMBER.NEXTVAL, ?, ?, ?)";
}
}


db에 데이터도 잘 들어와있다.
로거
slf4j-api
logback classic 구현체
implementation 'org.slf4j:slf4j-api:2.0.13'
testImplementation 'ch.qos.logback:logback-classic:1.5.6'
#src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %c{2} - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout" />
</root>
<logger name="org.springframework.jdbc" level="DEBUG" />
</configuration>
더 자세히 보려면 level속성 바꾸면 된다.

현재 Member테이블에 3명의 사용자가 저장되어있는 상태이다.
PreparedStatementCreator로 사용자 추가


👩🏫람다식을 사용해서 짧게쓰기!

java.sql.Connection 인터페이스

preparedStatement -> 자동 생성된 키값을 가져오려고 쓴다



🔼Number클래스🔼
증감번호 형태의 PK조회 코드
@Test
void test1() {
KeyHolder keyHolder = new GeneratedKeyHolder();
int result = jdbcTemplate.update(con -> {
String sql = "INSERT INTO MEMBER (SEQ, EMAIL, PASSWORD, USER_NAME)" + " VALUES (SEQ_MEMBER.NEXTVAL,?,?,?)";
PreparedStatement pstmt = con.prepareStatement(sql, new String[] {"SEQ"});
//Column name
pstmt.setString(1, "user05@test.org");
pstmt.setString(2, "12345678");
pstmt.setString(3, "사용자05");
return pstmt;
},keyHolder); //세번째 매개변수 key holeder에 발생한 자동 증감번호를 넣어준다
System.out.println(result);
Number key = keyHolder.getKey();//증감번호 가져오기 , 반환값이 Number인 이유? -> Number에는 변환 메서드가 있다 int, long 어떤걸 쓸지는 개발자만 아니까.. 숫자 관련 상위 추상 클래스
long seq = key.longValue(); //자동 증감된 키값을 가져올 수 있게되었다.
System.out.println(seq);
}
SQLExcpetion, HibernateException, PersistenceException -> DataAccessException(RuntimeException)
@Transactional
세팅 - Appctx
자동화 -> @EnableTransactionManagement


수동 관리할때는 autoCommit을 false로 하고 마지막에 commit을 해줘야한다.
Connection
setAutoCommit(false) //공통기능
/*핵심기능*/
SQL문1...
SQL문2...
SQL문3...
/*핵심기능*/
Connection
commit(); //공통기능