[Spring] JDBC 와 JDBC Template 이용해보기 본문

백앤드 개발일지/스프링부트

[Spring] JDBC 와 JDBC Template 이용해보기

giron 2021. 8. 15. 21:47
728x90

JPA만 사용하다가 오랜만에 JDBC를 공부해보려고 한다. 어느 정도 JDBC의 작동원리를 알아야 나중에 JPA를 더 깊이 공부할 때도 도움이 될 것 같다고 생각되기 때문이다. 기본적인 사용법을 알아두면 나중에 쓰일 수(?)도 있으니...

JDBC는 JPA와 다르게 db에 날리는 쿼리를 직접 작성해야하는 단점이 있다. 대신 복잡한 쿼리를 작성할때는 직접 쿼리를 작성하므로 jpa보다 좋다.

JDBC

package com.example.demo.controller;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class HomeController {
    
	@RequestMapping(value="step1", method = RequestMethod.GET)
	public List<SailorsDto> getSailorss() throws SQLException
	{
		List<SailorsDto> sailorsInfoList = new ArrayList<>();
		try(Connection conn = DriverManager.getConnection("jdbc:mariadb://localhost:3306/jdbc", "giron", "pass")){
			
			try(Statement stmt = conn.createStatement()){
				
				try(ResultSet rs = stmt.executeQuery("SELECT S.rating, AVG(S.age) AS average\n"
						+ "FROM Sailors S\n"
						+ "WHERE S.age >= 18\n"
						+ "GROUP BY S.rating\n"
						+ "HAVING 1<(SELECT COUNT(*)\n"
						+ "FROM Sailors S2\n"
						+ "WHERE S.rating = S2.rating);")){
					
					while(rs.next()) {
						String rating = rs.getString("rating");
						String average = rs.getString("average");
						sailorsInfoList.add(new SailorsDto(Integer.parseInt(rating), Double.parseDouble(average)));
					}
				}
			}
		}

		return sailorsInfoList;
	}
    
}

마리아디비 커넥터를 사용해 localhost:3306번 포트를 통해 접근하고 jdbc라는 데이터베이스에 접근한다. giron과 pass는 데이터베이스 아이디와 비밀번호이다!

 

Saliros Dto

package com.example.demo.controller;

public class SailorsDto {
	 
	   public Integer rating;
	   public Double age;
	   
	   public SailorsDto() {
	       
	   }
	   public SailorsDto(Integer rating, Double age) {
	      super();
	      this.rating = rating;
	      this.age = age;
	   }
	   

	   @Override
	   public String toString() {
	      return "Sailors [rating=" + rating + ", age=" + age + "]";
	   }
	}

마리아디비 jdbc connector를 이용하여 db로부터 쿼리를 날릴 수 있다.

 

 

이번엔 JDBC template을 이용하여 더욱 간편하게 코드를 작성해보겠다.

JDBC Template

[User Entity]

@Getter
@Setter
public class User {
	private Long id; 
	private String name;
}

[UserRepository]

public interface UserRepository {
    User save(User user);

    Optional<User> findById(Long id);

    Optional<User> findByName(String name);

    List<User> findAll();
}

[JdbcTemplateUserRepository]

public class JdbcTemplateUserRepository implements UserRepository {
    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public JdbcTemplateUserRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public User save(User user) {
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        jdbcInsert.withTableName("user").usingGeneratedKeyColumns("id"); //파라미터 바인딩 
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", user.getName()); // 실행 & db 에서 생성된 key 받아오기
        Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
        user.setId(key.longValue());
        return user;
    }

    @Override
    public Optional<User> findById(Long id) {
        List<User> result = jdbcTemplate.query("select * from user where id = ?", userRowMapper(), id);
        return result.stream().findAny();
    }

    @Override
    public Optional<User> findByName(String name) {
        List<User> result = jdbcTemplate.query("select * from user where id= ?", userRowMapper(), name);
        return result.stream().findAny();
    }

    @Override
    public List<User> findAll() {
        return jdbcTemplate.query("select * from user", userRowMapper());
    }

    private RowMapper<User> userRowMapper() {
        return (res, rowNum) -> {
            User user = new User();
            user.setId(res.getLong("id"));
            user.setName(res.getString("name"));
            return user;
        };
    }
}

기본 Config

@Configuration
public class SpringConfig {
	private final DataSource dataSource;
 	public SpringConfig(DataSource dataSource) {
 		this.dataSource = dataSource;
 	}
 	@Bean
 	public UserService userService() {
 		return new UserService(userRepository());
 	}
    //Bean 주입
	 @Bean
	 public UserRepository userRepository() {
		 return new JdbcTemplateUserRepository(dataSource);
 	 }
}

 

기본적인 사용법

 

jdbcTemplate.query("쿼리문", RowMapper<?>, 바인딩할 파라미터 값)

 

여기서 RowMapper는 결과값을 매핑하는 용도라고 보면 되고

바인딩할 파라미터는 순서대로 쿼리문의 ? 에 대응되어 값이 들어가게 된다.


Insert의 경우가 약간 특이한데

쿼리문을 따로 생성할 필요는 없고

SimpleJdbcInsert 를 생성하여 파라미터를 바인딩하여 사용하면 된다.

 

일반적으로는 jdbcTemplate.execute() 를 호출하면 쿼리가 날아간다.

 

Reference

 

728x90
Comments