Skip to content
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

[JDBC 라이브러리 구현하기 - 4단계] 이리내(성채연) 미션 제출합니다. #588

Merged
merged 8 commits into from
Oct 12, 2023

Conversation

hectick
Copy link

@hectick hectick commented Oct 9, 2023

안녕하세요 채채
빨간날이지만 힘내서 코딩을 마저 해보았습니다

3단계때 코멘트 달아주신 부분에 대한 답변은 달아놓았으니 확인해주세용

이번 단계를 진행하면서 모든 요청은 반드시 TxUserService를 거칠 것이라 생각하고 만들었습니다.
왜냐하면 언제는 트랜잭션 없이 바로 AppUserService를 호출하고 언제는 트랜잭션 있게 TxUserService를 호출하고 이것을 현재 코드상으로 어떻게 구분해줄지 모르겠거든요
그래서 jdbcTemplate에는 따로 connection을 close하는 로직을 넣지 않았어요. 어차피 TxUserService의 TransactionExecutor가 해줄테니까용ㅎㅎ,..

그럼 리뷰 잘부탁해용😘

Copy link

@chaewon121 chaewon121 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이리내 안녕하세요~! 이번 미션 어려웠는데 이리내는 잘 구현해주셨군요~! 저도 아직 트렌젝션 커넥션처리 등등에 관해서 이해도가 높지않아서 이리내의 의견이 궁금한 점들이 있어서 코멘트로 남겨두었습니다~! 커넥션을 닫는 로직에 대해서 좀더 의견을 나누고 싶네요~!

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Map;

public abstract class TransactionSynchronizationManager {

private static final ThreadLocal<Map<DataSource, Connection>> resources = new ThreadLocal<>();
private static final ThreadLocal<Map<DataSource, Connection>> resources = ThreadLocal.withInitial(HashMap::new);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withInitial 잘 사용하셨군요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지선생한테 배웠습니다 ㅋ.ㅋ

Comment on lines 15 to 17
@Nullable
public static Connection getResource(DataSource key) {
return null;
return resources.get().getOrDefault(key, null);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인자에final을 붙여준다면 코드컨벤션에 더 맞는 코드가 될것같아요!ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

힘내볼게요

Comment on lines +21 to +24
@Override
public void insert(final User user) {
transactionExecutor.execute(() -> userService.insert(user));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

캬 나머니 코드도 모두 트렌젝션을 적용해주셨군요 굳입니다

@@ -30,6 +30,7 @@ public static Connection getConnection(DataSource dataSource) throws CannotGetJd
public static void releaseConnection(Connection connection, DataSource dataSource) {
try {
connection.close();
TransactionSynchronizationManager.unbindResource(dataSource);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connection.close(); 에서 예외가 발생하더라도
TransactionSynchronizationManager.unbindResource(dataSource);가 일어날 수 있도록 하는것이 좋을까요? 예외가 발생하면 일어나지 않도록 하는것이 좋을까요? 저도 고민이 되었었는데 이리내와 얘기를 나눠보고싶네요!

Copy link
Author

@hectick hectick Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러게요 생각해보지 못했는데 고민할만한 내용인 것 같습니다
생각해보니 저라면 unbind 해줄 것 같아요
쓰레드가 재사용되는 상황이라면, 이전 Connection이 비워지지 않고 남아있으면 위험하지 않을까요??

return executeWithConnection(connection, preparedStatementCallback, executionCallback);
} catch (SQLException e) {
throw new DataAccessException(e);
}
}

public int update(final Connection connection, final String sql, final Object... args) {
return executeWithConnection(connection, conn -> prepareStatement(sql, conn, args), PreparedStatement::executeUpdate);
}

private <T> T executeWithConnection(final Connection connection,
final PreparedStatementCallback preparedStatementCallback,
final ExecutionCallback<T> executionCallback) {
final Connection connection = DataSourceUtils.getConnection(dataSource);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 트렌젝션 밖에서 해당 함수가 호출이 되었다면 커넥션을 어떻게 닫을 수 있을까요!? 저도 이부분에 대해서 많이 고민해 보았는데요! 만약 트렌젝션이 실행중이라면 닫지 않고, 실행중이 아니라면 커넥션을 닫는 로직에 대해서 이리내의 의견이 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 코드리뷰를 하다가 힌트를 좀 얻었는데요
실제로 ConnectionHolder라는 클래스가 존재하더라구요
Connection 객체를 한번 더 감싸준 클래스인데요 필드에 transactionActive가 있어서 이걸 토대로 트랜잭션이 실행중인지 아닌지 판단해서 커넥션을 닫아줄지 말지를 결정할 수 있을 것 같아요
결론은 Connection을 ConnectionHolder로 한번 더 감싸주고 필드에 transactionActive를 추가하는 것!! 입니다.
image

Comment on lines 21 to 25
resources.get().put(key, value);
}

public static Connection unbindResource(DataSource key) {
return null;
return resources.get().remove(key);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get()의 중복제거에 대해서 이리내는 어떻게 생각하시나요?

Copy link
Author

@hectick hectick Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get()의 중복 제거가 resources.get()을 따로 메서드로 빼는 것인가요?!
필드가 어떻게 생겼는지 인지를 못했다면(ThreadLocal로 감싸져있는 것을 모른다면) 현재 코드를 봤을 때 resources에서 왜 get을 해와서 지우는거지? 생각할 수도 있을 것 같아요(실제로 제가 그랬음)
getResources().remove(key) 이러면 더 직관적이고 좋은 것 같슴니다

Copy link

@chaewon121 chaewon121 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이리내 안녕하세요~! 학습테스트까지 모두 하시느라 고생 많으셨습니다. 저의 지식을 총동원해서 리뷰를 남겼었는데 모두 적용해주셨네요 ..감동받았구요..
이리내의 답변을 보고 저도 힌트를 많이 얻어갈 수 있었네요!
이만 머지하도록 하겠습니다~!

if(connectionHolder.isTransactionActive()) {
return;
}
TransactionSynchronizationManager.unbindResource(dataSource);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저또한 예외가 발생하더라도 비워주는것이 현재코드로서는 안전하다는 생각이 드네요
저같은 경우는 final로 처리를 하였는데 여기 순서를 변경하는 방법으로 구현하셨군요~!이런 방법도 있네요!?

Comment on lines 17 to 29
return getResources().getOrDefault(key, null);
}

public static void bindResource(final DataSource key, final Connection value) {
resources.get().put(key, value);
getResources().put(key, value);
}

public static Connection unbindResource(final DataSource key) {
return resources.get().remove(key);
return getResources().remove(key);
}

private static Map<DataSource, Connection> getResources() {
return resources.get();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

취향차이일수도 있지만 더 가독성이 올라간 코드가 된것같네욯ㅎ

Comment on lines +6 to +13

public class ConnectionHolder {

private Connection connection;
private boolean transactionActive = false;

public ConnectionHolder(final Connection connection) {
this.connection = connection;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코멘트에 남겨주신것처럼 커넥션 홀더를 적용해주셨네요! 감동1

@chaewon121 chaewon121 merged commit 53077b2 into woowacourse:hectick Oct 12, 2023
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants