-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
๐ 4๋จ๊ณ - ํธ๋์ญ์ ๋๊ธฐํ ๊ตฌํํ๊ธฐ #41
base: sm9171
Are you sure you want to change the base?
Conversation
# Conflicts: # README.md # app/src/main/java/camp/nextstep/dao/UserDao.java # app/src/main/java/camp/nextstep/dao/UserRowMapper.java
# Conflicts: # README.md
- update ๋ฉ์๋์์ connection์ DataSourceUtils์์ ๊ฐ์ ธ์ค๋๊ฑธ๋ก ๋ณ๊ฒฝ
# Conflicts: # README.md # app/src/main/java/camp/nextstep/dao/UserHistoryDao.java # app/src/main/java/camp/nextstep/service/UserService.java # app/src/test/java/camp/nextstep/service/UserServiceTest.java
๊ทธ๋ฆฌ๊ณ 3๋ฒ์งธ ๋ฏธ์
๋ ์ฃผ์ ์ง๋ฌธ์ด ์ ํํ
๋ ์ด๋ ค์์ ํน์ ์ ์๋์ ์๊ฐ์ ์ด๋ ์ ์ง ๊ถ๊ธํฉ๋๋ค. ๋์๋ฏผ ๋! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๋์๋ฏผ๋!
๋๋ฒ๊น
๋์ ์์ฒญ์ฃผ์ ๊ฒ์ ๋ํด ์๊ฒฌ ๋จ๊ฒจ๋ณด์์ต๋๋ค.
์ ์ถ์ธก์ DataSourceUtils.releaseConnection
๊ณผ๋ ๋ณ๊ฐ๋ก
try-with-resources ๋ฌธ๋ฒ์์ ํ๋ฒ ๋ connection ์ close ํ๋ ค ํด์ ๋ฐ์ํ ๋ฒ๊ทธ๊ฐ ์๋๊ฐ ์ถ์ต๋๋ค.
ํ์ฌ Transaction ๊ด๋ จ ๊ฐ์ฒด๋ค์ ๊ดํ ํ
์คํธ๊ฐ ์ ๋ฌดํ๋ฐ,
ํ
์คํธ ์์ฑ ๋ถํ๋๋ฆฝ๋๋ค!
try (final Connection connection = DataSourceUtils.getConnection(dataSource); | ||
final PreparedStatement pstmt = connection.prepareStatement(sql)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try (final Connection connection = DataSourceUtils.getConnection(dataSource); | |
final PreparedStatement pstmt = connection.prepareStatement(sql)) { | |
try { | |
final Connection connection = DataSourceUtils.getConnection(dataSource); | |
final PreparedStatement pstmt = connection.prepareStatement(sql); |
๋ฏธ์ ์ ๊ตฌํํ๋๋ฐ UserServiceTest์์ testChangePassword ํ ์คํธ์์ ์คํจ๋ฅผ ํ๊ณ ์์ต๋๋ค.
๋๋ฒ๊น ์ ํ์ ๋ AppUserService์์ userHistoryDao.log(new UserHistory(user, createBy)); ๋ฅผ ์คํํ ๋ jdbcTemplateํด๋์ค์ update๋ฉ์๋์์ `PreparedStatement๋ฅผ ํ ๋นํ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ผ๊ตฌ์.
์ ๊ฐ ๋ก๊ทธ์ ํ์ธํ ์๋ฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
org.h2.jdbc.JdbcSQLNonTransientException: The object is already closed
์ด๋ฏธ closed ๋ ์์์ ์ฌ์ฉํ๊ณ ์๋ค๊ณ ๋ก๊ทธ๊ฐ ๋งํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฏธ์
์์ closed ๋ ์์์ด๋ ๊ณง connection ์ ๋ปํฉ๋๋ค.
Try-With-Resources ๋ฌธ๋ฒ์ ๋ํด์ ๋ ๊น๊ฒ ๊ณต๋ถํด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์ง๊ธ๊ณผ ๊ฐ์ ์ํฉ์์๋ update ๋ฉ์๋๋ฅผ ํธ์ถํ ๋๋ง๋ค, connection ์ด ๋ฐ๋ก closed ๋๊ฒ ๋์ด,
๊ฐ์ ํธ๋์ญ์
์์ ์๋ ๋ค๋ฅธ update ์์ connection ์ ์ฌํ์ฉํ์ง ๋ชปํฉ๋๋ค.
๊ฐ์ ํธ๋์ญ์
์ด๋ผ๋ฉด connection ์ด closed ๋์ง ์๊ณ , ๊ฐ์ connection ์ ์ฌ์ฌ์ฉํ ์ ์๊ณ ,
ํธ๋์ญ์
์ด ์๋ฃ๋์์ ๋ connection ์ด closed ๋๋๋ก ํด์ฃผ์ธ์.
์ฐธ๊ณ ๋ก ๋งจ ์์ suggestion code ๋ ์ด๋ ๊ฒ ํ๋ฉด ํ
์คํธ๋ฅผ ํต๊ณผํ ์ ์๋ค์ด์ง,
์ด๋ ๊ฒ ๊ณ ์ณ์ผ ํ๋ค๋ ๋ป์ด ์ ๋ ์๋๋๋ค.
์์ ์ฝ๋์์๋ ๊ทธ๋์ connection ์ ์ด๋์ ์ด๋ป๊ฒ close ํ ๊ฒ์ธ์ง์ ๋ํ ๋ถ๋ถ์ด ๋น ์ ธ์๋๋ฐ,
์ด ๋ถ๋ถ์ ๊ตฌํํด์ฃผ์๊ธฐ ๋ฐ๋๋๋ค.
public class TxUserService implements UserService { | ||
private final UserService userService; | ||
private final TransactionTemplate transactionTemplate; | ||
|
||
@Autowired | ||
public TxUserService(final UserService userService, DataSource dataSource) { | ||
this.userService = userService; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DI ๋ฅผ ํ๋ ์ํฉ์ ๊ฐ์ ํ๊ณ ํ๋ผ๋ฏธํฐ ํ์
์ ๊ณ ๋ คํ๋ฉด ์ข์ง ์์๊น ์ถ์ต๋๋ค.
๊ฐ๋ น ํ์ฌ UserService ๋ฅผ implements ํ๋ ํด๋์ค๋ TxUserService ์ AppUserService 2๊ฐ๋ผ์,
๋์ค์ ํ๋ ์์ํฌ๊ฐ DI ๋ฅผ ํ๋ คํ ๋ ์ด๋ค ํด๋์ค๋ฅผ ์์กด์ฑ ์ฃผ์
์ ํด์ผํ ์ง ํท๊ฐ๋ฆด ๊ฒ ๊ฐ์ต๋๋ค.
๋ด๋ถ ๋งด๋ฒ์ ํ์
์ด UserService ์ด๋๋ผ๋,
DI ํ๋ ์์ํฌ๊ฐ ํ์ธํ๋ ์์ฑ์ ํ๋ผ๋ฏธํฐ์ ํ์
์ AppUserService ๋ก ๋ช
์ํ๋๊ฒ ๋ ์์ ํ์ง ์์๊น ์ ์ํด๋ด
๋๋ค.
@Override | ||
public void changePassword(final long id, final String newPassword, final String createdBy) { | ||
transactionTemplate.execute(ignored -> { | ||
userService.changePassword(id, newPassword, createdBy); | ||
return null; | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
๋ฏธ์ ์๋์ ์ผ์นํ๊ฒ transactionTemplate ์ ํ์ฉํด์ฃผ์ จ์ต๋๋ค. ๐
public <T> T execute(TransactionCallback<T> action) { | ||
TransactionStatus status = transactionManager.getTransaction(); | ||
|
||
try { | ||
T result = action.doInTransaction(status); | ||
transactionManager.commit(status); | ||
return result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TransactionCallback ์ ๋ณ๋ ์ธํฐํ์ด์ค๋ก ๋ฌด์ฒ ์ ์ ์ธํด์ฃผ์
จ์ต๋๋ค. ๐
์์ธ๋ก Callback ์ธํฐํ์ด์ค๋ฅผ ์ค์ํ๊ฒ ์๊ฐ์ง ์๊ฒ ๋๊ธฐ๋ ๊ฒฝ์ฐ๊ฐ ์ข
์ข
์๋๋ผ๊ณ ์.
public void commit(TransactionStatus status) { | ||
Connection connection = status.connection(); | ||
|
||
status.checkConnectionActive(); | ||
try { | ||
connection.commit(); | ||
} catch (SQLException e) { | ||
throw new DataAccessException(e); | ||
} | ||
DataSourceUtils.releaseConnection(connection, dataSource); | ||
} | ||
|
||
public void rollback(TransactionStatus status) { | ||
Connection connection = status.connection(); | ||
|
||
status.checkConnectionActive(); | ||
try { | ||
connection.rollback(); | ||
} catch (SQLException e) { | ||
throw new DataAccessException(e); | ||
} finally { | ||
DataSourceUtils.releaseConnection(connection, dataSource); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commit ํน์ rollback ์ด ์๋ฃ๋ ์ดํ์๋ DataSourceUtils.releaseConnection
๋ฅผ ํตํด connection ์ ํด๋ฐฉํ๋ ๊ตฐ์
connection.close(); | ||
} catch (SQLException ex) { | ||
throw new CannotGetJdbcConnectionException("Failed to close JDBC Connection"); | ||
} finally { | ||
TransactionSynchronizationManager.unbindResource(dataSource); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DataSourceUtils ์์ connection ์ close ๋ฅผ ๋ด๋นํ๊ณ ์๋ ๊ฒ ํ์ธํ์ต๋๋ค.
public static Connection getResource(DataSource key) { | ||
return null; | ||
final Map<DataSource, Connection> dataSourceConnectionMap = resources.get(); | ||
|
||
return dataSourceConnectionMap.get(key); | ||
} | ||
|
||
public static void bindResource(DataSource key, Connection value) { | ||
final Map<DataSource, Connection> dataSourceConnectionMap = resources.get(); | ||
|
||
if (dataSourceConnectionMap.containsKey(key)) { | ||
throw new IllegalStateException("Resource is already bound to key " + key); | ||
} | ||
|
||
dataSourceConnectionMap.put(key, value); | ||
} | ||
|
||
public static Connection unbindResource(DataSource key) { | ||
return null; | ||
return resources.get().remove(key); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
์ํ๊น์ด ์ ์ ์ด๋ฒ ๋ฏธ์
์ ํต์ฌ์ธ transaction/support
ํจํค์ง ๋ด๋ถ์ ๊ฐ์ฒด์ ๋ํ ํ
์คํธ๊ฐ ์ ํ ์กด์ฌํ์ง ์๋ ๊ตฐ์.
๊ฐ์ ํธ๋์ญ์
์ด๋ผ๋ฉด ๊ฐ์ connection ์ธ์ง,
ํธ๋์ญ์
์ด ๋ฌ๋ผ์ง๋ค๋ฉด connection ์ด ๋ฌ๋ผ์ง๋์ง,
ํ๋ฒ unbind ๋ resource ๋ ๋ค์ ์กฐํ๊ฐ ๋ถ๊ฐ๋ฅํ๋์ง ๋ฑ๋ฑ
์ ์๋ฏธํ ํ
์คํธ๋ค์ ์์ฑํด์ฃผ์๊ธฐ ๋ถํ๋๋ฆฝ๋๋ค.
์๋ ํ์ธ์ ์ ์๋
๋ฏธ์ ์ ๊ตฌํํ๋๋ฐ
UserServiceTest
์์testChangePassword
ํ ์คํธ์์ ์คํจ๋ฅผ ํ๊ณ ์์ต๋๋ค.๋๋ฒ๊น ์ ํ์ ๋
AppUserService
์์userHistoryDao.log(new UserHistory(user, createBy));
๋ฅผ ์คํํ ๋jdbcTemplate
ํด๋์ค์update
๋ฉ์๋์์ `PreparedStatement๋ฅผ ํ ๋นํ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋๋ผ๊ตฌ์.ํผ์ ์์ธ ํ์ ์ด ์ด๋ ค์์ ํด๋น ๋ด์ฉ ๊ณต์ ๋๋ฆฝ๋๋ค.
๊ทธ๋ฆฌ๊ณ 3๋ฒ์งธ ๋ฏธ์ ๋ ์ฃผ์ ์ง๋ฌธ์ด ์ ํํ ๋ ์ด๋ ค์์ ํน์ ์ ์๋์ ์๊ฐ์ ์ด๋ ์ ์ง ๊ถ๊ธํฉ๋๋ค.
#39 (comment)