DataBase는 데이터의 저장소로, 저장하는 데이터는 대부분 문자열로 저장된다.
DBMS(DataBase Managament System)는 데이터베이스를 운영하고 관리하는 소프트웨어를 말한다. MySQL, OracleDB, 등등이 있다.
우선 메모리에 데이터베이스를 구현해본다.
메모리에 쓰는 휘발성 데이터는 시스템 재부팅, 애플리케이션 종료 시 사라지며, 날아가는 것을 방지하기 위해 옛날에는 파일 라이터, 파일 리더 등 IO를 사용해 파일로 작성했다고 한다.
1. Repository 인터페이스
public interface Repository<T, ID> {}
데이터가 저장되는 저장소와 연결된 인터페이스.
데이터의 타입과, 데이터마다 각각의 고유한 키인 프라이머리 키 ID를 제네릭으로 받는다.
2. Repository를 상속한 Data Repository 인터페이스
public interface DataRepository<T, ID> extends Repository<T, ID>{
// CRUD 기능을 가짐
// Create, Update
T save(T data);
// Read
Optional<T> findById(ID id);
List<T> findAll();
// Delete
void delete(ID id);
}
Repository를 상속하므로 같은 <T,ID>를 받는다.
DataRepository는 CRUD 기능을 가지고 있다. (구현부는 x)
save()는 ID를 확인해서 이미 존재하는 데이터면 update, 새로운 데이터면 create기능을 하도록 이후에 구현한다.
findById()는 ID를 받아 해당하는 데이터를 반환한다.
delete() 역시 ID를 받으며, 해당하는 데이터를 삭제하기 때문에 반환하는 값은 없다.
3. PrimaryKey 인터페이스
public interface PrimaryKey {
void setId(Long id);
Long getId();
}
데이터의 수가 많아질 수 있으니 Long 타입으로 한다.
Id의 getter, setter를 가지고 있다.
4. PrimaryKey를 상속한 Entity
@Data
public abstract class Entity implements PrimaryKey{
private Long id;
}
5. DataRepository를 구현한 Simple Data Repository
DataRepository를 상속한 추상클래스로 만들어서, SimpleDataRepository를 상속받는 클래스들이 구현을 해도 되고 안해도 되는 형태로 만든다.
public abstract class SimpleDataRepository<T extends Entity, ID extends Long> implements DataRepository<T, ID> {
// 저장공간
private List<T> dataList = new ArrayList<T>();
// 정렬자
private Comparator<T> sort = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
return Long.compare(o1.getId(), o2.getId());
}
};
// 유일한 ID 를 생성하기 위한 index
private static long index = 0;
// Create, Update
@Override
public T save(T data){
// 전달받은 data가 null일 때
if (Objects.isNull(data)){
throw new RuntimeException("Data is null.");
}
// db에 있는 데이터인지 확인
var prevData = dataList.stream()
.filter(it -> {
return it.getId().equals(data.getId());
}).findFirst();
// 기존 데이터가 있는 경우
// update
if (prevData.isPresent()){
dataList.remove(prevData);
dataList.add(data);
} else {
// 기존 데이터가 없는 경우
// create
index++;
data.setId(index);
dataList.add(data);
}
return null;
}
// Read
@Override
public Optional<T> findById(ID id) {
return dataList.stream()
.filter(it -> {
return it.getId().equals(id);
}).findFirst();
}
@Override
public List<T> findAll() {
// return dataList;
return dataList.stream()
.sorted(sort)
.collect(Collectors.toList());
}
// delete
@Override
public void delete(ID id) {
var deleteEntity = dataList.stream()
.filter(it -> {
return it.getId().equals(id);
}).findFirst();
if (deleteEntity.isPresent()){
dataList.remove(deleteEntity);
}
}
}
데이터를 저장할 dataList를 생성한다.
고유한 숫자인 ID를 생성하기 위해 index를 설정한다.
DataRepository의 CRUD 기능을 재정의한다.
- CREATE/UPDATE: save()
- 가장 먼저, save()로 전달받은 데이터가 null인지 확인하고, null이면 런타임 예외를 생성한다.
- db에 이미 저장되어있는 데이터인지 확인한다. dataList를 스트림으로 받아 Entity들의 getId()와 전달받은 data.getId()가 일치하는 경우를 찾아 Optional로 받는다.
- 기존에 데이터가 존재할 경우 Optional.isPresent()가 true이다. 이 때는 기존의 prevData를 dataList에서 삭제하고, 전달받았던 data를 새로 저장한다.
- 기존에 데이터가 존재하지 않는 경우, Optional.isPresent()가 false이다. 이 경우에는 Id에 setId()로 index에 1을 더한 수를 저장하고, dataList에 data를 저장해준다.
* save()에서는 ID를 set 해주어야 하는데, 제네릭 T로만 받으면 세팅해줄 수 없다. 이를 해결하기 위해 SimpleDataRepository가 받는 제네릭에 Wildcard를 사용하여 타입에 제한을 둔다. ("<T extends Entity, ID extends Long>"
)
- READ: findByID(), findAll()
- findById()
- dataList를 stream으로 받고, 전달받은 id와 동일한 id를 가진 데이터를 찾아 Optional로 반환한다.
- findAll()
- 전체 dataList를 반환한다.
- Id 순으로 정렬하기 위해 Comparator를 선언했다. Comparator는 제네릭으로 Entity를 받고, 해당 Entity의 getId()를 서로 비교해 순서대로 정렬된다.
- DELETE: delete()
- 전달받은 id로 dataList stream에서 id가 일치하는 data를 찾는다.
- data가 존재하면 dataList에서 삭제한다.