18 JDBC 템플릿 활용
p355 - src/resources/application.properties
# Application Name
spring.application.name=template
# MySQL Database Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=1234
# HikariCP Configuration
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.pool-name=HikariPool
# MyBatis
mybatis.type-aliases-package=spring.jdbc.template.domain
mybatis.configuration.map-underscore-to-camel-case=true
logging.level.spring.jdbc.template.mybatis=trace
logging.level.spring.jdbc.template.repository=trace
logging.level.org.springframework.transaction.interceptor=TRACE
# JPA
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
p356 - build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'spring.jdbc'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' // MyBatis 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA 추가
}
tasks.named('test') {
useJUnitPlatform()
}
p357 - src/main/java/spring/jdbc/template/domain/User
@Getter @Setter
public class User {
private Long userIdx;
private String userId;
private int age;
public User(String userId, int age) {
this.userId = userId;
this.age = age;
}
}
p358 - src/main/java/spring/jdbc/template/repository/UserRepository
public interface UserRepository {
// 유저 저장 메서드
User save(User user);
// 유저 정보 업데이트 메서드
void update(Long userIdx, UserUpdateDto userUpdateDto);
// 유저 ID로 찾기 메서드
Optional<User> findByIdx(Long userIdx);
// 모든 유저 찾기 메서드 (검색 조건을 이용할 수 있음)
List<User> findAll(UserListDto dto);
}
p359 ~ p363 - src/main/java/spring/jdbc/template/repository/UserRepositoryImpl
@Repository
public class UserRepositoryImpl implements UserRepository {
private final JdbcTemplate jdbcTemplate;
// JdbcTemplate을 생성자 주입으로 받음
public UserRepositoryImpl(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// RowMapper를 사용하여 ResultSet을 User 객체로 변환
private final RowMapper<User> userRowMapper = (rs, rowNum) -> {
User user = new User(rs.getString("userId"), rs.getInt("age"));
user.setUserIdx(rs.getLong("userIdx"));
return user;
};
@Override
public User save(User user) {
String sql = "INSERT INTO users (userId, age) VALUES (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, user.getUserId());
pstmt.setInt(2, user.getAge());
return pstmt;
}, keyHolder);
// 생성된 userIdx 값을 User 객체에 설정
user.setUserIdx(keyHolder.getKey().longValue());
return user;
}
@Override
public void update(Long userIdx, UserUpdateDto updateParam) {
String sql = "UPDATE users SET userId = ?, age = ? WHERE userIdx = ?";
jdbcTemplate.update(sql, updateParam.getUserId(), updateParam.getAge(), userIdx);
}
@Override
public Optional<User> findByIdx(Long userIdx) {
String sql = "SELECT * FROM users WHERE userIdx = ?";
List<User> result = jdbcTemplate.query(sql, userRowMapper, userIdx);
return result.stream().findAny(); // Optional로 반환
}
@Override
public List<User> findAll(UserListDto dto) {
String sql = "SELECT * FROM users WHERE 1=1";
// 동적 쿼리 생성
StringBuilder query = new StringBuilder(sql);
if (dto.getUserId() != null && !dto.getUserId().isEmpty()) {
query.append(" AND userId LIKE '%").append(dto.getUserId()).append("%'");
}
if (dto.getMinAge() != null) {
query.append(" AND age >= ").append(dto.getMinAge());
}
if (dto.getMaxAge() != null) {
query.append(" AND age <= ").append(dto.getMaxAge());
}
return jdbcTemplate.query(query.toString(), userRowMapper);
}
}
p364 - src/main/java/spring/jdbc/template/service/UserService
public interface UserService {
// 유저 생성
User save(User user);
// 유저 정보 업데이트
void update(Long userIdx, UserUpdateDto userUpdateDto);
// 유저 ID로 찾기
Optional<User> findByIdx(Long userIdx);
// 모든 유저 조회 (검색 조건 적용)
List<User> findAll(UserListDto userListDto);
}
p365 - src/main/java/spring/jdbc/template/service/UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
// UserRepository를 생성자 주입
// public UserServiceImpl(@Qualifier("userRepositoryImpl")UserRepository userRepository) {
// public UserServiceImpl(@Qualifier("userRepositoryNameParameterImpl")UserRepository userRepository) {
public UserServiceImpl(@Qualifier("userRepositoryMyBatisImpl")UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User save(User user) {
// 유저를 저장하는 로직
return userRepository.save(user);
}
@Override
public void update(Long userIdx, UserUpdateDto updateParam) {
// 유저 정보를 업데이트하는 로직
userRepository.update(userIdx, updateParam);
}
@Override
public Optional<User> findByIdx(Long userIdx) {
// 유저 ID로 유저를 조회하는 로직
return userRepository.findByIdx(userIdx);
}
@Override
public List<User> findAll(UserListDto userListDto) {
// 유저 목록을 검색 조건에 맞춰 조회하는 로직
return userRepository.findAll(userListDto);
}
}
p367 ~ 370 - src/main/java/spring/jdbc/template/repository/UserRepositoryNameParameterImpl
@Repository
public class UserRepositoryNameParameterImpl implements UserRepository {
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// NamedParameterJdbcTemplate을 생성자 주입으로 받음
public UserRepositoryNameParameterImpl(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
// RowMapper를 사용하여 ResultSet을 User 객체로 변환
private final RowMapper<User> userRowMapper = (rs, rowNum) -> {
User user = new User(rs.getString("userId"), rs.getInt("age"));
user.setUserIdx(rs.getLong("userIdx"));
return user;
};
@Override
public User save(User user) {
String sql = "INSERT INTO users (userId, age) VALUES (:userId, :age)";
KeyHolder keyHolder = new GeneratedKeyHolder();
// 파라미터 설정
MapSqlParameterSource params = new MapSqlParameterSource()
.addValue("userId", user.getUserId())
.addValue("age", user.getAge());
// update 메서드를 통해 INSERT 쿼리 실행
namedParameterJdbcTemplate.update(sql, params, keyHolder, new String[]{"userIdx"});
// 생성된 userIdx 값을 User 객체에 설정
user.setUserIdx(keyHolder.getKey().longValue());
return user;
}
@Override
public void update(Long userIdx, UserUpdateDto dto) {
String sql = "UPDATE users SET userId = :userId, age = :age WHERE userIdx = :userIdx";
// 파라미터 설정
MapSqlParameterSource params = new MapSqlParameterSource()
.addValue("userId", dto.getUserId())
.addValue("age", dto.getAge())
.addValue("userIdx", userIdx);
// update 메서드를 통해 UPDATE 쿼리 실행
namedParameterJdbcTemplate.update(sql, params);
}
@Override
public Optional<User> findByIdx(Long userIdx) {
String sql = "SELECT * FROM users WHERE userIdx = :userIdx";
// 파라미터 설정
Map<String, Object> params = new HashMap<>();
params.put("userIdx", userIdx);
List<User> result = namedParameterJdbcTemplate.query(sql, params, userRowMapper);
return result.stream().findAny(); // Optional로 반환
}
@Override
public List<User> findAll(UserListDto dto) {
String sql = "SELECT * FROM users WHERE 1=1";
// 동적 쿼리 생성 및 파라미터 설정
StringBuilder query = new StringBuilder(sql);
MapSqlParameterSource params = new MapSqlParameterSource();
if (dto.getUserId() != null && !dto.getUserId().isEmpty()) {
query.append(" AND userId LIKE :userId");
params.addValue("userId", "%" + dto.getUserId() + "%");
}
if (dto.getMinAge() != null) {
query.append(" AND age >= :minAge");
params.addValue("minAge", dto.getMinAge());
}
if (dto.getMaxAge() != null) {
query.append(" AND age <= :maxAge");
params.addValue("maxAge", dto.getMaxAge());
}
return namedParameterJdbcTemplate.query(query.toString(), params, userRowMapper);
}
}
p372 ~ p375 - src/test/java/spring/jdbc/template/repository/UserRepositryNameParameterImplTest
@Slf4j
@Transactional
@SpringBootTest
class UserRepositoryNameParameterImplTest {
@Autowired
@Qualifier("userRepositoryNameParameterImpl") // 특정 빈을 명시적으로 주입
private UserRepository userRepository;
@Autowired
private JdbcTemplate jdbcTemplate; // JdbcTemplate 주입
private Long userIdx1;
private Long userIdx2;
@BeforeEach
void setUp() {
// 데이터를 저장하고 각각의 userIdx를 저장
User user1 = userRepository.save(new User("testUser1", 25));
User user2 = userRepository.save(new User("testUser2", 32));
userIdx1 = user1.getUserIdx();
userIdx2 = user2.getUserIdx();
}
@Test
void testSave() {
User user = new User("testUser3", 35);
User savedUser = userRepository.save(user);
assertNotNull(savedUser.getUserIdx()); // Auto-generated ID should not be null
assertEquals("testUser3", savedUser.getUserId());
assertEquals(35, savedUser.getAge());
}
@Test
void testUpdate() {
Optional<User> userOpt = userRepository.findByIdx(userIdx1);
assertTrue(userOpt.isPresent());
User user = userOpt.get();
UserUpdateDto dto = new UserUpdateDto("updatedUser1",40);
dto.setAge(40);
dto.setUserId("updatedUser1");
userRepository.update(user.getUserIdx(), dto);
Optional<User> updatedUserOpt = userRepository.findByIdx(user.getUserIdx());
assertTrue(updatedUserOpt.isPresent());
User updatedUser = updatedUserOpt.get();
assertEquals("updatedUser1", updatedUser.getUserId());
assertEquals(40, updatedUser.getAge());
}
@Test
void testFindByIdx() {
// userIdx1을 기준으로 조회
Optional<User> userOpt = userRepository.findByIdx(userIdx1);
assertTrue(userOpt.isPresent());
User user = userOpt.get();
assertEquals("testUser1", user.getUserId());
assertEquals(25, user.getAge());
}
@Test
void testFindAll() {
UserListDto dto = new UserListDto(null, 20, 35);
List<User> users = userRepository.findAll(dto);
assertEquals(2, users.size());
}
@Test
void testFindAllWithUserIdFilter() {
UserListDto dto = new UserListDto("testUser1", null, null);
List<User> users = userRepository.findAll(dto);
assertEquals(1, users.size());
assertEquals("testUser1", users.get(0).getUserId());
}
}
Last updated