DataSource를 쉽게 접근하기 위한 유틸 클래스, 트랜젝션매니져 및 다양한 DataSource 구현을 제공
org.springframework.jdbc.object
RDBMS 조회, 갱신, 저장등을 안전하고 재사용 가능한 객제 제공
org.springframework.jdbc.support
jdbc.core 및 jdbc.object를 사용하는 JDBC 프레임워크를 지원
JDBC Template
org.springframework.jdbc.core에서 가장 중요한 클래스입니다.
리소스 생성, 해지를 처리해서 연결을 닫는 것을 잊어 발생하는 문제 등을 피할 수 있도록 합니다.
스테이먼트(Statement)의 생성과 실행을 처리합니다.
SQL 조회, 업데이트, 저장 프로시저 호출, ResultSet 반복호출 등을 실행합니다.
JDBC 예외가 발생할 경우 org.springframework.dao패키지에 정의되어 있는 일반적인 예외로 변환시킵니다.
실습코드
JdbcTemplate select 예제1
열의 수 구하기
int rowCount = this.jdbcTemplate.queryForInt("select count(*) from t_actor");
JdbcTemplate select 예제2
변수 바인딩 사용하기
int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt("select count(*) from t_actor where first_name = ?", "Joe");
JdbcTemplate select 예제3
String값으로 결과 받기
String lastName = this.jdbcTemplate.queryForObject("select last_name from t_actor where id = ?", new Object[]{1212L}, String.class);
JdbcTemplate select 예제4
한 건 조회하기
Actor actor = this.jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
new Object[]{1212L},
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
JdbcTemplate select 예제5
여러 건 조회하기
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
JdbcTemplate select 예제6
중복 코드 제거 (1건 구하기와 여러 건 구하기가 같은 코드에 있을 경우)
public List<Actor> findAllActors() {
return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper<Actor> {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
}
JdbcTemplate insert 예제
INSERT 하기
this.jdbcTemplate.update("insert into t_actor (first_name, last_name) values (?, ?)", "Leonor", "Watling");
JdbcTemplate update 예제
UPDATE 하기
this.jdbcTemplate.update("update t_actor set = ? where id = ?", "Banjo", 5276L);
JdbcTemplate delete 예제
DELETE 하기
this.jdbcTemplate.update("delete from actor where id = ?", Long.valueOf(actorId));
JdbcTemplate외의 접근방법
NamedParameterJdbcTemplate
JdbcTemplate에서 JDBC statement 인자를 ?를 사용하는 대신 파라미터명을 사용하여 작성하는 것을 지원
Spring JPA의 경우에는 객체 단위로 CRUD가 가능합니다. 일반적으로 CUD는 단일 테이블에 대하여 작업을 하는 경우가 많은데 Mybatis에서 하게 되면 해당 쿼리를 직접 짜고, 들어오는 Parameter에 대하여 모두 확인하여 처리해야 하는 경우가 많습니다.
그러나 MyBatis의 장점을 무시할 순 없습니다. ER-DB를 사용하는 경우 자기가 원하는 Query를 짜서 직접 데이터를 손쉽게 전달하고 처리할 수 있다는 장점이 있죠. 특히, Spring JPA에서 활용하는 상당히 복잡한 나름대로의 SQL을 배워야 하고, 테이블 Join을 하게 될 경우 해당 조건을 객체구조에도 녹여내야 하는 등 배우고 익혀서 적용해야 할 포인트가 많아지게 됩니다.
@Controller, @Service, @Repository, @Component 어노테이션이 붙은 클래스를 찾아 컨테이너에 등록
@Component
컴포넌트 스캔의 대상이 되는 애노테이션 중 하나로써 주로 유틸, 기타 지원 클래스에 붙이는 어노테이션
@Autowired
주입 대상이되는 bean을 컨테이너에 찾아 주입하는 어노테이션
Java Config를 이용해 설정하기
ApplicationConfig.java
package kr.or.connect.diexam01;
import org.springframework.context.annotation.*;
@Configuration
public class ApplicationConfig {
@Bean
public Car car(Engine e) {
Car c = new Car();
c.setEngine(e);
return c;
}
@Bean
public Engine engine() {
return new Engine();
}
}
@Configuration 은 스프링 설정 클래스라는 의미를 가집니다.
JavaConfig로 설정을 할 클래스 위에는 @Configuration가 붙어 있어야 합니다.
이때 설정파일 중에 @Bean이 붙어 있는 메소드들을 AnnotationConfigApplicationContext는 자동으로 실행하여 그 결과로 리턴하는 객체들을 기본적으로 싱글턴으로 관리를 하게 됩니다.
ApplicationContextExam03.java
package kr.or.connect.diexam01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextExam03 {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Car car = (Car)ac.getBean("car");
car.run();
}
}
파라미터로 요청하는 class 타입으로 지정 가능합니다.
Car car = ac.getBean(Car.class);
ApplicationConfig2.java
package kr.or.connect.diexam01;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan("kr.or.connect.diexam01")
public class ApplicationConfig2 {
}
기존 JavaConfig에서 빈을 생성하는 메소드를 모두 제거했습니다.
단, @Configuration아래에 @ComponentScan이라는 어노테이션을 추가했습니다.
@ComponentScan어노테이션은 파라미터로 들어온 패키지 이하에서 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 클래스를 찾아 메모리에 몽땅 올려줍니다.
기존의 Car클래스와 Engine클래스 위에 @Component를 붙이도록 하겠습니다.
Engine.java
package kr.or.connect.diexam01;
import org.springframework.stereotype.Component;
@Component
public class Engine {
public Engine() {
System.out.println("Engine 생성자");
}
public void exec() {
System.out.println("엔진이 동작합니다.");
}
}
Car.java
package kr.or.connect.diexam01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Car {
@Autowired
private Engine v8;
public Car() {
System.out.println("Car 생성자");
}
public void run() {
System.out.println("엔진을 이용하여 달립니다.");
v8.exec();
}
}
수정된 JavaConfig를 읽어들이여 실행하는 클래스를 보도록 하겠습니다.
ApplicationContextExam04.java
package kr.or.connect.diexam01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationContextExam04 {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig2.class);
Car car = ac.getBean(Car.class);
car.run();
}
}
Spring에서 사용하기에 알맞게 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 객체들은 ComponentScan을 이용해서 읽어들여 메모리에 올리고 DI를 주입하도록 하고, 이러한 어노테이션이 붙어 있지 않은 객체는 @Bean어노테이션을 이용하여 직접 생성해주는 방식으로 클래스들을 관리하면 편리합니다.
생각해보기
다루는 빈(Bean)이 많아질수록 xml로 설정하는 것과 @ComponentScan, @Component, @Autowired를 이용하는 것 중 어떤 것이 유지보수에 좋을 것 같습니까?
@AutoWired 는 Field, Constructor, Setter Method 에 사용할 수 있습니다. 각각의 방식에 장단점에 대해서 더 생각해보세요.
1. 후자의 경우가 좀 더 유지보수하기에 좋을 것같습니다. xml로 설정하게될경우 우선순위는 좀 더 높지만 일일이 id를 지정해줘야하고 그에맞게 연결해줘야하는데 이건 여간 힘든작업이란 생각이 듭니다.
package kr.or.connect.diexam01;
public class Engine {
public Engine() {
System.out.println("Engine 생성자");
}
public void exec() {
System.out.println("엔진이 동작합니다.");
}
}
Car.java
package kr.or.connect.diexam01;
public class Car {
Engine v8;
public Car() {
System.out.println("Car 생성자");
}
public void setEngine(Engine e) {
this.v8 = e;
}
public void run() {
System.out.println("엔진을 이용하여 달립니다.");
v8.exec();
}
}
위의 Car 클래스가 제대로 동작하도록 하려면 보통 다음과 같은 코드가 작성되야 합니다.
Engine e = new Engine();
Car c = new Car();
c.setEngine( e );
c.run();
1, 2 번째 줄을 Spring 컨테이너에게 맡기기 위해 설정파일에 다음과 같은 코드를 입력합니다.
Engine e = new Engine();
Car c = new Car();
c.setEngine( e );
이번엔 위의 설정 파일을 읽어들여 실행하는 ApplicationContextExam02.java를 작성해보도록 하겠습니다.
package kr.or.connect.diexam01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextExam02 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"applicationContext.xml");
Car car = (Car)ac.getBean("car");
car.run();
}
}
콘솔을 보면 다음과 같이 실행된 것을 알 수 있습니다.
생각해보기
Spring컨테이너가 관리하는 객체를 빈(Bean)이라고 말합니다. (여러분들이 직접 new연산자로 생성해서 사용하는 객체는 빈(Bean)이라고 말하지 않습니다.) Spring은 빈을 생성할 때 기본적으로 싱글톤(Singleton)객체로 생성합니다. 싱글톤이란 메모리에 하나만 생성한다는 것입니다. 메모리에 하나만 생성되었을 경우, 해당 객체를 동시에 이용한다면 어떤 문제가 발생할 수 있을까요? 이런 문제를 해결하려면 어떻게 해야할까요? ( 참고로 Spring에서 빈을 생성할 때 스코프(scope)를 줄 수 있습니다. 스코프를 줌으로써 기본으로 설정된 싱글톤 외에도 다른 방법으로 객체를 생성할 수 있습니다. )
운영체제나 데이터베이스에서 나타나는 동기/비동기와 관련된 문제가 생길 수 있다고 생각합니다. 같은 객체에 동시에 다른 사용자가 접근하여 몇 명은 수정을, 몇 명은 참조를 진행한다면 문제가 생길 수 있습니다. 또한 이를 막기 위해 코딩을 하다보면 데드락과 관련된 이슈도 나올 수 있다고 생각합니다.
: 전 이미 maven-compiler-plugin 이 이미 있기 때문에 버전만 1.7에서 1.8로 변경하였습니다.
프로젝트를 선택하고, Maven -> Update Project를 선택합니다.
위와 같은 창이 뜨면 OK버튼을 클릭합니다.
실습코드
App.java
package kr.or.connect.diexam01;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
AppTest.java
package kr.or.connect.diexam01;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}
AppTest.java 를 선택한 후 우측버튼을 클릭하고 Run As -> JUnit Test 메뉴를 선택합니다.
하단의 JUnit 뷰에 하나의 테스트가 성공했다는 메시지와 함께 녹색 막대가 보여집니다.
Bean class란?
예전에는 Visual 한 컴포넌트를 Bean이라고 불렀지만, 근래 들어서는 일반적인 Java클래스를 Bean클래스라고 보통 말합니다.
Bean클래스의 3가지 특징은 다음과 같습니다.
기본생성자를 가지고 있습니다.
필드는 private하게 선언합니다.
getter, setter 메소드를 가집니다.
getName() setName() 메소드를 name 프로퍼티(property)라고 합니다. (용어 중요)
실습코드
UserBean.java
package kr.or.connect.diexam01;
//빈클래스
public class UserBean {
//필드는 private한다.
private String name;
private int age;
private boolean male;
//기본생성자를 반드시 가지고 있어야 한다.
public UserBean() {
}
public UserBean(String name, int age, boolean male) {
this.name = name;
this.age = age;
this.male = male;
}
// setter, getter메소드는 프로퍼티라고 한다.
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
}
}