Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4단계 - EntityEntryStep4 #252

Open
wants to merge 5 commits into
base: dohoonkim023
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@

## 3단계
### 요구 사항1 - 만들었던 PersistenceContext 에서 효율적인 메모리 관리를 위한 기능 구현 (1차 캐싱)[O]
### 요구 사항2 - 더티체킹 구현[]
### 요구 사항2 - 더티체킹 구현[O]

## 4단계
### 요구 사항 1 - Entity 의 라이프 사이클 관리 작업 수행 시 엔터티의 상태를 추가[O]

15 changes: 15 additions & 0 deletions src/main/java/jdbc/JdbcTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ public void execute(final String sql) {
}
}

public Long executeAndReturnGeneratedKey(final String sql) {
try (final Statement statement = connection.createStatement()) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
return generatedKeys.getLong(1);
} else {
throw new RuntimeException("No ID generated for the insert.");
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public <T> T queryForObject(final String sql, final RowMapper<T> rowMapper) {
final List<T> results = query(sql, rowMapper);
if (results.size() != 1) {
Expand Down
34 changes: 0 additions & 34 deletions src/main/java/persistence/entity/DatabaseSnapshots.java

This file was deleted.

19 changes: 0 additions & 19 deletions src/main/java/persistence/entity/DirtyCheck.java

This file was deleted.

36 changes: 36 additions & 0 deletions src/main/java/persistence/entity/EntityEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package persistence.entity;

import static persistence.util.ReflectionCopy.copy;

import java.util.List;
import lombok.Getter;
import persistence.sql.dml.ColumnValues;

public class EntityEntry {
@Getter
private final Object entity;
@Getter
private Status status;
private Object snapshot;

public EntityEntry(Object entity, Status status) {
this.entity = entity;
this.status = status;
}

public void updateStatus(Status newStatus) {
this.status = newStatus;
}

public void updateSnapshot(Object entity) {
this.snapshot = copy(entity);
}

public List<String> findDirtyColumns(Object entity) throws IllegalAccessException {
if (status != Status.MANAGED) {
throw new IllegalAccessException("Entity is not managed");
}
return new ColumnValues<>(snapshot).findDifferentColumns(new ColumnValues<>(entity));
}

}
22 changes: 20 additions & 2 deletions src/main/java/persistence/entity/EntityPersister.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package persistence.entity;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -25,9 +26,15 @@ public void update(Object entity, List<String> changedColumns) throws IllegalAcc
.update(entity, changedColumns);
}

public void insert(Object entity) throws IllegalAccessException {
getEntityQueryHandler(entity.getClass())
public Object insert(Object entity) throws IllegalAccessException {
long id = getEntityQueryHandler(entity.getClass())
.insert(entity);
return setId(entity, id);
}

public void delete(Object entity) {
getEntityQueryHandler(entity.getClass())
.delete(entity);
}

private EntityQueryHandler<?> getEntityQueryHandler(Class<?> entityClass) {
Expand All @@ -37,4 +44,15 @@ private EntityQueryHandler<?> getEntityQueryHandler(Class<?> entityClass) {
return entityQueryHandlerMap.get(entityClass);
}

private Object setId(Object entity, long id) throws IllegalAccessException {
try {
Field idField = entity.getClass().getDeclaredField("id");
idField.setAccessible(true);
idField.set(entity, id);
return entity;
} catch (NoSuchFieldException e) {
throw new IllegalStateException("Entity does not have an 'id' field", e);
}
}

}
8 changes: 4 additions & 4 deletions src/main/java/persistence/entity/EntityQueryHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ public T findById(Object primaryKey) {
return jdbcTemplate.queryForObject(sql, entityLoader);
}

public void update(Object entity, List<String> changedColumns) throws IllegalAccessException {
public void update(Object entity, List<String> updateColumns) throws IllegalAccessException {
UpdateQuery updateQuery = sqlQueries.getSqlQuery(UPDATE);
String sql = updateQuery.generateQuery(entity, changedColumns);
String sql = updateQuery.generateQuery(entity, updateColumns);
jdbcTemplate.execute(sql);
}

public void insert(Object entity) throws IllegalAccessException {
public long insert(Object entity) throws IllegalAccessException {
InsertQuery insertQuery = sqlQueries.getSqlQuery(INSERT);
String sql = insertQuery.generateQuery(entity);
jdbcTemplate.execute(sql);
return jdbcTemplate.executeAndReturnGeneratedKey(sql);
}

public void delete(Object entity) {
Expand Down
21 changes: 11 additions & 10 deletions src/main/java/persistence/entity/PendingEntities.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
package persistence.entity;

import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PendingEntities {

private final Set<Object> pendingEntities = new HashSet<>();
private final Map<Integer, EntityEntry> entityEntries = new HashMap<>();

public void persistEntity(Object entity) {
pendingEntities.add(entity);
entityEntries.put(getKey(entity), new EntityEntry(entity, Status.SAVING));
}

public Set<Object> getEntities() {
return pendingEntities;
public List<Object> getEntities() {
return entityEntries.values().stream().map(EntityEntry::getEntity).toList();
}

public void removeEntity(Object entity) {
pendingEntities.remove(entity);
public void evict(Object entity) {
entityEntries.remove(getKey(entity));
}

public void clear() {
pendingEntities.clear();
private int getKey(Object entity) {
return System.identityHashCode(entity);
}

}
52 changes: 42 additions & 10 deletions src/main/java/persistence/entity/PersistedEntities.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,63 @@
package persistence.entity;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PersistedEntities {

private final Map<EntityKey, Object> persistedEntities = new HashMap<>();
private final Map<EntityKey, EntityEntry> entityEntries = new HashMap<>();

public Object findEntity(EntityKey entityKey) {
return persistedEntities.getOrDefault(entityKey, null);
if (entityEntries.containsKey(entityKey)) {
return entityEntries.get(entityKey).getEntity();
}
return null;
}

public void persistEntity(EntityKey entityKey, Object entity) {
persistedEntities.put(entityKey, entity);
var entityEntry = new EntityEntry(entity, Status.MANAGED);
entityEntry.updateSnapshot(entity);
entityEntries.put(entityKey, entityEntry);
}

public void removeEntity(EntityKey entityKey) {
persistedEntities.remove(entityKey);
public void evict(Object entity) {
var entityKey = getKey(entity);
entityEntries.remove(entityKey);
}

public Collection<Object> getEntities() {
return persistedEntities.values();
public void changeToDeleteState(EntityKey entityKey) {
var entityEntry = entityEntries.get(entityKey);
if (entityEntry == null) {
return;
}
entityEntry.updateStatus(Status.DELETED);
}

public void clear() {
persistedEntities.clear();
public List<Object> getDeletedEntities() {
return entityEntries.values().stream().filter(e -> e.getStatus() == Status.DELETED).map(EntityEntry::getEntity)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return entityEntries.values().stream().filter(e -> e.getStatus() == Status.DELETED).map(EntityEntry::getEntity)
return entityEntries.values().stream().filter(e -> e.isDeleted().map(EntityEntry::getEntity)

객체에게 물어볼수 있겠네요 🤔

.toList();
}

public List<Object> getManagedEntities() {
return entityEntries.values().stream().filter(e -> e.getStatus() == Status.MANAGED).map(EntityEntry::getEntity)
.toList();
}

public List<String> findDirtyColumns(Object entity) throws IllegalAccessException {
var entityKey = getKey(entity);
var entityEntry = entityEntries.get(entityKey);
return entityEntry.findDirtyColumns(entity);
}

public void updateDatabaseSnapshot(Object entity) {
var entityKey = getKey(entity);
var entityEntry = entityEntries.get(entityKey);
entityEntry.updateSnapshot(entity);
}

private EntityKey getKey(Object entity) {
return new EntityKey(new LongTypeId(entity).getId(), entity.getClass().getName());
}

}
19 changes: 11 additions & 8 deletions src/main/java/persistence/entity/PersistenceContext.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package persistence.entity;

import java.util.Collection;
import java.util.Set;
import java.util.List;

public interface PersistenceContext {

<T> T getEntity(Class<T> entityClass, Object primaryKey);

void attachEntity(Object entity);
void addEntity(Object entity);

void removeEntity(Object entity);

void detachEntity(Object entity);

Set<Object> getPendingEntities();
List<Object> getSavingEntities();

List<Object> getDeletedEntities();

Collection<Object> getPersistedEntities();
List<Object> getManagedEntities();

void captureDatabaseSnapshot(Object entity);
List<String> findDirtyColumns(Object entity) throws IllegalAccessException;

Object getDatabaseSnapshot(Object entity);
void updateDatabaseSnapshot(Object entity);

void reset();
void mangeEntity(Object entity) throws IllegalAccessException;

}
10 changes: 10 additions & 0 deletions src/main/java/persistence/entity/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package persistence.entity;

public enum Status {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 경우 EntityEntry 객체의 상태를 GONE으로 변경하는 것이 과연 의미가 있을지 고민이 되었습니다.
이 부분에 대한 의견이나 조언 주시면 감사하겠습니다!

우선 도훈님의 의견도 궁금해서 아래 질문드려 볼께요!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case 1

func test() {
  em.persist(person1);
  em.flush();
  
  em.remove(person1);
  em.persist(person1); <=== (1)
  em.flush();
}

(1) persist 후 flush될때 어떤 흐름을 기대하시나요?

case 2

func test() {
  em.persist(person1);
  em.flush();

  em.remove(person1);
  em.flush();
  em.persist(person1); <=== (1)
}

(1) persist 후 어떤 결과를 기대하시나요?

MANAGED,
READ_ONLY,
DELETED,
GONE,
LOADING,
SAVING
}
Loading