데이터베이스: 데이터를 보관하기 위한 상자
데이터베이스에서 데이터를 모을 때는 특정한 규칙을 적용해 데이터를 정리해 보관한다. 보통 데이터베이스를 DB라고 줄여서 부르는 경우가 많다.
관계형 데이터베이스(Relational Database): 데이터를 표 형식으로 표현하고, 여러 표에서 항목의 값 사이에 관계를 맺고 있는 데이터베이스를 말한다. 보통 RDB로 줄여서 부른다.
책에서는 PostgreSQL 전용 관리 소프트웨어인 pgAdmin 4를 사용하지만 나는 젯 브레인의 DataGrip을 사용했다.(대학생 인증하면 무료!)
DB가 데이터를 보관하기 위한 상자라고 설명했지만 DB 안에서 실제로 규칙을 가진 데이터가 저장되는 상자를 테이블이라고 한다. 테이블은 데이터를 항목으로 해서 이차원 표 형식으로 정리해 저장한다.
테이블의 가로 행(row)을 레코드(record)라고 한다. 하나의 레코드가 한 건의 데이터이다. 테이블의 세로 열(column)을 칼럼이라고 한다. 하나의 칼럼은 데이터의 각 요소가 된다.
DataGrip을 사용했지만 pgAdmin4와 유사한 부분이 많았다. DB툴은 다 거기서 거기인듯
DataGrip으로 테이블 생성 과정 생략
제약 조건의 예
제약 조건 | 개요 |
---|---|
NOT NULL | NULL 입력을 허용하지 않습니다(필수입력). |
UNIQUE | 중복값 입력을 허용하지 않습니다(고유한 값). |
CHECK | 지정한 조건을 만족하지 않는 값의 입력을 허용하지 않습니다. |
PRIMARY KEY | 테이블 안에서 레코드를 식별하는 기본키를 설정합니다. 기본키는 NOT NULL과 UNIQUE가 함께 적용됩니다. |
FOREIGN KEY | 관련된 테이블을 연결하는 설정입니다. 외부 키라고도 부릅니다. |
DEFAULT | 칼럼의 초깃값을 설정합니다. |
SQL(Structured Query Language): DB를 조작하기 위한 언어. ANSI와 ISO에서 사양이 표준화되어 있어 다른 DB에서도 거의 같은 방법으로 조작이 가능하다.
CRUD: 영속적으로 데이터를 취급하는 4개의 기본적인 기능인 생성(Create), 읽기(Read), 갱신(Update), 삭제(Delete)의 머리글자를 따서 만든 단어
SQL의 CRUD
CRUD | 명령어 | 개요 |
---|---|---|
생성(Create) | INSERT | 데이터를 등록 |
읽기(Read) | SELECT | 데이터를 참조 |
갱신(Update) | UPDATE | 데이터를 갱신 |
삭제(Delete) | DELETE | 데이터를 삭제 |
PostgreSQL에서의 CRUD 구문
CRUD | 구문 |
---|---|
생성(Create) | INSERT INTO 테이블명(칼럼명, 칼럼명...) VALUES(값, 값, ...); |
읽기(Read) | SELECT 칼럼명 FROM 테이블명; |
갱신(Update) | UPDATE 테이블명 SET 칼럼명 = 값 WHERE 갱신할_레코드를_특정하는_조건; |
삭제(Delete) | DELETE FROM 테이블명 WHERE 삭제할_레코드를_특정하는_조건; |
INSERT 실행
INSERT INTO member(name) VALUES ('홍길동');
INSERT INTO member(name) VALUES ('이순신');
SELECT 실행
SELECT id, name FROM member;
UPDATE 실행
UPDATE member SET name = 'Yi Sun-sin' WHERE id = 2;
DELETE 실행
DELETE FROM member WHERE id=2;
엔티티: 데이터를 담아두는 객체. DB 테이블의 한 행(레코드)에 대응하는 객체다. 엔티티의 필드는 테이블의 칼럼값에 대응한다.
/**
* Member 테이블: 엔티티
*/
public class Member {
/** id 칼럼 대응 */
private Integer id;
/** name 칼럼 대응 */
private String name;
public Integer getId(){
return id;
}
public void setId(Integer id){
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
}
엔티티는 실제 데이터를 담아 두는 개체다. 사용할 때는 다음 세 가지를 기억해두면 좋다.
리포지토리: DB를 조작하는 클래스
리포지토리를 생성하는 경우에는 반드시 인터페이스를 정의하고 구현해야 한다. 그 이유는 리포지토리 인터페이스의 필드에 리포지토리 구현 클래스를 DI하여 특정 구현에 의존하는 것을 피할 수 있기 때문이다.
O/R 매퍼(Object-relational Mapper)를 간단하게 설명하면 애플리케이션에서 사용하는 O(Object):'객체'와 R(Relational): '관계형 데이터베이스'의 데이터를 매핑하는 것이다.
O/R 매퍼는 미리 설정된 객체와 RDB 간의 대응 관계 정보를 가지고 인터페이스의 데이터에 대응하는 테이블에 내보내거나 DB에서 값을 읽어 들여 인터페이스에 대입하는 작업을 자동으로 실행합니다.
스프링 데이터 JDBC는 O/R 매퍼이다. 스프링 데이터가 제공하는 CrudRepository를 상속해서 자동으로 CRUD를 지원하는 메서드를 사용할 수 있다.
package com.example.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Member 테이블 엔티티
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
/** Member 번호 */
private Integer id;
/** Member 이름 */
private String name;
}
Lombok에서 제공하는 어노테이션을 사용한다.
엔티티 생성 후 테이블의 기본키(Primary Key)에 해당하는 id 필드에 대해 @Id 어노테이션을 부여한다.
/** Member 번호 */
@Id
private Integer id;
package com.example.demo.repository;
import com.example.demo.entity.Member;
import org.springframework.data.repository.CrudRepository;
/**
* Member 테이블: 리포지토리
*/
public interface MemberCrudRepository extends CrudRepository<Member, Integer> {
}
CrudRepository<Member, Integer>에서 Member는 저장 대상 객체, Integer는 저장 대상 객체의 기본키 타입이다.
이렇게 MemberCrudRepository 인터페이스가 CrudRepository를 상속하게 함으로써 인터페이스 안에서 메서드를 작성하지 않아도 사용 가능하게 되었다. 메서드에서 사용되는 반환값이나 인수의 엔티티나 ID는 <Member, Integer>로 지정한 타입이 된다.
CrudRepository의 CRUD 메서드
반환형 | 메서드 | 개요 |
---|---|---|
long | count() | 취득한 엔티티의 수를 돌려줍니다. |
void | delete(Member entity) | 지정한 엔티티를 삭제합니다. |
void | deleteAll() | 리포지토리에서 관리하는 모든 엔티티를 삭제합니다. |
void | delteAll(Iterable entities) | 지정한 복수의 엔티티를 삭제합니다. |
void | deleteById(Integer id) | 지정한 ID에 해당하는 엔티티를 삭제합니다. |
boolean | existsById(Integer id) | 지정한 ID에 해당하는 엔티티의 존재 여부를 확인합니다. |
Iterable | findAll() | 모든 엔티티를 반환합니다. |
iterable | findAllById(Iterable ids) | 지정한 복수의 ID에 해당하는 엔티티를 반환합니다. |
Optional | findById(Integer id) | 지정한 ID에 해당하는 엔티티를 반환합니다. |
Member | save(Member entity) | 지정한 엔티티를 저장합니다. |
Iterable | saveAll(Iterable entities) | 지정한 모든 엔티티를 저장합니다. |
중요한 점은 save 메서드는 @Id 어노테이션이 부여된 필드의 값이 null인 경우에는 INSERT가 실행되고, 그 외에는 UPDATE가 실행된다는 점이다.
package com.example.springdatajdbcsample;
import com.example.demo.entity.Member;
import com.example.demo.repository.MemberCrudRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringDataJdbcSampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataJdbcSampleApplication.class, args)
.getBean(SpringDataJdbcSampleApplication.class).execute();
}
@Autowired
MemberCrudRepository repository;
/**
* 등록과 전체 취득 처리
*/
private void execute(){
// 등록
executeInsert();
// 전체 취득
executeSelect();
}
/**
* 등록
*/
private void executeInsert(){
// 엔티티 생성(id는 자동 부여되기 때문에 null을 설정)
Member member = new Member(null, "이순신");
// 리포지토리를 이용해 등록을 수행하고 결과를 취득
member = repository.save(member);
// 결과를 표시
System.out.println("등록 데이터:" + member);
}
/**
* 전체 취득
*/
private void executeSelect(){
System.out.println("--- 전체 데이터를 취득합니다 ---");
// 리포지토리를 이용해 전체 데이터를 취득
Iterable<Member> members = repository.findAll();
for(Member member : members){
System.out.println(member);
}
}
}
실행해보면 Insert와 Select가 실행된다.