Spring DB ๊ธฐ์ ํํค์น๊ธฐ #1
by JiwonDev
Java์ ๋ฐ์ดํฐ ์ ๊ทผ๊ธฐ์ ์ 1997๋
JDBC (JDK 1.1)์ ์์๋์ด 20๋
์ ์ญ์ฌ๋ฅผ ๊ฑฐ์น๋ฉฐ ๋ฐ์ ํด์์ต๋๋ค.
Oracle, MySql, PostgreSql ๊ฐ์ ๋ฒค๋๋ฅผ ์ ์ธํ๊ณ Java์ DB ์ ๊ทผ๊ธฐ์ ๋ง ๊ณต๋ถํ๋คํด๋ ๊ทธ ๊ท๋ชจ๊ฐ ๋๋ฌด ์ปค์ ธ ์ ์ด๋ ๊ฒ ์ฌ์ฉํ๋์ง, ๋ด๋ถ์์๋ ์ด๋ป๊ฒ ๋์ํ๋์ง๋ฅผ ์ดํดํ๊ธฐ๋ ์ฝ์ง ์์ต๋๋ค.
๐ ์๋ฐ ์ฝ๋๋ก DB๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
try {
// JDBC 4.0 ๋ถํฐ๋ META-INF/services/java.sql.Driver๋ฅผ ์ฝ์ด DriverManager๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํด์ค๋๋ค.
// ํ์ํ๋ค๋ฉด Class.forName("com.my.db.jdbc.Driver") ์ ๊ฐ์ด ์ง์ DriverManager์ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
connection = DriverManager.getConnection(jdbcUrl, username, password)
println("Database connection established.")
// Statement ์์ฑ
statement = connection.createStatement()
// SQL ์ฟผ๋ฆฌ ์คํ
val sql = "SELECT * FROM your_table"
resultSet = statement.executeQuery(sql)
// ๊ฒฐ๊ณผ ์ฒ๋ฆฌ
while (resultSet.next()) {
// ์ฒซ ๋ฒ์งธ ์ปฌ๋ผ ๊ฐ์ String์ผ๋ก ์ฝ๊ธฐ
val firstColumnValue = resultSet.getString(1)
println(firstColumnValue)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
// DB ์ปค๋ฅ์
์ ๋ซ์์ค์ผ ํฉ๋๋ค. Java7 ๋ถํฐ๋ try-resource ๋ฌธ (kotlin ์์ use)๋ฅผ ์ฌ์ฉํ๋ฉด ํธํฉ๋๋ค.
resultSet?.close()
statement?.close()
connection?.close()
}
๐ JDBC
์๋ฐ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ JDBC(Java DataBase Connectivity) API ๋จ ํ๋ ๋ฟ์ด๋ค. ์ด๋ ์๋ฐ์์ ์ ๊ณตํด์ฃผ๋ DB ์ฐ๊ฒฐ ์ธํฐํ์ด์ค์ด๊ณ ์ค์ ๊ตฌํ์ฒด๋ ๊ณต์ DB ๋ฒค๋์ฌ (Oracle, MS..)์์ ์ ๊ณตํฉ๋๋ค. ๋ง์ฝ ๋๋ง์ Driver๋ฅผ ๊ตฌํํ๊ณ ์ถ๋ค๋ฉด ๋์ผํ๊ฒ JDBC Driver ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์ด์ ๋ฑ๋กํ๋ฉด ๋ฉ๋๋ค.
JDBC๋ ํฌ๊ฒ 2๊ฐ์ง ์ธํฐํ์ด์ค๋ก ์ด๋ฃจ์ด์ ธ์์ต๋๋ค.
- JDBC Driver: ์ค์ DB์ ๋คํธ์ํฌ ํต์ ์ ๋ด๋นํ๋ค.
- DataSource : ๊ฐ์ข
์ค์ ๋ฐ ์ปค๋ฅ์
ํ์ ๊ด๋ฆฌํ๋ค.
** ๊ถ์ฅํ๋ ๋ฐฉ๋ฒ์ ์๋์ง๋ง, ๋จ์ํ ํ๋ฒ ์ฐ๊ฒฐ ํ๋๊ฑฐ๋ผ๋ฉด DriverManger ์ ํธ์ ์ง์ ๊บผ๋ด ๊ฐ๋จํ ์ปค๋ฅ์ ์ ์ป๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค.
์ค์ ๊ตฌํ์ฒด๋ ์๋ฐ์ ๊ณต์ DB ๋ฒค๋์ฌ(MS, Oracle...)๊ฐ ๋ง๋ค์ด์ ์ ๊ณตํ๋ฉฐ Java1.4 ์ดํ ๋ฒ์ ์๋ฐ ์ค์น์ META-INF/services/java.sql.Driver ๊ฒฝ๋ก์ ํฌํจ๋์ด์๋ค.
์ฐธ๊ณ ๋ก ๊ทธ ์ด์ ์ Driver ๊ตฌํ์ฒด๋ฅผ ์ง์ ๋ก๋ํด์ ์ฌ์ฉํ๋๋ฐ, ์ปดํ์ผ ์์ ์ ๋ชจ๋ DB Driver ํด๋์ค๋ฅผ ์ฝ๋๊ฑด ๋ถํ์ํ๋ฏ๋ก Class.forName("oracle.jdbc.driver.OracleDriver") ๋ฉ์๋๋ฅผ ์ด์ฉํด ๋ฐํ์์ ํ์ํ DB ๊ตฌํ์ฒด๋ง ํฌํจ ๋๋๋ก ์ฌ์ฉํ์๋ค.
๐ Java DataSource ์ DB Connection Pool (DBCP)
์ฌ์ฉ์๋ JDBC Connection ๊ฐ์ฒด๋ฅผ ์ด์ฉํด DB์ ํต์ ์ ํฉ๋๋ค.
DB๋ ์ปค๋ฅ์ ์ ํด๋ผ์ด์ธํธ์ฉ ์ธ์ ์ ๋ฐ๋ก ๋ง๋ค์ด ์์ฒญ์ ๋ฐ๊ณ ์ํ๋ ์๋ต์ ์ ๊ณตํฉ๋๋ค.
์ฐธ๊ณ ๋ก ์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ์ด ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฌ์ฉ๋๋ Connection๊ณผ Session์ ์๋ฏธ๋ ์๋์ ๊ฐ๋ค.
< ์ปค๋ฅ์ ์ด ์ฐ๊ฒฐ๋๋ฉด ์ธ์ ์ด ๋ง๋ค์ด์ง๊ฒ ๋๊ณ , ์ธ์ ์ ์์ ์ ์์ฒญํ๋ฉด ํ๋ก์ธ์ค๊ฐ ์คํ๋๋ค. >
- ์ปค๋ฅ์ (Connection)์ ํด๋ผ์ด์ธํธ ํ๋ก์ธ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค๊ฐ์ ๋ฌผ๋ฆฌ์ ์ธ TCP/IP ํต์ ๊ฒฝ๋ก๋ฅผ ์๋ฏธํ๋ค.
- ์ธ์ (Sessions)์ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ๋ ์ผ๋ก ํ ์ ์ ์ ๋ก๊ทธ์ธ ์ํ๋ฅผ ์๋ฏธํ๋ค. ๋ณดํต ์ปค๋ฅ์ ๊ณผ 1:1 ์ผ๋ก ๋์๋๋ค.
- ํ๋ก์ธ์ค(Process)๋ ์ค์ ์๋ฒ์ ์์ฑ๋๋ ์ด์์ฒด์ ํ๋ก์ธ์ค๋ฅผ ์๋ฏธํ๋ค.
๋ค๋ง ํด๋น DB๊ฐ ์์ฒด์ ์ธ Connection Pool์ด๋ Shared Server Mode ๊ฐ์ ๊ฑธ ์ ๊ณตํด์ค๋ค๋ฉด ํ ์ปค๋ฅ์ ์ ์ฌ๋ฌ๊ฐ์ ์ธ์ ์ด ๋ถ์ ์๋ ์๋ค.
์ฐธ๊ณ ๋ก ์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ์ด ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฌ์ฉ๋๋ Connection๊ณผ Session์ ์๋ฏธ๋ ์๋์ ๊ฐ๋ค.
< ์ปค๋ฅ์ ์ด ์ฐ๊ฒฐ๋๋ฉด ์ธ์ ์ด ๋ง๋ค์ด์ง๊ฒ ๋๊ณ , ์ธ์ ์ ์์ ์ ์์ฒญํ๋ฉด ํ๋ก์ธ์ค๊ฐ ์คํ๋๋ค. >
- ์ปค๋ฅ์ (Connection)์ ํด๋ผ์ด์ธํธ ํ๋ก์ธ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค๊ฐ์ ๋ฌผ๋ฆฌ์ ์ธ TCP/IP ํต์ ๊ฒฝ๋ก๋ฅผ ์๋ฏธํ๋ค.
- ์ธ์ (Sessions)์ ๋ ผ๋ฆฌ์ ์ธ ๊ฐ๋ ์ผ๋ก ํ ์ ์ ์ ๋ก๊ทธ์ธ ์ํ๋ฅผ ์๋ฏธํ๋ค. ๋ณดํต ์ปค๋ฅ์ ๊ณผ 1:1 ์ผ๋ก ๋์๋๋ค.
- ํ๋ก์ธ์ค(Process)๋ ์ค์ ์๋ฒ์ ์์ฑ๋๋ ์ด์์ฒด์ ํ๋ก์ธ์ค๋ฅผ ์๋ฏธํ๋ค.
๋ค๋ง ํด๋น DB๊ฐ ์์ฒด์ ์ธ Connection Pool์ด๋ Shared Server Mode ๊ฐ์ ๊ฑธ ์ ๊ณตํด์ค๋ค๋ฉด ํ ์ปค๋ฅ์ ์ ์ฌ๋ฌ๊ฐ์ ์ธ์ ์ด ๋ถ์ ์๋ ์๋ค.
์ ๊ทธ๋ฆผ์ ์ฌ์ฉ์ ์ ์ฅ์์ ๋ค์ ๊ทธ๋ ค๋ณด๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
๋ค๋ง ์ด๋ค ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ์๋ฐ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด JDBC Driver ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๋๊ฑด ๋์ผํฉ๋๋ค.
- <DataSource ๋๋ DriverManager ์ง์ ์ฌ์ฉ> ๋น์ฐํ ์ฉ JDBC ๋ฅผ ์ง์ ์ฌ์ฉํด์ ์ปค๋ฅ์ ์ ์ป์ด์๋ ์ฌ์ฉ ๊ฐ๋ฅ
- <Spring JDBC Template> ๋ด๋ถ ํ๋๋ก DataSource๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
- <MyBatis (๊ตฌ iBatis)> SqlSessionFactory (spring mybatis๋ SqlSessionFactoryBean) ์ DataSource๋ฅผ ๋ฑ๋กํด๋๊ณ ์ธ์ ์ธ SqlSessionTemplate ๋ฅผ ์ด์ฉํด ์ฌ์ฉํ๋ค.
- < JPA > EntityManagerFactory์ DataSource๋ฅผ ๋ฑ๋กํด๋๊ณ ์ธ์ ์ธ EntityManager๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ค.
- < Spring Data JPA > ๊ตฌํ์ฒด(SimpleJpaRepository)๋ฅผ ํ์ธํด๋ณด๋ฉด EntityManager๋ฅผ ์คํ๋ง ๋น์ผ๋ก ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ๋ค. ํด๋น ๋น์ Spring AOP ํ๋ก์๋ฅผ ๊ฑฐ์ณ ํ์ฌ ํธ๋์ญ์ ์ ๋ง์ถฐ ์ ์ ํ ์ปค๋ฅ์ ์ ํ ๋น๋๋ค.
์ปค๋ฅ์ ๊ณผ ์ปค๋ฅ์ ํ์ ๋ํ์ฌ ๋ ์์ธํ๊ฒ ์๊ณ ์ถ๋ค๋ฉด ์๋ ๊ธ์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
๐ Transaction
ํธ๋์ญ์ ์ ๊ดํ ๊ธฐ์ด ๋ด์ฉ์ ์ด๋ฏธ ๊ธ์ด ์์ผ๋ฏ๋ก ์๋ตํฉ๋๋ค.
2021.08.10 - [๐๊ธฐ๋ณธ ์ง์/CS ๊ธฐ๋ณธ์ง์] - ํธ๋์ญ์ ๊ฒฉ๋ฆฌ์์ค(DB Isolation Level)
๐ Spring Transaction ์ถ์ํ
JDBC๋ฅผ ์ด์ฉํ๋ฉด DB์ ์์ธ ์คํ์ ๋ชฐ๋ผ๋ Java ์ฝ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค๋ง, ์ง์ ์ฌ์ฉํ๊ธฐ์ ์ฌ๋ฌ ๋จ์ ์ด ์์ต๋๋ค.
- JDBC๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด try-catch-finally ๊ฐ์ ํน์ ํจํด์ ์ฝ๋๊ฐ ๋ฐ๋ณต๋ฉ๋๋ค.
- Connection ๊ฐ์ ์์์ด ๋์๋์ง ์๋๋ก ์ง์ ๊ด๋ฆฌํด์ผํฉ๋๋ค.
JDBC ๊ด๋ จ ์ฝ๋๋ฅผ ์จ๊ธฐ๋ ค๊ณ ํด๋, Transaction ์ค์ ๋ฐ ๋๊ธฐํ ๋๋ฌธ์ ๊ฐ์ ๋ก ์๋น์ค์ฝ๋์ ์์ด๊ฒ ๋ฉ๋๋ค. - JDBC์ `throws SQLException` ์ ์ด๋๊ฐ์์ ์ง์ ์ฒ๋ฆฌํด์ผํฉ๋๋ค.
ํด๋น ๋ฌธ์ ๋ค์ ํด๊ฒฐํ๊ธฐ์ํด ์คํ๋ง์์ ํธ๋์ญ์ ์ ์๋์ ๊ฐ์ด ์ถ์ํ ํด๋์์ต๋๋ค.
public interface PlatformTransactionManager{
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
public interface AbstractPlatformTransactionManager extends PlatformTransactionManager{
/**
* begin, commit, rollback ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ค์ ๋์์ ์๋ ๋ฉ์๋์ ์์ํ๋ค
* (EX commit()-> processCommit() -> doCommit())
*/
protected void doCommit(DefaultTransactionStatus status) throws TransactionException;
protected void doRollback(DefaultTransactionStatus status) throws TransactionException;
protected void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException;
}
์ด์ ๊ฐ๋ฐ์๋ PlatformTransactionManager ๋ฅผ ๊บผ๋ด์ getTransaction, commit, rollback๋ง ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
๋ณดํต try-catch ์์ด ํธ๋ฆฌํ๊ฒ ์ฐ๋ ค๊ณ ์คํ๋ง์ด ์ ๊ณตํ๋ ํ ํ๋ฆฟ ํด๋์ค(TransactionTemplate)๋ก ๋ํํด์ ์ฌ์ฉํฉ๋๋ค.
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
// DataSource๋ก PlatformTransactionManager ์์ฑ
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
// PlatformTransactionManager ์ผ๋ก TransactionTemplate ์์ฑ
return new TransactionTemplate(transactionManager);
}
@Service
@RequiredArgsConstructor
public class MyService {
private final TransactionTemplate transactionTemplate;
/** โญ๏ธ Java8 ๋๋ค๋ก ์ ์๋ ๋ฉ์๋๋ฅผ ์ฐ๋ฉด ๋งค์ฐ ๊น๋ํฉ๋๋ค. */
void myTransactionalLambda() {
transactionTemplate.executeWithoutResult(status -> { /*..ํธ๋์ญ์
๋ก์ง..*/ });
var result = transactionTemplate.execute(status -> { /*..ํธ๋์ญ์
๋ก์ง..*/ return null; });
}
/** ์ต๋ช
ํด๋์ค๋ก ์ฌ์ฉํ ๋ ์๋์ ๊ฐ์ต๋๋ค */
MyResultDto myTransactionalMethod() {
return transactionTemplate.execute(new TransactionCallback<MyResultDto>() {
@Override
public MyResultDto doInTransaction(TransactionStatus status) {
// ์ฌ๊ธฐ์ ํธ๋์ญ์
์ ์ ์ฉํ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํฉ๋๋ค.
// ํ์ํ ๊ฒฝ์ฐ status.setRollbackOnly(); ํธ์ถ๋ก ๋กค๋ฐฑ์ ์ง์ํ ์ ์์ต๋๋ค.
MyResultDto result = new MyResultDto();
return result;
}
});
}
void myTransactionalMethod() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// ์ฌ๊ธฐ์ ํธ๋์ญ์
์ ์ ์ฉํ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํฉ๋๋ค.
// ํ์ํ ๊ฒฝ์ฐ status.setRollbackOnly(); ํธ์ถ๋ก ๋กค๋ฐฑ์ ์ง์ํ ์ ์์ต๋๋ค.
}
});
}
}
์ค์ ๋ด๋ถ ๊ตฌํ์ ๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ ค๋ณด๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
์ฐธ๊ณ ๋ก ํ ์ค๋ ๋๊ฐ ์ฌ๋ฌ DB Connection์ ๋ฌผ๊ณ ์์ ์๋ ์์ง๋ง, ๋ฐ๋๋ ๋ถ๊ฐ๋ฅํฉ๋๋ค. ๊ด๋ฆฌ๋ ์์ฒญ ๋ณต์กํด์ง๊ณ ์ค๋ ๋ ๊ฒฝํฉ๋ฌธ์ ๋ ์๊ธฐ๋๋ฌธ์
๋๋ค. ์ ์ด์ ์ปค๋ฅ์
ํ๋ก ๊ด๋ฆฌํ๋๋ฐ ํ ์ปค๋ฅ์
์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ์ฌ์ฉํ ํ์๋ ์๊ตฌ์.
๋ณต์กํด๋ณด์ผ์ ์๋๋ฐ ์ญํ ์ ๋ฐ๋ผ ๋ถ๋ฆฌํด๋ณด๋ฉด ์๊ฐ๋ณด๋ค ๊ฐ๋จํฉ๋๋ค. ์๋ฅผ ๋ค์ด DataSourceTransactionManager.doBegin() ๋ฅผ ๊น๋ณด๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
/**
* doBegin: ํธ๋์ญ์
์ ์์ํฉ๋๋ค. ์ฐธ๊ณ ๋ก ์ฉ JDBC ์์๋์์ ๋ฉ์๋๋ ๋ฐ๋ก ์๊ณ
setAutoCommit(false) ๋ก ํธ๋์ญ์
์ ์์ํฉ๋๋ค.
*
* @param transaction ํ์ฌ ํธ๋์ญ์
์ ๊ดํ ์ค์ ์ ๋ณด ๋ฐ Connection ์ ๊ด๋ฆฌํฉ๋๋ค.
๊ตฌํ์ฒด์ ๋ฐ๋ผ TransactionObject ํ์
์ด ๋ฌ๋ผ์ง๋๋ค.
* (** Connection ์์ ํ ๋น ๋ฐ ํ์๋ TransactionSynchronizationManager ๋ฅผ ์ฌ์ฉํด์ ์ฒ๋ฆฌํฉ๋๋ค.)
* @param definition ํธ๋์ญ์
์ ํ, ๊ฒฉ๋ฆฌ, ํ์์์, ์ฝ๊ธฐ ์ ์ฉ๋ฑ์ ๋์ ๋ฐฉ์์ ๊ด๋ฆฌํฉ๋๋ค.
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
/**
* 1. ์ฌ์ฉ์ค์ธ Connection์ด ์์ผ๋ฉด ์๋ก ์์ฑํฉ๋๋ค.
* 2. ์ฌ์ฉ์ค์ธ Connection์ด ๋ค๋ฅธ ํธ๋์ญ์
์ ์ฌ์ฉ(๋๊ธฐํ)์ค์ด๋ผ๋ฉด ์๋ก ์์ฑํฉ๋๋ค.
*
* ์ฆ ์ด๋ฏธ ์กด์ฌํ๋ Connection์ด ์ฌ์ฉ์ค์ด ์๋๋ผ๋ฉด ํด๋น ์ปค๋ฅ์
์ ์ฌ์ฉํฉ๋๋ค.
*/
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
// ์๋กญ๊ฒ ์์ฑ๋ Connection์ ConnectionHolder๋ฅผ ์ด์ฉํด ์ ์ฅํฉ๋๋ค.
// ConnectionHolder ๋ ์ฐธ์กฐ ์นด์ดํธ๋ฅผ ์ด์ฉํ ์ปค๋ฅ์
์๋ช
์ฃผ๊ธฐ ๊ด๋ฆฌ, rollback ์ง์, DB ํธ๋์ญ์
์ํ ์ ์ฅ๋ฑ์ ๋ด๋นํฉ๋๋ค.
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// ๋ค๋ฅธ ํธ๋์ญ์
์ด ํด๋น Connection ์ ์ฌ์ฉํ์ง ๋ชปํ๊ฒ {๋๊ธฐํ=true} ๋ก ์ค์ ํ ๋ค ์ปค๋ฅ์
์ ๊บผ๋ด์ต๋๋ค.
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// ํธ๋์ญ์
์ ์์ ๋ฐ๋ผ ๊ฒฉ๋ฆฌ ์์ค์ ์ค์ ํฉ๋๋ค. prepareConnectionForTransaction ๊น๋ณด๋ฉด ๊ทธ๋ฅ JDBC ์ฝ๋์
๋๋ค.
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// ํธ๋์ญ์
์ ์ง์ ๊ด๋ฆฌํ๊ธฐ๋๋ฌธ์, autoCommit=true ์ผ๋ก ์ค์ ๋ ๊ฒฝ์ฐ ์์๋ก false ๋ณ๊ฒฝํฉ๋๋ค.
// ์ด๋ setMustRestoreAutoCommit ์ ๊ธฐ๋กํด๋๋ค๊ฐ ํธ๋์ญ์
์ด ๋๋๋ ์์ ์ ๊ธฐ์กด autoCommit ์ค์ ์ผ๋ก ์์ ๋ณต๊ท๋ฉ๋๋ค.
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
// ์ปค๋ฅ์
์ฐ๊ฒฐ์ ์ค๋นํฉ๋๋ค. ๊น๋ณด๋ฉด ๊ทธ๋ฅ JDBC๋ก "SET TRANSACTION READ ONLY" ๊ฐ์ ๊ฑธ ์ค์ ํ๋ ๊ฐ๋จํ ์ฝ๋์
๋๋ค.
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// ํธ๋์ญ์
์ ์์ ๋ฐ๋ผ ํ์์์์ ์ค์ ํฉ๋๋ค.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// ConnectionHolder๊ฐ ์ฌ๊ธฐ์์ ์ฒซ ์์ฑ๋์๋ค๋ฉด TransactionSyncManager ์ ๋ฑ๋กํด ์์์ ๊ด๋ฆฌํฉ๋๋ค.
// Map (key=Datasource, value=ConnectionHolder)
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
// ์์ธ ๋ฐ์ ์ ์ฐ๊ฒฐ์ ํด์ ํ๊ณ TransactionSyncManager ์ ์์ ํด์ ๋ฅผ ์์ฒญํฉ๋๋ค.
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
๐ค @Transactional์ ์ด๋ป๊ฒ ๋์ํ๋๊ฑฐ์์?
์ด๋ ์คํ๋ง AOP ๋ฅผ ํธ๋์ญ์ ์ ์ ์ฉํ ๋๊ตฌ์ ๋๋ค. ๋์๊ณผ์ ์ ์๋์ ๊ฐ์ต๋๋ค.
2022.03.09 - [๐ฑ Spring Framework/Spring Core] - ์๋ฐ AOP์ ๋ชจ๋ ๊ฒ(Spring AOP & AspectJ)
- 1๏ธโฃ @EnableTransactionManagement ๋ก ์คํ๋ง ํธ๋์ญ์
AOP๋ฅผ ํ์ฑํํฉ๋๋ค.
* @SpringBootApplication -> @EnableAutoConfiuration์ ์ํด EnableTransactionManagement ์๋ ํ์ฑํ ๋จ - 2๏ธโฃ ํ์ฑํ๋๋ฉด @Import(TransactionManagementConfigurationSelector.class) ์ ์ํด ๋น์ผ๋ก ๋ฑ๋ก๋ฉ๋๋ค.
์ฐ๋ฆฌ๋ ์คํ๋ง ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์๋ ์ฝ๋์ ๋ฐ๋ผ ProxyTransactionManagementConfiguration ๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋ฉ๋๋ค.
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/** {์คํ๋ง ํ๋ก์ ๊ฐ์ฒด} or {AspectJ ํ๋ก์ ๊ฐ์ฒด} ์ค ํ๋๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํฉ๋๋ค. */
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
}
- 3๏ธโฃ ProxyTransactionManagementConfiguration ์ผ๋ก ํธ๋์ญ์ AOP์ ํ์ํ ๊ตฌํ ํด๋์ค๋ค์ด ๋น์ผ๋ก ๋ฑ๋ก๋ฉ๋๋ค.
/** Advice (ํ๋ก์๋ก ์ ์ฉํ ์ค๋์ ์ฝ๋) ๋ฅผ ๋ด๋นํ๋ Advisor ํด๋์ค, ์คํ๋ง ์ปจํ
์ด๋ AOP(BeanFactory)์์ ์ ์ฉ๋จ*/
BeanFactoryTransactionAttributeSourceAdvisor;
/** ์ฌ์ฉ์ ํธ๋์ญ์
์ค์ (TransactionAttributeSource)๋ฅผ ์ฝ์ด๋ค์ด๋ ํด๋์ค, ์ด๋
ธํ
์ด์
์ ์ฝ๋ ๊ตฌํ์ฒด ์ฌ์ฉ */
AnnotationTransactionAttributeSource;
/** PointCut(์ ์ฉ ์กฐ๊ฑด), ์ฆ invoke() ๋ฉ์๋๋ฅผ ๊ตฌํํด์ AOP์ ์ ์ฉํ ์กฐ๊ฑด์ ์ง์ ํ๋ ํด๋์ค */
TransactionInterceptor;
transactionInterceptor ์ฝ๋๋ฅผ ๋ณด์๋ฉด `setTransactionManager(this.txManager)` ๋ฅผ ๋ฑ๋กํ๋ ๋ถ๋ถ์ด ์๋๋ฐ, ์ฌ๊ธฐ์ PlatformTransactionManager ๊ฐ ์ค์ ๋ฉ๋๋ค. ์ด์ @Transactional ์ ์ฌ์ฉํ๋ฉด AOP๋ฅผ ํตํด ํธ๋์ญ์ ์ด ๊ฑธ๋ฆฌ๊ฒ ๋ฉ๋๋ค.
์ค์ ๋ถ๋ถ์ด ๋ณต์กํ์ง๋ง ๋์์ ์๊ฐ๋ณด๋ค ๊ฐ๋จํฉ๋๋ค.
- ๋ด๊ฐ ์ํ๋ ๋ฉ์๋์ @Transactional ๋ช ์
- ํด๋์ค๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋๋ ์์ ์ Spring BeanPostProcessor ์ ์ํ AOP ํ๋ก์ ์์ฑ
- PlatformTransactionManager ์ผ๋ก ๋ฉ์๋ ์์์ , ํ์ ํธ๋์ญ์ ์ฝ๋ ์ง์ด๋ฃ๊ธฐ
๐ค ํธ๋์ญ์ ์ ํ์ต์ ..? ์ด๋ฐ๊ฒ JDBC์ ์์๋๊ฐ์?
ํธ๋์ญ์ ๊ฒฉ๋ฆฌ๋ ๋ฒจ(Transaction Isolation)์ JDBC์์๋ ์์ฝ๊ฒ ์ค์ ๊ฐ๋ฅํฉ๋๋ค.
// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
๊ทธ๋ฐ๋ฐ ์ ํ์ต์
(Transaction Propagation)์ ์ค์ ์ ๋ฐ๋ผ ๊ธฐ์กด ํธ๋์ญ์
์ ์ฐ์ฅํ๊ฑฐ๋, ์๋ก์ด ํธ๋์ญ์
์ ๋ง๋๋ ๋ฑ์ ๊ธฐ๋ฅ์ธ๋ฐ
์ด๋ ๋น์ฐํ JDBC ์์ฒด์ ์ผ๋ก ์ ๊ณตํด์ฃผ๋ ๊ธฐ๋ฅ์ ์๋๋๋ค. TransactionDefinition ์ค์ ๊ฐ์ ๋ฐ๋ผ ์คํ๋ง์ด ๊ตฌํํ ๋ด์ฉ์ ๋๋ค.
๊ตฌํ ๋ด์ฉ์ ๊น๋ด์ผ๊ฒ ์ง๋ง, JDBC์ savePoint ๋ฑ์ ์ ์ ํ๊ฒ ํ์ฉํ๋ฉด ๋ถ๊ฐ๋ฅํ๊ฑด ์๋๋๋ค.
์ฐธ๊ณ ๋ก JDBC savePoint ๋ ์๋์ ๊ฐ์ด ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ ๋๋ค.
// try-with-resources ๋ฌธ์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ๋ฆฌ์์ค ํด์
try (Connection conn = DriverManager.getConnection(url, user, password)) {
conn.setAutoCommit(false);
// ์ฒซ ๋ฒ์งธ ์
๋ฐ์ดํธ
try (PreparedStatement pstmt1 = conn.prepareStatement(sql1)) { pstmt1.executeUpdate(); }
// Savepoint ์ค์
Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
// ๋ ๋ฒ์งธ ์
๋ฐ์ดํธ
try (PreparedStatement pstmt2 = conn.prepareStatement(sql2)) { pstmt2.executeUpdate(); }
conn.commit();
} catch (SQLException e) {
try {
if (savepoint1 != null) {
// Savepoint๊ฐ ์๋ค๋ฉด, ๊ฑฐ๊ธฐ๋ก ๋กค๋ฐฑ ( savePoint ์ดํ ์์
๋ง ๋กค๋ฐฑ๋จ )
conn.rollback(savepoint1);
conn.commit(); // ๋ค์ ์ปค๋ฐ
}
} catch (SQLException se) {
/* .. */
}
}
๊ฐ์ด๋ณด๋ฉด ์ข์ ๊ธ
2021.08.18 - [๐ฑ Spring Framework/Spring Core] - @Transactional ์ ๋์์๋ฆฌ, ํธ๋์ญ์
๋งค๋์
๐ ์๋ฐ์ Exception ์ฒ๋ฆฌ
- Throwable: throw๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ธํฐํ์ด์ค
- Throwable->Error: ๊ฐ๋ฐ์๊ฐ ์ฒ๋ฆฌํ ์ ์๋ JVM ์์ฒด ์๋ฌ (๋ฉ๋ชจ๋ฆฌ๋ถ์กฑ, ์์คํ OS ์ค๋ฅ)
- Throwable->Exception: ๊ฐ๋ฐ์๊ฐ ์ฒ๋ฆฌํ ์ ์๋ ์๋ฌ
- Throwable->Exception -> RuntimeException: ์ธ์ฒดํฌ๋ ์์ธ, ์ฆ ๋ฐํ์์์ ์ฒ๋ฆฌํด์ผํ๋ ์์ธ
์๋ฐ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ์์ ๋ฉ์๋๋ก ๋์ ธ์ง๋ค.
๋๊น์ง ์ฒ๋ฆฌํ์ง์์ผ๋ฉด main() ์ค๋ ๋๊น์ง ์์ธ๊ฐ ์ ๋ฌ๋๊ณ ์ด๋ ์์คํ ์ด ์ข ๋ฃ๋๋ค. ์น ์๋ฒ๋ผ tomcat ๋ฑ์ ์ฌ์ฉํ ๊ฒฝ์ฐ main ๊น์ง ๊ฐ์ง์๊ณ WAS๊ฐ ๋ฐ์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ค.
๋จ์ํ ์ค๋ช ๋ง ๋ค์ผ๋ฉด RuntimeException์ ์ฌ์ฉํ์ง์๊ณ , ์ ๋ถ ์ฒดํฌ ์์ธ (throws ๋ก ๋ช ์ํ๊ณ try-catch๊ฐ ๊ฐ์ ๋๋ ์์ธ)๋ฅผ ์ฌ์ฉํ๋ฉด ํธํ ๊ฒ ๊ฐ์ง๋ง, ํน์ Exception ๊ฐ์ฒด์ ์์กด์ฑ์ด ์๊ธฐ๊ฒ๋๊ณ ์ฝ๋ ์์ฒด๊ฐ ๋๋ฌ์์ง๋ค.
๊ทธ๋์ ๋ณดํต ์น ์๋ฒ์ ๊ฒฝ์ฐ RuntimeException์ ๋ง์ด ํ์ฉํ๋ค.
์ค์ ๋ก ์ฒดํฌ ์ต์ ์ ์ผ๋ก ์์ธ์ฒ๋ฆฌ๋ฅผ ๊ฐ์ ํด๋ด์ผ ์ฝ๋์์ผ๋ก ํ ์ ์๋๊ฒ ์์ด์ ์์ธ๋ฅผ try{..} catch ๋ก ์๋ฌด ์ฒ๋ฆฌ๋ ํ์ง์๊ณ ๋ฌด์ํ๋ ๊ฐ๋ฐ์๋ ๋ง์์๋ค. ๊ฐ๋ฐ์๊ฐ ์์ํ์ง ๋ชปํ ์๋ฌ๋ ์ต์์์์ ControllerAdvice (๋๋ main ์ฝ๋) ์์๋ง ์์ธ๋ฅผ ์ฒ๋ฆฌํด์ฃผ๋๊ฒ ๋ง๋ค.
2021.07.07 - [๐ฑBackend/Java] - #7 Exception (์์ธ์ฒ๋ฆฌ)
๐ ์คํ๋ง์ DB Exception ์ฒ๋ฆฌ
์คํ๋ง๋ ์ด์ ๋น์ทํ๊ฒ DB์์ ๋ฐ์ํ๋ ์๋ฌ๋ฅผ ์ถ์ํํด๋์๋ค. ( org.springframework.dao.DataAccessException )
์ฌ๋ด์ผ๋ก DB ๋ฟ๋ง ์๋๋ผ ์คํ๋ง์ ๋ค์ํ ๊ธฐ๋ฅ์ด ์์ธ๋ฅผ ์ด๋ฐ์์ผ๋ก ์ฒ๋ฆฌํ๋ค.
์์ธ๊ฐ ์ ์ ๋์ด์๋คํ๋ค, ๋ด๊ฐ ๋ชจ๋ DB์ ์ต์ ์ ์ DataAccessException ์ผ๋ก ๋ณํํด์ฃผ์ด์ผํ๋ ๋ฒ๊ฑฐ๋ก์์ ๊ทธ๋๋ก์ด๋ค. ๊ทธ๋์ ์คํ๋ง์์ SQLExceptionTranslator ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด์ ์ฌ์ฉํ๋ค.
SQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
// exTranslator.translate(String task, String sql, Exception e)
// ์ฐธ๊ณ ๋ก String task ๋ ์ผ์ข
์ ์ฃผ์์ด๋ค. ์๋ฅผ ๋ค์ด ํ์ด๋ฒ๋ค์ดํธ์ ๊ฒฝ์ฐ "Hibernate operation: " ์ผ๋ก ์ ๋ฌํ๋ค.
DataAccessException resultEx = exTranslator.translate("select", sql, e);
// ๋ณํํ ์์ธ๋ฅผ ๊ทธ๋๋ก ๋์ง ์๋ ์๋ค.
throw resultEx;
์คํ๋ง์ผ๋ก DB๋ฅผ ์ฌ์ฉํด๋ดค๋ค๋ฉด ์๊ฐ๋ณด๋ค ์๋ฌ ์ข ๋ฅ๊ฐ ์์ธํ๊ฒ ๊ตฌ๋ถ๋์ด์๋๊ฑธ ์ ์ ์๋๋ฐ, ์ด๋ ์คํ๋ง์ด ๊ฐ DB ๊ฐ ๋ฐํํ๋ ์๋ฌ๋ณ๋ก class SqlErrorCodes ๊ฐ์ฒด๋ฅผ ์ ์ํด๋์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ org.springframework.jdbc.support.sql-error-codes.xml ํ์ผ์ ํตํด ๋น์ด ์์ฑ๋๋ค.
์ฐธ๊ณ ๋ก ์ฌ๋ฌ ๊ณผ์ ์ ๊ฑฐ์ณค์์๋ ์ ์๋ ์๋ฌ๋ฅผ ์ฐพ์ง ๋ชปํ๋ค๋ฉด, ์๋ฌ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ DataAccessException์ ์์ํ UncategorizedSQLException ์ ๋์ง๋ค. ๋ฌผ๋ก ์ด๋ ๊ตฌํ์ฒด์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์๋ค.
์ด์ฐ๋ณด๋ฉด ๋น์ฐํ๊ฑฐ์ง๋ง, ํด๋น ๊ธฐ๋ฅ์ ํธํ๊ฒ ์ฐ๋ ค๋ฉด Spring์์ ์ ๊ณตํด์ฃผ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
Spring์ ์์กด์ฑ๊น์ง ์ ๊ฑฐํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฅผ ์ฐธ์กฐํด์ ์ง์ ์๋ฐ์ฝ๋๋ก ๊ตฌํํด๋ ๋์ง๋ง, ๋๋ถ๋ถ์ ์๋น์ค๋ DB ๋ฅผ ์ถ์ํํ ๋ถ๋ถ์ ๋๋ฉ์ธ ๋ก์ง์ฒ๋ผ ์์ํ Java (POJO)๋ก ๊ฐ์ ธ๊ฐ ํ์๋ ์๋ค. Spring Framework๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค๋ฉด ๋๋ถ๋ถ ํฌ๊ฒ ์ ๊ฒฝ์ฐ์ง์์๋ ๋๋ ๋ถ๋ถ์ด๋ค.
๐ ์ฌ๋ด
๊ทผ๋ฐ ์คํ๋ง์ด ์ด ๋ชจ๋ ๊ฑธ ๋คํด์ค JdbcTemplte ์ ์ฌ์ฉํ๋๋ผ๋ ์ฌ์ค์ ์ค์ ํ๋ก๋์ ์ฝ๋๋ก ์ฌ์ฉํ๋ค๋ณด๋ฉด ๊ทธ๋ ๊ฒ ์์ฒญ ํธ๋ฆฌํ์ง๋ ์๋ค.
@Repository
class EmployeeRepository(private val jdbcTemplate: JdbcTemplate) {
fun findEmployeesWithJoin(minSalary: Double, maxSalary: Double, departmentName: String, positionTitle: String): List<Employee> {
val sql = """
SELECT
e.*,
d.id as department_id, d.name as department_name,
p.id as position_id, p.title as position_title
FROM employees e
INNER JOIN departments d ON e.department_id = d.id
INNER JOIN positions p ON e.position_id = p.id
WHERE e.salary BETWEEN ? AND ?
AND d.name = ?
AND p.title = ?
ORDER BY e.lastName, e.firstName
"""
return jdbcTemplate.query(
sql,
arrayOf(minSalary, maxSalary, departmentName, positionTitle),
EmployeeRowMapper()
)
}
}
class EmployeeRowMapper : RowMapper<Employee> {
override fun mapRow(rs: ResultSet, rowNum: Int): Employee {
return Employee(
id = rs.getLong("id"),
firstName = rs.getString("firstName"),
lastName = rs.getString("lastName"),
// ๊ธฐํ Employee ํ๋
department = Department(
id = rs.getLong("department_id"),
name = rs.getString("department_name")
// ๊ธฐํ Department ํ๋
),
position = Position(
id = rs.getLong("position_id"),
title = rs.getString("position_title")
// ๊ธฐํ Position ํ๋
)
// ๊ธฐํ ํ๋
)
}
}
๋ฌผ๋ก ORM (ex JPA + QueryDsl ์กฐํฉ)์ผ๋ก ๋๋ถ๋ถ ํด๊ฒฐ๋์ง๋ง, ํน์ DB์ ์ข ์๋ ๊ณ ๊ธ ์ฟผ๋ฆฌ๋ ํต๊ณ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ ๊ทธ๊ฒ ๋ถ๊ฐ๋ฅ ํ ๊ฒฝ์ฐ๋ ๋ง๋ค. ๊ทธ๋ฐ ์ฟผ๋ฆฌ์ JPA ๊ฐ ๋๋ฐ๋ก ์์ฑํ๋์ง ๋ฏฟ๊ธฐ ํ๋ ๊ฒ๋ ์๊ณ
์ด๋ฐ ๋ฅ์ ๊ธฐ์ ๋ก ์ฌ๋ฌ๊ฐ์ง๋ฅผ ์ฐพ์๋ดค์๋๋ฐ, ์ ๋ถ ์ฅ๋จ์ ์ด ์์ด ์ ๋งคํ์๋ค.
๋ฌผ๋ก DB๋ก ํด๊ฒฐํ์ง์๊ณ ๊ฒ์์ด๋ฉด Elasticsearch, ๋์ฉ๋ ์ฒ๋ฆฌ๋ Hadoop์ ๋ฐ์ด๋ฃ๊ณ Google BigQuery๋ก ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋๋ฐ ๊ทธ๊ฑด ๊ธฐ์ ์ด ํฐ ๊ฒฝ์ฐ์๋ง ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์ด๋ ์ ๊น ๋ฐ์ด๋๊ณ , ๊ฐ๋ฅํ ๋ฐฉ๋ฒ๋ค์ ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ๋ค.
- Spring JDBC Template (์ ์์ ์ฝ๋)
- Jooq ( native sql์ ๋ง์น JPA์ฒ๋ผ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ )
- MyBatis ( ์ ํธํ์ง๋ ์์ง๋ง Java8 ์ดํ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ ์ผํ ์ ํ์ง ๐ฅฒ )
- JPA + ๋ณต์กํ ์ฟผ๋ฆฌ๋ง EntityManager.createNativeQuery ์ฌ์ฉ
์ด๋ฐ ๊ณ ๋ฏผ์ ๋๋ง ํ๊ฒ ์๋์ง SpringBoot 3.0 ์ JDBC Client ๋ผ๋๊ฒ ์ถ๊ฐ๋์๋๋ผ. ์๊ฐ๋ณด๋ค ์ธ๋งํด๋ณด์ฌ์ ๋์ค์ ์จ๋ณด๊ณ ๊ธ๋ก ํ๊ธฐ๋ฅผ ์จ๋ณผ ์์ ์ด๋ค.
@Repository
class EmployeeRepository(private val jdbcAggregateOperations: JdbcAggregateOperations) {
fun findEmployeeById(employeeId: Long): Employee? {
return jdbcAggregateOperations.findById(employeeId, Employee::class.java)
}
fun findAllEmployees(): List<Employee> {
return jdbcAggregateOperations.findAll(Employee::class.java)
}
}
์ฐธ๊ณ ๋ก Jpa์์ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ๋ค์ค, Spring Data์๋ง ์์กดํ๊ณ ์๋ ๊ธฐ๋ฅ๋ค๋ ๋ง๋ค. ์๋์ ๊ฐ์ด Spring Data์ Criteria๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋ค
/** Spring Data์ Criteria ํ์ฉ */
@Repository
class EmployeeRepository(private val jdbcAggregateTemplate: JdbcAggregateTemplate) {
fun findEmployeesWithJoin(minSalary: Double, maxSalary: Double, departmentName: String, positionTitle: String): List<Employee> {
val query = Query.query(
Criteria.where("e.salary").between(minSalary, maxSalary)
.and("d.name").`is`(departmentName)
.and("p.title").`is`(positionTitle)
).sort(Sort.by("e.lastName").and(Sort.by("e.firstName")))
return jdbcAggregateTemplate.find(query, Employee::class.java, "employees e")
.innerJoin("departments d", "e.department_id = d.id")
.innerJoin("positions p", "e.position_id = p.id")
.all()
}
}
๊ถ๊ธํ๋ค๋ฉด ์๋ ๋งํฌ๋ฅผ ํ๋ฒ์ฏค ๊ผญ ๋ณด๋๊ฑธ ์ถ์ฒํ๋ค.
์ค๋ช - https://www.baeldung.com/spring-6-jdbcclient-api
์ ํ๋ธ - ํ์ฐฎ์์คํ๋ JdbcClient์ record๋ฅผ ๊ฒฐํฉํ๋ฉด ์๋์ง๊ฐ ์ปค์ง๋๋ค.
'๐ฑBackend > JDBC & JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring DB ๊ธฐ์ ํํค์น๊ธฐ #2 (0) | 2024.01.11 |
---|---|
JDBC Connection ์ ๋ํ ์ดํด, HikariCP ์ค์ ํ (0) | 2023.11.21 |
์คํ๋งJPA์ ์์์ฑ์ปจํ ์คํธ (EntityManager) (0) | 2022.02.03 |
QueryDSL + JPA (0) | 2022.02.02 |
Spring Data JPA (0) | 2022.01.31 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev