@Configuration
。외부 라이브러리 또는 Application의 class를Spring Context에 의해 관리되는Configuration class로 선언하는 Annotaton.
▶ 해당 Annotation이 선언된 Class 객체는Spring Context에 등록 시Spring Beaninstance로서 생성되어LifeCycle,Dependency가 관리되며Spring Bean에 관한 설정 정보를 포함하는Configuration class로 지정.
@ComponentScan("경로"):
。Spring이 특정 package에 존재하는@Component로 선언된 class 객체인Spring Bean을 자동으로 찾기 위해Spring Context를 정의한 class에Spring Bean이 존재하는 경로를 Annotation으로 정의.
。경로 설정을 안할 경우 , 현재Spring Context가 선언된 Class 파일이 위치한 package 경로내에 존재하는@Component가 선언된 Class 객체의Spring Beaninstance를 자동으로 찾아서 Auto-wiring을 수행한다.
@Autowired:
。Dependency Injection: Spirng에서 요구하는 변수 type에 해당하는 특정Spring Context내부의Spring Beaninstance를 식별 및 자동으로 주입.
@Repository:
。DB와의 상호작용을 담당하는 Class객체의 경우@Component대신 선언.
▶Spring Bean이 DB의 데이터를 참조 , 수정 시 사용.
。주로 DB와 상호작용을 담당하는DAO(Data Access Obejct : DB에 접근하는 역할을 수행하는 객체)를 구현하는 class에 선언.
▶@Repository가 선언된 Class를 Spring의 Data Access 예외변환 기능과 함께 Spring Bean으로 등록하는 역할을 수행.
@GeneratedValue:
。Primary Key를 자동할당.
- Persistence Layer :
。Data를 영구적으로 저장 및 관리함으로써 데이터의 지속성 보장.
。DB와의 상호작용 & 통신 & CRUD & transaction 관리 등을 수행.
- H2 DataBase :
。Java로 작성된 오픈소스RDBMS
。Spring Boot가 지원하는In-Memory형식의 RDBMS로서, Application 재기동시 초기화.
▶ 로컬 또는 테스트 환경에서 주로 사용됨.
。개발용 Local DB로 사용이 용이
- JPA와 Hibernate에 맞는 새로운 Spring Boot Project 생성
。Spring Framework Dependencies :
Spring Web/Spring Data JDBC/Spring Data JPA의 starter와H2 DataBase의 framework를 dependency로서 설정.<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
H2 console을 사용 설정 또는H2 DB로 Access
。application.properties로 진입해서 다음 구문으로 property 설정하여 H2-DB를 사용설정하기.spring.h2.console.enabled=true이후 다음 URL로 h2-console 진입하기. http://localhost:8080/h2-console/
▶ Application 실행 시 console에서 지시하는JDBC URL를 h2-console 사이트의JDBC URL에 입력하여 h2 console로 진입한다.
。그러나 해당 URL은 동적 URL로서 Application 실행시마다 URL이 달라지므로 일일이 URL을 확인해야하므로 불편하다.
▶ 정적JDBC URL로 변경. ( 뒷부분의 난수는 삭제한다. )spring.datasource.url=jdbc:h2:mem:testdb。정적
JDBC URL을 포함한 다음 property를application.properties로 추가 후 재실행 시 난수를 포함한 동적 URL이jdbc:h2:mem:testdb로 URL이 고정됨.
이후 다음처럼 URL 입력 후 h2-console로 connect.Server Port 변경하기
application.properties에 다음 property를 정의.
server.port=8081
▶ 기존에 Application에 부여된 로컬가상주소로서localhost:8080/로 진입이 아닌localhost:8081/로 진입하게됨.
H2 DB초기화 시 생성될sql파일을 이용하여 table 구현하기
。H2-DB는In-Memory DB이므로 Application 재시작할때마다 데이터 초기화되므로 Application 재실행마다 실행할 초기값 설절용.sql파일을 생성.
▶DB사용 시 해당.sql파일을 만들 필요는 없다.
。schema.sql:
Application 초기화 시 초기 정의될 DB Schema를 정의하는 파일.
Schema: DB 구조로서Table, View, Index등의 객체를 정의.
。data.sql:
Application 초기화 시 정의된 DB Table에 삽입할 초기 데이터를 정의하는 파일
。실행 순서 :schema.sql->data.sqlApplication 구동 및 초기화 시 구현될 table 정의
。sql파일을src/main/resources/경로에 생성. ( 반드시 해당 경로에 존재해야함.)# schema.sql create table course( id bigint not null, name varchar(255) not null, author varchar(255) not null, primary key (id) );。
JDBC,JPA,Hibernate등의 기법을 사용하도록 table을 생성
▶spring-boot-starter-data-jpa를 활용 시 Application이 구동 및 초기화될때 자동으로schema.sql을 참조하여 H2-DataBase에 table 생성.
Application을 DB와 상호작용할 수 있도록 하는 API
- JDBC ( Java Database Connectivity ) :
。자바(Java)에서 DB에 접속할 수 있도록 연결하는 API
▶ Java Application에서 DB에 접근하여 사용자가 작성한 SQL을 통해SELECT , INSERT , UPDATE , DELETE수행.
- Spring JDBC
。Spring Framework에서 제공하는 JDBC 추상화 library
▶ JDBC 기능으로 DB와 연결 시 JDBC 코드를 훨씬 더 적고 간결하게 작성하는 이점이 존재.
(위 : JDBC / 아래 : Spring JDBC )
JdbcTemplate:
。Spring JDBC를 간소화하고 편리하게 활용하여DB와 상호작용할 수 있도록 돕는 Class.
。DAOClass에서 instance를 생성하여 instance의 method에 SQL를 넣어서 활용하여 DB와 상호작용을 수행.
Persistence Layer의 Java Bean 객체.
DAO( Data Access Object ) :
。DB의 데이터로 접근하는Transaction Object로서,DB의 데이터를 조회하거나 조작하는 기능을 수행하는 객체.
。Spring에서는 Spring Bean으로서@RepositoryAnnotation으로 Class에 선언하여Spring Beaninstance으로 생성.
▶ 내부에는 Spring JDBC를 활용한JdbcTemplate같은 DB와 상호작용하는 method를 제공하는 Class를 instance로 생성하여DB와의CRUD를 수행하는DAO로서 작용하도록 설정.
DTO( Data Transfer Object )
。계층간 데이터 교환에서 데이터를 전송하는 객체를 의미하는Java Bean
。 순전히 데이터를 저장 및 회수하는 기능을 제외하고 아무 기능을 가지고 있지 않음.
▶Getter,SetterMethod만 포함하고있는 순수한 Class.
VO( Value Object )
。간단한 Entity를 의미하는 작은 객체.
▶DTO와 달리Getter만 제공.
。
CommandLineRunnerInterface를 상속한 Class를 정의 및CommandLineRunnerInterface의 Abstract Methodrun()를 구현하여 application 구동 시 DB와 상호작용하는Business Logic를 구현한Spring Beaninstance의 method를 작동.
▶JdbcTemplate의update(sql)method로 DB와 상호작용을 수행하는 method를 구현.
。JDBC와 Spring JDBC를 사용 시 SQL Query문을 많이 활용
。Query문 입력 시text-block내에 SQL문을 입력.
- Spring JDBC을 활용해 DB Table에 data insert
// CourseJdbcRepository.java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class CourseJdbcRepository { @Autowired // Field-base Dependency Injection 수행. private JdbcTemplate jdbcTemplate; private static String sqlinsert = """ insert into course values(2,'lee','머야'); """; public void insert(){ jdbcTemplate.update(sqlinsert); } }。해당 Class는
DAO객체로서 DB와 상호작용할Spring Bean용도로 사용하기위해@Repository선언.
。JdbcTemplateClass 객체를 사용하여 Spring JDBC 사용.
▶JdbcTemplate객체.update(sql)을 통해 DB에 해당 SQL 구문을 적용.// CourseJdbcCommandRunner.java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class CourseJdbcCommandRunner implements CommandLineRunner { @Autowired private CourseJdbcRepository cjcr; @Override public void run(String... args) throws Exception { cjcr.insert(); } }。해당 Class는 Application 구동 시 특정
Spring Bean을 실행하는CommandLineRunnerinterface를 상속.
。@Component: Application 구동 시@SpringBootApplication으로 선언한 Class가Component Scan을 통해 해당 Class를 Spring Bean으로 식별하도록 함.
。CourseJdbcRepositoryClass 객체를@Autowired를 통해Field-base의존성 주입을 하여Spring Beaninstance 생성.
。CommandLineRunner의run()method 내부에Spring Beaninstance (cjcr)에 구현된 DB와 상호작용하는 method(insert())를 구동.
h2-console진입하여 확인 시JdbcTemplate의update(sql)method를 통해 SQL query가 잘 작동했음을 확인 가능.
@SpringBootApplication:
。Spring Boot Application의 진입점 Class에 사용되는 Annotation.
▶ Spring Boot에서 Application을 실행하는 역할을 수행.
。@SpringBootApplication을 통해 Spring Boot의 Auto-Configuration, Spring Bean의 생성 등이 자동으로 설정.
▶ Spring Application 실행 시Spring Bean은Component Scan을 통해 자동으로 식별 및 생성되며 해당Spring Bean은@Component가 선언되어야한다.
。해당 Annotation이 선언된 Class는Spring Bean이자Configuration Class로서 내부에@Bean Method를 선언할 수 있다.@SpringBootApplication public class Practice2Application { public static void main(String[] args) { SpringApplication.run(Practice2Application.class, args); } }。
CommandLineRunner:
。Spring Application이 구동 시 실행해야하는Spring Bean을 정의하기위해 사용하는 Interface.
▶ application 실행 시 동시에 정의된 query문을 실행하면서 초기화 하는 역할 수행.
。해당 interface를 상속한 Class에서 구현해야하는run()Method 제공.
▶run()method는Stringtype의 가변 매개변수를 받는다.
▶ 해당 Method에 Application 구동 시 실행할Spring Beaninstance의 method 등을 서식.@Override public void run(String... args) throws Exception{ cjcr.insert() }。
@Override는 구현하지 않아도 되지만 다른 개발자가 파악하는 명시적 용도로 선언.
- Course class를 생성하여 Spring JDBC를 통해 data의 삽입을 용이하게 하기.
。CourseJdbcRepositoryclass에서JdbcTemplate의update(sql)method로INSERTorDELETEorUPDATE를 수행하는 SQL문과 data를 포함하는Courseclass의 instance를 통해 DB와 상호작용하는 method들을 생성
。CourseJdbcCommandRunnerclass에서 data를 생성자로 넣어서Courseinstance들을 생성한 후 해당Courseinstance들을CourseJdbcRepository의 method의 매개변수로 넣어서 DB에 반영.public class Course { private long id; private String name; private String description; public Course() {} // update 용도의 Course instance 생성자 public Course( String name, long id) { this.id = id; this.name = name; } // insert 용도의 Course instance 생성자 public Course(long id, String name, String description) { this.id = id; this.name = name; this.description = description; } public long getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } // SELECT 기능 존재 시 Setter 설정 public void setId(long id) { this.id = id; } public void setName(String name) { this.name = name; } public void setDescription(String description) { this.description = description; } }。매개변수가 존재하는 생성자 구축 시 , 기본생성자도 같이 정의해야한다.
。SELECT를 통해Coursedata type으로서 결과값을 가져오게될 경우, Class에서setter method를 정의.
。data를 받아 DB에 넣기 위한 instance로 작용할Courseclass 정의.
。update용도의 Course instance의 생성자의 매개변수는 2개로 설정
▶update course set name=? where id = ?;
。insert용도의 Course instance의 생성자의 매개변수는 3개로 설정.
▶insert into course values(?,?,?);
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository public class CourseJdbcRepository { @Autowired // Field-base Dependency Injection 수행. private JdbcTemplate jdbcTemplate; private static String sqlinsert = """ insert into course values(?,?,?); """; private static String sqldelete = """ delete from course where id = ?; """; private static String sqlupdate = """ update course set name=? where id = ?; """; private static String sqlselect = """ select * from course where id = ?; """; public void insert(Course cs){ jdbcTemplate.update(sqlinsert,cs.getId(),cs.getName(),cs.getDescription()); } public void delete(long id){ jdbcTemplate.update(sqldelete,id); } public void update(Course cs){ jdbcTemplate.update(sqlupdate,cs.getName(),cs.getId()); } public Course select(long id){ return jdbcTemplate.queryForObject(sqlselect,new BeanPropertyRowMapper<>(Course.class),id); } }。하드코딩을 통해
insert,update,delete,select를 수행하는 sql을 작성.
▶ 매개변수의?는JdbcTemplate의update(sql,매개변수)method의 매개변수에 값을 입력하여 전달.
。DB에SELECT를 수행 시 return값이 존재하므로,select()method의 return type을 Spring Bean(Course객체 )로 정의 및jdbcTemplate객체.queryForObject()로SELECT를 통해 반환된 data를Course객체의 Spring Bean instance로 획득한다.
▶ 결과값을 가져오게될 경우, Course class에서setter method를 정의.import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class CourseJdbcCommandRunner implements CommandLineRunner { @Autowired private CourseJdbcRepository cjcr; @Override public void run(String... args) throws Exception { cjcr.insert(new Course(2,"Learn AWS","wjdtn747")); cjcr.insert(new Course(3,"Learn JPA","wjdtn747")); cjcr.delete(1); cjcr.update(new Course("Learn JDBC",3)); System.out.println(cjcr.select(3)); } }。 application이 실행되자마자
CommandLineRunner의run()method를 통해 table에 반영할 data를Courseinstance에 넣어서CourseJdbcRepository의insert(),delete(),update(),select()method를 실행함.
。cjcr.select(3)의 경우select를 통해 return한 data를 Spring Bean(Course객체 )로 획득.
- 하드코딩 :
。코드 내부에 데이터를 직접 입력하는것을 의미.
。매개변수에 들어가는 값을 직접 코드로 작성(상수로 값이 고정된 상태) 후 프로그램이 동작하게 하는것.
- JdbcTemplate :
。Spring JDBC를 간소화하고 편리하게 활용하여DB와 상호작용할 수 있도록 돕는 Class.
。DAOClass에서 instance를 생성하여 instance의 method에 SQL를 넣어서 활용하여 DB와 상호작용을 수행.
DAO(Data Access Obejct) : DB에 접근하는 역할을 수행하는 객체
。DB연결, Query 실행, 결과처리, 예외처리 등을 간편하게 처리private JdbcTemplate springJdbcTemplate;▶ JdbcTemplate class 객체 선언
JdbcTemplate Method
JdbcTemplate객체.update(SQL_query,매개변수값):
。JdbcTemplateclass의updatemethod를 통해INSERT / UPDATE / DELETE의 SQL query를 활용하여 DB 수정.
。sql의 매개변수가 다음처럼?로 표현된 경우,updatemethod 매개변수에 sql 매개변수에 입력될 data를 정의.String sql = "insert into course values(?,?,?)"; JdbcTemplate객체.update(sql, 1 , 2 , 3).
JdbcTemplate객체.queryForObject(SQL, new BeanPropertyRowMapper<>(Spring Bean) , 매개변수값 ):
。단일행(Row) 결과를 반환하는SELECTQuery를 실행 시 사용.
▶SELECTquery를 통해 추출된 데이터가Spring Beaninstance로 변환되어 반환.
▶ 여러 행 결과 반환 시jdbctemplate객체.query()사용.
。데이터를 가져올SQL의 Column명과BeanPropertyRowMapper의 생성자에 정의된 Spring Bean의 Field명이 서로 동일해야 SQL 작동 시 해당 Spring Bean instance로 Mapping되어 input이 수행.
。개발자는 SQL 작성 및 전달할 매개변수를 직접 정의하여 mapping을 수행.
。transaction을 위한 connection 동기화 및 Spring 예외 변환기를 자동 실행함.
。결과값을 가져오게될 경우,SpringBeanclass에서 반드시setter method를 정의.private JdbcTemplate jdbcTemplate; private static String sqlselect = " select * from course where id = ?;"; jdbcTemplate.queryForObject(sqlselect,new BeanPropertyRowMapper<>(Course.class),id);
new BeanPropertyRowMapper<>( Spring Bean )
。RowMapper: 원하는 형태의 결과값으로 반환.
▶queryForObject()의 return type은 data type만 가능하므로 , Select를 통해 도출된 Data를 사용자가 원하는 Format의 결과값을 반환하는 역할
JdbcTemplate객체.query(SQL, 매개변수포함Object배열 , RowMapper 객체)
。DB에서 여러행(Row) 결과를 반환하는SELECTQuery를 실행 시 사용.
▶RowMapper<Type>을 사용하여 결과를 Class Instance로 Mapping.
- 활용예시
public class PgGeoJSONTemplate { private String geojson; public PgGeoJSONTemplate(){} public String getGeojson() { return geojson; } public PgGeoJSONTemplate(String geojson) { this.geojson = geojson; } public void setGeojson(String geojson) { this.geojson = geojson; } }。 Return된
SQL행 데이터를 포함할 Template 혹은DTO역할의 Class.
▶queryForObject()에 의한 단일행데이터 반환 시 단일 Class instance 반환.
▶query()에 의한 여러행데이터 반환 시List<Class객체>로 반환.import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; public class GeoJSONRowMapper implements RowMapper<PgGeoJSONTemplate> { public PgGeoJSONTemplate mapRow(ResultSet rs, int rowNum) throws SQLException { PgGeoJSONTemplate template = new PgGeoJSONTemplate(); template.setGeojson(rs.getString("geojson")); return template; } }public List<PgGeoJSONTemplate> createCourceGeoJSON(PgJdbcOptionTemplate option){ Object[] params = new Object[]{option.getFromnodeid(),option.getTonodeid(),option.getDistance()}; return jdbcTemplate.query(sqlpgrdijkstraGeoJSON,params,new GeoJSONRowMapper()); }
RowMapper<ClassType>
。Spring JDBC에서 SQL Query의 결과를 Class 객체로 변환하는Interface.
▶JdbcTemplate객체.query()에서 사용되며 각 행을 원하는 Class객체로 Mapping.
。해당 Interface를 Class로 구현 및 해당 Class 객체를JdbcTemplate객체.query()의 매개변수로 설정.
。mapRow()구현메소드를 구현하여 매개변수로 전달되는ResultSet객체를 이용하여 SQL의 각Column을Template,DTO역할의 Class 객체의 Field로 Mapping.import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; public class GeoJSONRowMapper implements RowMapper<PgGeoJSONTemplate> { public PgGeoJSONTemplate mapRow(ResultSet rs, int rowNum) throws SQLException { PgGeoJSONTemplate template = new PgGeoJSONTemplate(); // SQL 데이터의 Column을 rs객체를 통해 가져와서 // template 역할 Class의 Field로 Mapping. template.setGeojson(rs.getString("geojson")); return template; } }
DTO( Data Transfer Object )
。 계층 간 데이터 교환을 위해 사용하는 Class의 instance.
▶ 주로Controller → Service → Repository또는 API 요청/응답에서 데이터를 전달하는 용도로 사용됩니다.
JPA (Java Persistence API)
- JAVA에서 ORM 기술표준으로 활용되는 interface의 집합.
▶ 호환성을 목적으로 DB mapping을 용도로 해당 interface를 구현한 Class를 생성하기 위해 활용되는 Framework.- JPA를 구현한 대표적인 오픈소스로
Hibernate가 존재.ORM(Object-Relational Mapping) :
。Application의DB Entity Classinstance를 RDBMS의DB Table에 자동으로 영속화 하는것을 의미.
▶DB Entityinstance와RDBMS DB Table간 자동 Mapping.
。SQL을 직접 사용하지 않고,DB Entityinstance를 중심으로DB의 Data 조작을 수행.
▶RDBMS(H2-DB,mySQL,postgreSQL) 의 변경에도 유연한 적용이 가능.
- 영속성 컨텍스트 :
。Entity를 영구 저장하는 환경
。데이터를 영구적으로 저장 및 관리함으로써 데이터의 영속성 보장.
。DB와의 상호작용 & CRUD & transaction 관리를 수행.
- 영속화 :
。영속성 컨텍스트에 관리되는 상태로 만드는것을 의미.
JPA와 Hibernate의 차이점
- JPA
。API를 정의 ( 객체를 DB의 table로 Mapping하는 방식 정의 )
。@Entity: Interface로서 Entity가 무엇인지 정의
。@Id,@Column: 변수를 DB Table의 Field로 mapping
▶EntityManager에 존재하는 Method를 구현함으로써JPA API사용.
- Hibernate
。JPA를 구현한 대표적인 오픈소스.
。Hibernate JAR을 class path에 추가해서 Hibernate를 JPA 구현체로 사용.
▶ (ex.@Entity의 경우.jakarta.persistence대신org.hibernate.annotations사용. )
。코드에서 직접Hibernate annotation을 사용하지 않는 이유는 다른 JPA 구현체(Toplink등)이 존재하므로, Hibernate로만 한정해서 사용하지 않기 위해!
▶ 따라서. 코드는JPA를 사용하여 작성하고,Hibernate는 코드가 아닌, 구현체로만 사용하는것이 좋다.
Dialect:
。JPA또는Hibernate에서 사용하는 특정 DB에 해당하는 SQL문법과 기능을 정의한 설정.
▶ 각 DB(MySQL,PostgreSQL,Oracle)등은 공통적으로 SQL 표준을 따르지만, 각각 조금씩 차이가 발생하는 SQL 문법을 가지므로Hibernate는 해당 문법/기능적 차이를Dialect를 통해 자동으로 조정.
- JPA의
ORM특징을 활용하여 Course class의Spring Bean을DB Entity로서 DB table로 mapping
。Course class에 Setter 메소드가 존재해야SELECT를 통해 데이터를 가져올 수 있다.import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity(name="course") public class Course2 { @Id private long id; @Column(name="name") private String name1; // DB table에서 동일 이름의 Column를 가지는 경우 따로 @Column을 정의 안해도 된다. private String author; public Course2(){}; public Course2(long id, String name1, String author) { this.id = id; this.name1 = name1; this.author = author; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName1() { return name1; } public void setName1(String name1) { this.name1 = name1; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
@Entity: Class와 DB table 간을 Mapping하는DB Entity를 생성
▶ 이후 DB Table의 Column역할을 수행할DB Entity의 Field에@Id,@Column을 선언.
。@Id: 선언된 Field를 DB의 Table의 primary key로 정의하는 annotation.
。@Column(name="DB_table_Column이름"): DB table에서"name"라는 이름을 가지는 Column과 해당 Field를 연결.
- JPA를 활용해 Data를
insert, delete, select를 수행하는 Repository를 구현.
。사전에 DB Table과 Mapping된DB Entity(Course2)이 마련되어있어야한다.
。Spring JDBC에서는JdbcTemplate를 통해 DB와 상호작용을 수행한다면, JPA에서는EntityManager를 통해 DB와 상호작용을 수행
。JPA로 Query를 수행시,@Repository와 함께@transactional을 선언!// CourseJpaRepository import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.transaction.Transactional; import org.springframework.stereotype.Repository; @Transactional @Repository public class CourseJpaRepository { @PersistenceContext private EntityManager em; public void insert(Course2 course){ em.merge(course); } public Course2 select(long id){ return em.find(Course2.class, id); } public void delete(long id){ Course2 c1 = em.find(Course2.class, id); em.remove(c1); } }。
@Transactional,@Repository: JPA를 활용한(EntityManager) DB작업 수행 시 해당 Annotation을 Class에 선언.
。@PersistenceContext:EntityManagerClass의 instance를 해당 Field에 의존성주입하는데 사용하는 Annotation.
。em.merge(course):@Entity를 선언한DB Entityinstance를 전달받아서 해당 instance의 data를 DB Table로 insert.
。em.find(Course2.class, id): DB Table에서 해당 id를 가진DB Entityinstance를 반환.
。em.remove(c1):em.find(Course2.class, id)를 통해 얻은DB Entityinstance를 입력하여 DB Table의 해당 instance를 삭제.// CourseCommandRunner import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class CourseCommandRunner implements CommandLineRunner { @Autowired private CourseJpaRepository cjcr; public void run(String... args) throws Exception { cjcr.insert(new Course2(1,"Learn AWS jpa","wjdtn747")); cjcr.insert(new Course2(2,"Learn JDBC jpa","wjdtn747")); cjcr.insert(new Course2(3,"Learn JPA jpa","wjdtn747")); cjcr.delete(2); System.out.println(cjcr.select(1)); System.out.println(cjcr.select(3)); } }。
@Component:@SpringBootApplication으로 선언한 진입점 Class를 통해 Application 구동 시Component Scan을 통해 해당 Class를 Spring Bean으로 식별하도록 함.
。@Autowired: CourseJpaRepository class의 instance를 Field에 의존성 주입. ( Field-based Injection )
- 이때 , JPA를 사용해서 생성된 SQL을 확인하려면 다음 구문을
application.properties에 추가.spring.jpa.show-sql=true
다음처럼 SQL Query문이 도출됨.
▶CodeLineRunner의run()으로 DB작업을 실행 시 data의 Primary Key가 겹치지 않는 한JDBC방식과JPA방식으로 동시에 data insert가 가능.
@Repository:
。DB와의 상호작용을 담당하는 Class객체의 경우@Component대신 선언.
▶Spring Bean이 DB의 데이터를 참조 , 수정 시 사용.
。주로 DB와 상호작용을 담당하는DAO(Data Access Obejct : DB에 접근하는 역할을 수행하는 객체)를 구현하는 class에 선언.
▶@Repository가 선언된 Class를 Spring의 Data Access 예외변환 기능과 함께 Spring Bean으로 등록하는 역할을 수행.
EntityManager:@PersistenceContext // 선언 시 `EntityManager` Class의 instance를 주입 private EntityManager em;。
JPA를 활용해서 DB Table과 Mapping된DB Entity간 CRUD 작업을 수행하기 위한 method 제공.
▶ Spring JDBC에서의JdbcTemplate와 유사한 기능 수행.
。사전에@Entity를 선언한 DB Entity(ex :Course class)를 관리하는 역할 수행.
▶ 사전에 DB Entity내 Field가 모두 DB와 Mapping이 되어있어야함.
▶ DB Entity의 영속성관리, life-cycle을 관리.
DB Entity를 정의한 객체를 DB에 삽입(merge) / 탐색(find) / 삭제(remove) 시 사용
EntityManager객체.merge(DBEntityInstance):
。DB Entityinstance내 data를insert.
EntityManager객체.find(DBEntity.class , id(primary key)):
。해당DBEntityClass로 생성된 instance를 조회하는select와 동일한 기능 수행.
▶ 해당 id에 해당하는DBEntity를 반환.
EntityManager객체.remove(DBEntityInstance):
。DB에 해당DB EntityInstance로 insert된 데이터를Delete하는 기능 수행
▶ 보통EntityManager객체.find(DBEntity.class , id)를 통해 해당 id의DBEntity를 획득 후EntityManager객체.remove에 입력하여 DB에서 삭제 수행.
@PersistenceContext:
。DB Entity개체에 대하여 Spring Bean의@Autowired역할을 수행.
▶EntityManagerClass의 instance를 주입하는데 사용하는 Annotation.@Autowired // Field-base Dependency Injection 수행. private JdbcTemplate jdbcTemplate; @PersistenceContext private EntityManager em;.
@Transactional: 선언적 트랜잭션
。DB와 관련된 작업( insert, select, update 등 )을 일관성 있게 처리하기 위한 개념.
。개별 Class 혹은 Method에 선언 시 내부 DB작업을 하나의 Transaction으로 묶어주는 역할을 수행.
▶ 단위 Transaction 내 DB관련작업이 성공하면 Transaction을 Commit하여 작업을 영구적 저장, 하나라도 실패 시 Rollback 하여 이전 상태로 복구
- 트랜잭션(Transaction) :
。DB의 트랜잭션의 경우 DBMS에서의 상태변화의 한 주기.
주기 : 직전 Commit부터 다음 Commit까지의 주기.
。트랙잭션 시작 ▶ DB관련작업 ▶ 트랜잭션 종료
트랜잭션 종료 : 단위 Transaction 내 DB관련작업이 성공하면 Transaction을 Commit하여 작업을 영구적 저장, 하나라도 실패 시 Rollback 하여 이전 상태로 복구.
JPA사용 시, Background에서 어떤 SQL query가 생성되는지 확인하는 방법
。JpaRepository<>를 활용한Spring JPA의 method(save(),findById())의 Query 확인용도로도 사용됨.
。다음 구문을application.properties로 추가
spring.jpa.show-sql=true
。웹페이지 상에서 DB와 관련된 작업을 수행하면 콘솔창에 해당 작업의 query문이 표시됨.
▶ Spring Data JPA 활용 시 사용자가 SQL을 사용하진 않지만, 백그라운드상에서는 SQL이 활용됨.
Spring Data JPA
。 기존JPA방식에서EntityManager를 활용하지 않는 방식.
▶ 따로EntityManager를 통해 JPA를 구현한 Class를 생성하지 않고 간단한JpaRepository만 상속하는 Interface만 생성
JpaRepository<DBEntity class, id data Type>를 상속하는 Interface를 생성.// CourseSpringDataJpaRepository interface public interface CourseSpirngDataJpaRepository extends JpaRepository<Course,Long>
JpaRepository를 상속한 Interface를 Field-based Injection 및DB Entity를 이용해 DB와 상호작용.// CourseCommandRunner.java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class CourseCommandRunner implements CommandLineRunner { @Autowired private CourseSpringDataJpaRepository rp; public void run(String... args) throws Exception { rp.save(new Course2(1,"Learn AWS jpa","wjdtn747")); rp.save(new Course2(2,"Learn JDBC jpa","wjdtn747")); rp.save(new Course2(3,"Learn JPA jpa","wjdtn747")); rp.deleteById(2l); // Integer를 Long type으로 형변환. System.out.println(rp.findById(1l)); System.out.println(rp.findById(3l)); System.out.println(rp.findAll()); System.out.println(rp.count()); } }。
private CourseSpringDataJpaRepository rp:JpaRepository를 상속한 Interface를@Autowired로 Field-based Injection
。Interface객체.count(): 특정 DB Table의 DB Entity의 갯수 확인
。Interface객체.findAll(): JpaRepository를 통해 실행된 Query문을 Console에 표현.
다음과 같이 결과값 출력!
- 임의의 Custom Method 생성하기
。JpaRepository를 상속한 Interface에 생성할 Custom Method를 서식.
。Custom method작성 시SpringDataJpaInterface 내에서 명명 규칙을 지켜서 작성.
▶Custom Method명명규칙 :
。findByname1()의 매개변수의 이름은 반드시DB EntityClass의 Field명(name1)과 동일.
。Field명이username인 경우,findByusername(String username)
▶Custom Method의 매개변수를Spring Bean의 Field명과 동일하게 설정 시Spring Data JPA가 자동으로 검색
。JpaRepository는 DB의Id로 검색하는findById()는 기본적으로 제공하지만, Field명으로 검색하는 기능은 구현이 되지 않았으므로, Custom Method를 구현.import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface CourseSpringDataJpaRepository extends JpaRepository<Course2, Long> { List<Course2> findByAuthor(String author); List<Course2> findByname1(String name1); }// CourseCommandRunner.java public void run(String... args) throws Exception { rp.save(new Course2(1,"Learn AWS jpa","wjdtn747")); rp.save(new Course2(2,"Learn JDBC jpa","wjdtn747")); rp.save(new Course2(3,"Learn JPA jpa","wjdtn747")); System.out.println(rp.findByname1("Learn AWS jpa")); System.out.println(rp.findByAuthor("wjdtn747")); }
。rp.findByName1("Learn AWS jpa")을 통해 해당하는 Entity를 총 1개 return.
。rp.findByAuthor("wjdtn747")을 통해 해당하는 Entity를 총 3개 return.
JpaRepository<DBEntity class, id data Type>:
。Spring Data JPA에서 제공하는 Interface로서Spring JPA의ORM을 활용한 자동으로 Query를 생성하는 DB 상호작용 Method를 제공.
▶Spring JDBC의JdbcTemplate객체의 경우 직접SQL을 작성하여JdbcTemplate객체.update(SQL)로 상호작용한다면JpaRepository는 자동으로Query가 생성되는 Method를 제공.public interface CourseSpirngDataJpaRepository extends JpaRepository<Course,Long>@Autowired private CourseSpringDataJpaRepository rp;▶ 해당 Interface를 상속받는 Interface를 정의 후, 해당 Interface를 구현한 Field는 JPA에서 제공하는 Method를 사용 가능.
JpaRepository<DBEntity class, id data Type>구현 Method :
JpaRepository객체.save(DBEntity객체):
。INSERT와UPDATE역할을 수행
▶ 기존DB Entity는UPDATE, 새로운DB Entity는INSERT
JpaRepository객체.deleteById(Id)
。DELETE역할 수행
JpaRepository객체.findById(Id)
。SELECT역할 수행
。DB에서 조회한 Data를Optional<Bean>으로 return.
▶JpaRepository객체.findById(Id).get()으로 Spring Bean instance로 가져올 수 있다.
。JpaRepository의findById()사용 시Optional<T>로 return하는 이유?
▶ 조회한DB Entity가 존재하지 않을 수 있으므로 (null방지용 )
Optional<T>:null값을 직접 다루는 대신 값이 존재할 수 있고 없을 수 있는 상황을 명확히 표현하는데 사용하는 ClassOptional<User> user = userRepository.findById(id); User user1 = userRepository.findById(id).get();。
JpaRepository객체.findById(ID).get():findById()로 조회된 Data를 Spring Bean instance를 return
。JpaRepository는 DB의Id로 검색하는findById()는 기본적으로 제공하지만, Field명으로 검색하는 기능은 구현이 되지 않았으므로, Custom Method를 구현해야함.List<Course2> findByname1(String name1).
JpaRepository객체.count(): 특정 DB Table의DB Entity의 갯수 확인
JpaRepository객체.findAll():
。@Entity를 통해 특정 Spring Bean과 Mapping된DB Table에 존재하는 모든 Data를 해당Spring BeanClass의 Spring Bean intance로서List<SpringBean>로 return.
。JpaRepository를 통해 실행된 Query문을 Console에 표현하는 역할도 수행.@GetMapping(path="/jpa/users") public List<User> ListAllUsers(){ List<User> users = userRepository.findAll(); return users; }
JpaRepository객체.getReferenceById():
。DB Table에서 ID에 해당하는 Spring Bean을 return.
EmbeddedDatabaseBuilder:
。Spring JDBC에서Embedded DB를 쉽게 구축 및 구성하는데 사용되는 Class.
▶ 주로 Test 환경에서 가벼운Embedded DB를 설정 시 사용됨.
。@Configuration을 선언한Configuration Class에서DataSourcetype으로EmbeddedDatabaseBuilder객체를build하여 반환하는@Bean Method정의.@Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION) .build(); }▶
Spring Security의JDBC기반 사용자인증을 위한 기본 테이블을H2-DB에 자동으로 생성.
EmbeddedDatabase:
。Application내부에서 실행되는 가벼운 DB로서 별도의 설치없이 사용이 가능.
▶ 대표적으로H2,Derby,HSQLDB등이 존재.
。빠르게 DB를 초기화 가능하여 TEST용으로 적합하지만, 영구저장되지 않고, 대량의 데이터를 다루기에는 적합하지않은 단점이 존재.
▶ 운영환경에서는 부적합.
DataSource:javax.sql.DataSource
。JDBC에서DB Connection을 관리하는 Interface.
JdbcDaoImpl:
。Spring Security에서JDBC기반으로 사용자정보를 가져오는 기본 구현체.
▶DB에서 사용자정보를 읽어Authentication을 처리하는 용도로 사용됨.
JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION:
。Spring Security에서 기본적으로 제공하는 라이브러리 내 포함된users.ddl의 파일 경로.
public static final String DEFAULT_USER_SCHEMA_DDL_LOCATION = "classpath:org/springframework/security/core/userdetails/jdbc/users.ddl";
users.ddl:
。JDBC기반 사용자인증을 수행 시 기본적으로 필요한 테이블을 생성하는SQL이 포함됨.CREATE TABLE users ( username VARCHAR(50) NOT NULL PRIMARY KEY, password VARCHAR(500) NOT NULL, enabled BOOLEAN NOT NULL ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username) ); CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);
JDBC/Spring JDBC/JPA/Spring Data JPA차이
JDBC:
。SQL을 많이 작성하면서 Java 코드가 많다.
▶ 하드코딩이 많음.
Spring JDBC:
。SQL을 많이 작성
。JdbcTemplateinstance를 통해 Java코드는 적게 사용.
JPA:
。SQL을 작성하지않음
。EntityManager을 통해DB Entity를 DB Table로 Mapping 하는 과정 필요.
Spring JPA:
。SQL,EntityManager을 사용하지 않는다.
▶EntityManager대신JpaRepository<Entity class, ID>Interface를 상속한 Interface를 Instance로 생성하여 활용.
。Spring JDBC,Spring JPA를 사용할 경우PostgreSQL DB,MySQL DB,H2-DB등에 관계없이 구축된DB Entity을 매우 쉽게 적용할 수 있다.
。Spring JDBC의JdbcTemplate객체의 경우 직접SQL을 작성하여JdbcTemplate객체.update(SQL)로 상호작용한다면JpaRepository는 자동으로Query가 생성되는 Method를 제공.