From 856e473461c4989b5b3a5afe8b3bee3b6ef83b61 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:02:16 +0900 Subject: [PATCH 01/30] =?UTF-8?q?feat:=20step1=20-=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?EntityMetaData=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20EntityJoinMet?= =?UTF-8?q?aData=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/entity/EntitySnapshot.java | 4 +- .../sql/ddl/CreateQueryBuilder.java | 3 +- .../persistence/sql/ddl/DropQueryBuilder.java | 4 +- .../{FieldInfo.java => EntityColumn.java} | 12 +-- src/main/java/pojo/EntityJoinMetaData.java | 73 +++++++++++++++++++ src/main/java/pojo/EntityMetaData.java | 42 +++++++++-- src/main/java/pojo/JoinColumnField.java | 73 +++++++++++++++++++ 7 files changed, 190 insertions(+), 21 deletions(-) rename src/main/java/pojo/{FieldInfo.java => EntityColumn.java} (64%) create mode 100644 src/main/java/pojo/EntityJoinMetaData.java create mode 100644 src/main/java/pojo/JoinColumnField.java diff --git a/src/main/java/persistence/entity/EntitySnapshot.java b/src/main/java/persistence/entity/EntitySnapshot.java index 66efc8ce..3902840e 100644 --- a/src/main/java/persistence/entity/EntitySnapshot.java +++ b/src/main/java/persistence/entity/EntitySnapshot.java @@ -1,6 +1,6 @@ package persistence.entity; -import pojo.FieldInfo; +import pojo.EntityColumn; import pojo.FieldInfos; import java.lang.reflect.Field; @@ -17,7 +17,7 @@ public class EntitySnapshot { public EntitySnapshot(Object entity) { List fields = new FieldInfos(entity.getClass().getDeclaredFields()).getIdAndColumnFields(); this.map = fields.stream() - .map(field -> new FieldInfo(field, entity)) + .map(field -> new EntityColumn(field, entity)) .collect(Collectors.toMap( fieldInfo -> fieldInfo.getFieldName().getName(), fieldInfo -> fieldInfo.getFieldValue().getValue() diff --git a/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java b/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java index 67178aac..8f875eac 100644 --- a/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java +++ b/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java @@ -34,9 +34,10 @@ public CreateQueryBuilder(Dialect dialect, EntityMetaData entityMetaData) { public String createTable(Object entity) { FieldInfos fieldInfos = new FieldInfos(entity.getClass().getDeclaredFields()); - return String.format(CREATE_TABLE_QUERY, entityMetaData.getTableInfo().getName(), createClause(fieldInfos.getFieldDataList(), entity)); + return String.format(CREATE_TABLE_QUERY, entityMetaData.getEntityName(), createClause(fieldInfos.getFieldDataList(), entity)); } + //create 시에 JoinColumn 어노테이션이 있는 필드는 -> JoinColumn 의 name 을 그 필드 객체 생성 시 만들어줘야 한다. private String createClause(List fields, Object entity) { return fields.stream() .filter(field -> !field.isAnnotationPresent(Transient.class)) diff --git a/src/main/java/persistence/sql/ddl/DropQueryBuilder.java b/src/main/java/persistence/sql/ddl/DropQueryBuilder.java index 26f7b09e..ac2c2e6f 100644 --- a/src/main/java/persistence/sql/ddl/DropQueryBuilder.java +++ b/src/main/java/persistence/sql/ddl/DropQueryBuilder.java @@ -4,7 +4,7 @@ public class DropQueryBuilder { - private static final String DROP_TABLE_QUERY = "DROP TABLE %s IF EXISTS;"; + private static final String DROP_TABLE_QUERY = "DROP TABLE IF EXISTS %s;"; private final EntityMetaData entityMetaData; @@ -13,6 +13,6 @@ public DropQueryBuilder(EntityMetaData entityMetaData) { } public String dropTable() { - return String.format(DROP_TABLE_QUERY, entityMetaData.getTableInfo().getName()); + return String.format(DROP_TABLE_QUERY, entityMetaData.getEntityName()); } } diff --git a/src/main/java/pojo/FieldInfo.java b/src/main/java/pojo/EntityColumn.java similarity index 64% rename from src/main/java/pojo/FieldInfo.java rename to src/main/java/pojo/EntityColumn.java index c0acbb25..df99568b 100644 --- a/src/main/java/pojo/FieldInfo.java +++ b/src/main/java/pojo/EntityColumn.java @@ -3,13 +3,13 @@ import java.lang.reflect.Field; import java.util.Objects; -public class FieldInfo { +public class EntityColumn { private final Field field; private final FieldName fieldName; private final FieldValue fieldValue; - public FieldInfo(Field field, Object entity) { + public EntityColumn(Field field, Object entity) { if (Objects.isNull(field)) { throw new IllegalArgumentException("field 가 null 이어서는 안됩니다."); } @@ -29,12 +29,4 @@ public FieldName getFieldName() { public FieldValue getFieldValue() { return fieldValue; } - - public boolean isNotBlankOrEmpty() { - return Objects.nonNull(fieldName) && Objects.nonNull(fieldValue); - } - - public String joinNameAndValueWithDelimiter(String delimiter) { - return String.join(delimiter, fieldName.getName(), String.valueOf(fieldValue.getValue())); - } } diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java new file mode 100644 index 00000000..df3a63b8 --- /dev/null +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -0,0 +1,73 @@ +package pojo; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; + +/** + * ex) Order + */ +public class EntityJoinMetaData implements EntityClass { + + private final EntityMetaData owner; //orderItem + private final Class clazz; + private final Object entity; + private final String entityName; + private final List entityColumns; + private final boolean lazy; + + public EntityJoinMetaData(EntityMetaData owner, Class clazz, Object entity) { + if (!clazz.isAnnotationPresent(Entity.class)) { + throw new IllegalStateException("Entity 클래스가 아닙니다."); + } + this.owner = owner; + this.clazz = clazz; + this.entity = entity; + this.entityName = getEntityNameInfo(); + this.entityColumns = getEntityColumnsInfo(); + this.lazy = isLazy(); + } + + public EntityMetaData getOwner() { + return owner; + } + + @Override + public String getEntityName() { + return entityName; + } + + @Override + public List getEntityColumns() { + return entityColumns; + } + + public String joinColumnName() { + return new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField().getAnnotation(JoinColumn.class).name(); + } + + private String getEntityNameInfo() { + return clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() + : clazz.getSimpleName().toLowerCase(); + } + + private List getEntityColumnsInfo() { + return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() + .map(field -> new EntityColumn(field, entity)) + .collect(Collectors.toList()); + } + + private boolean isLazy() { + Field joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); + + //일단 OneToMany 만 고려 + FetchType fetchType = joinColumnField.getAnnotation(OneToMany.class).fetch(); + return !fetchType.equals(FetchType.EAGER); + } +} diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 86f64951..be394ea6 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -1,19 +1,49 @@ package pojo; import jakarta.persistence.Entity; +import jakarta.persistence.Table; -public class EntityMetaData { +import java.util.List; +import java.util.stream.Collectors; - private final TableInfo tableInfo; +/** + * ex) OrderItem + */ +public class EntityMetaData implements EntityClass { - public EntityMetaData(Class clazz) { + private final Class clazz; + private final Object entity; + private final String entityName; + private final List entityColumns; + + public EntityMetaData(Class clazz, Object entity) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } - this.tableInfo = new TableInfo(clazz); + this.clazz = clazz; + this.entity = entity; + this.entityName = getEntityNameInfo(); + this.entityColumns = getEntityColumnsInfo(); + } + + @Override + public String getEntityName() { + return entityName; + } + + @Override + public List getEntityColumns() { + return entityColumns; + } + + private String getEntityNameInfo() { + return clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() + : clazz.getSimpleName().toLowerCase(); } - public TableInfo getTableInfo() { - return tableInfo; + private List getEntityColumnsInfo() { + return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() + .map(field -> new EntityColumn(field, entity)) + .collect(Collectors.toList()); } } diff --git a/src/main/java/pojo/JoinColumnField.java b/src/main/java/pojo/JoinColumnField.java new file mode 100644 index 00000000..054940f5 --- /dev/null +++ b/src/main/java/pojo/JoinColumnField.java @@ -0,0 +1,73 @@ +package pojo; + +import jakarta.persistence.Column; + +import java.lang.reflect.Field; +import java.util.Objects; + +public class JoinColumnField implements FieldData { + + private final EntityColumn entityColumn; + + public JoinColumnField(Field field, Object entity) { + this.entityColumn = new EntityColumn(field, entity); + } + + public String getFieldLength(boolean isVarcharType) { + return Objects.nonNull(getColumnLength(isVarcharType)) ? "(" + getColumnLength(isVarcharType) + ")" : null; + } + + public String getColumnNullConstraint() { + if (!isColumnField() || entityColumn.getField().getAnnotation(Column.class).nullable()) { + return Constraints.NULL.getName(); + } + return Constraints.NOT_NULL.getName(); + } + + private String getColumnLength(boolean isVarcharType) { + if (isColumnField() && isVarcharType) { + return String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); + } + + if (isColumnField() && !isVarcharType) { + return getLengthOrDefaultValue(255); + } + + return null; + } + + private String getLengthOrDefaultValue(int defaultLengthValue) { + return entityColumn.getField().getAnnotation(Column.class).length() == defaultLengthValue ? null + : String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); + } + + @Override + public boolean isIdField() { + return false; + } + + @Override + public boolean isColumnField() { + return true; + } + + @Override + public boolean isNotTransientField() { + return true; + } + + @Override + public boolean isNullableField() { + return entityColumn.getField().getAnnotation(Column.class).nullable(); + } + + @Override + public String getFieldNameData() { + return entityColumn.getFieldName().getName(); + } + + @Override + public Object getFieldValueData() { + return entityColumn.getFieldValue().getValue(); + } +} From f54f250102f7aa4b8450b28dc15440c0ec6f2c1c Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:03:07 +0900 Subject: [PATCH 02/30] =?UTF-8?q?refactor:=20step1=20-=20=EA=B8=B0?= =?UTF-8?q?=EC=A1=B4=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20EntityMetaData=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/dml/DeleteQueryBuilder.java | 2 +- .../sql/dml/SelectQueryBuilder.java | 4 ++-- .../sql/dml/UpdateQueryBuilder.java | 21 +++++++++++-------- src/main/java/pojo/ColumnField.java | 18 ++++++++-------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/persistence/sql/dml/DeleteQueryBuilder.java b/src/main/java/persistence/sql/dml/DeleteQueryBuilder.java index f6fde488..c846bd8a 100644 --- a/src/main/java/persistence/sql/dml/DeleteQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/DeleteQueryBuilder.java @@ -17,7 +17,7 @@ public DeleteQueryBuilder(EntityMetaData entityMetaData) { } public String deleteQuery(IdField idField) { - return String.format(DELETE_QUERY, entityMetaData.getTableInfo().getName(), idField.getFieldNameData(), idField.getFieldValueData()); + return String.format(DELETE_QUERY, entityMetaData.getEntityName(), idField.getFieldNameData(), idField.getFieldValueData()); } public String deleteByIdQuery(Object entity) { diff --git a/src/main/java/persistence/sql/dml/SelectQueryBuilder.java b/src/main/java/persistence/sql/dml/SelectQueryBuilder.java index d5fad380..3cb0f60d 100644 --- a/src/main/java/persistence/sql/dml/SelectQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/SelectQueryBuilder.java @@ -18,13 +18,13 @@ public SelectQueryBuilder(EntityMetaData entityMetaData) { } public String findAllQuery() { - return String.format(FIND_ALL_QUERY, entityMetaData.getTableInfo().getName()); + return String.format(FIND_ALL_QUERY, entityMetaData.getEntityName()); } public String findByIdQuery(Object entity, Class clazz, Object condition) { Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); IdField idField = new IdField(field, entity); - return String.format(FIND_BY_ID_QUERY, entityMetaData.getTableInfo().getName(), + return String.format(FIND_BY_ID_QUERY, entityMetaData.getEntityName(), idField.getFieldNameData(), condition); } } diff --git a/src/main/java/persistence/sql/dml/UpdateQueryBuilder.java b/src/main/java/persistence/sql/dml/UpdateQueryBuilder.java index d0b5d3f4..a9e03f72 100644 --- a/src/main/java/persistence/sql/dml/UpdateQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/UpdateQueryBuilder.java @@ -1,15 +1,17 @@ package persistence.sql.dml; +import pojo.EntityColumn; import pojo.EntityMetaData; -import pojo.FieldInfo; import pojo.FieldInfos; import java.lang.reflect.Field; import java.util.List; +import java.util.Objects; import static constants.CommonConstants.AND; import static constants.CommonConstants.COMMA; import static constants.CommonConstants.EQUAL; +import static utils.StringUtils.joinNameAndValue; public class UpdateQueryBuilder { @@ -23,16 +25,16 @@ public UpdateQueryBuilder(EntityMetaData entityMetaData) { } public String insertQuery(Object entity) { - return String.format(INSERT_DATA_QUERY, entityMetaData.getTableInfo().getName(), columnsClause(entity), valuesClause(entity)); + return String.format(INSERT_DATA_QUERY, entityMetaData.getEntityName(), columnsClause(entity), valuesClause(entity)); } public String updateQuery(Object entity) { - return String.format(UPDATE_DATA_QUERY, entityMetaData.getTableInfo().getName(), setClause(entity), whereClause(entity)); + return String.format(UPDATE_DATA_QUERY, entityMetaData.getEntityName(), setClause(entity), whereClause(entity)); } private String columnsClause(Object entity) { return new FieldInfos(entity.getClass().getDeclaredFields()).getIdAndColumnFields().stream() - .map(field -> new FieldInfo(field, entity)) + .map(field -> new EntityColumn(field, entity)) .map(fieldInfo -> fieldInfo.getFieldName().getName()) .reduce((o1, o2) -> String.join(COMMA, o1, String.valueOf(o2))) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); @@ -40,7 +42,7 @@ private String columnsClause(Object entity) { private String valuesClause(Object entity) { return new FieldInfos(entity.getClass().getDeclaredFields()).getIdAndColumnFields().stream() - .map(field -> new FieldInfo(field, entity)) + .map(field -> new EntityColumn(field, entity)) .map(fieldInfo -> fieldInfo.getFieldValue().getValue()) .reduce((o1, o2) -> String.join(COMMA, o1, String.valueOf(o2))) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); @@ -58,10 +60,11 @@ private String whereClause(Object entity) { private String fieldNameAndValueClause(Object entity, List fields, String delimiter) { return fields.stream() - .map(field -> new FieldInfo(field, entity)) - .filter(FieldInfo::isNotBlankOrEmpty) - .map(fieldInfo -> fieldInfo.joinNameAndValueWithDelimiter(EQUAL)) - .reduce((o1, o2) -> String.join(delimiter, o1, String.valueOf(o2))) + .map(field -> new EntityColumn(field, entity)) + .filter(fieldInfo -> Objects.nonNull(fieldInfo.getFieldName()) && Objects.nonNull(fieldInfo.getFieldValue())) + .map(fieldInfo -> + joinNameAndValue(EQUAL, fieldInfo.getFieldName().getName(), String.valueOf(fieldInfo.getFieldValue().getValue()))) + .reduce((o1, o2) -> String.join(delimiter, o1, o2)) .orElseThrow(() -> new IllegalStateException("update 데이터가 없습니다.")); } } diff --git a/src/main/java/pojo/ColumnField.java b/src/main/java/pojo/ColumnField.java index 6ac8c35b..c64a5add 100644 --- a/src/main/java/pojo/ColumnField.java +++ b/src/main/java/pojo/ColumnField.java @@ -7,10 +7,10 @@ public class ColumnField implements FieldData { - private final FieldInfo fieldInfo; + private final EntityColumn entityColumn; public ColumnField(Field field, Object entity) { - this.fieldInfo = new FieldInfo(field, entity); + this.entityColumn = new EntityColumn(field, entity); } public String getFieldLength(boolean isVarcharType) { @@ -18,7 +18,7 @@ public String getFieldLength(boolean isVarcharType) { } public String getColumnNullConstraint() { - if (!isColumnField() || fieldInfo.getField().getAnnotation(Column.class).nullable()) { + if (!isColumnField() || entityColumn.getField().getAnnotation(Column.class).nullable()) { return Constraints.NULL.getName(); } return Constraints.NOT_NULL.getName(); @@ -26,7 +26,7 @@ public String getColumnNullConstraint() { private String getColumnLength(boolean isVarcharType) { if (isColumnField() && isVarcharType) { - return String.valueOf(fieldInfo.getField().getAnnotation(Column.class).length()); + return String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); } if (isColumnField() && !isVarcharType) { @@ -37,8 +37,8 @@ private String getColumnLength(boolean isVarcharType) { } private String getLengthOrDefaultValue(int defaultLengthValue) { - return fieldInfo.getField().getAnnotation(Column.class).length() == defaultLengthValue ? null - : String.valueOf(fieldInfo.getField().getAnnotation(Column.class).length()); + return entityColumn.getField().getAnnotation(Column.class).length() == defaultLengthValue ? null + : String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); } @Override @@ -58,16 +58,16 @@ public boolean isNotTransientField() { @Override public boolean isNullableField() { - return fieldInfo.getField().getAnnotation(Column.class).nullable(); + return entityColumn.getField().getAnnotation(Column.class).nullable(); } @Override public String getFieldNameData() { - return fieldInfo.getFieldName().getName(); + return entityColumn.getFieldName().getName(); } @Override public Object getFieldValueData() { - return fieldInfo.getFieldValue().getValue(); + return entityColumn.getFieldValue().getValue(); } } From a27127b65a928010236381eee4c1be7046ef57db Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:03:24 +0900 Subject: [PATCH 03/30] =?UTF-8?q?refactor:=20step1=20-=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/FieldInfos.java | 37 +++++++++++++++++------ src/main/java/pojo/FieldNameAndValue.java | 30 ------------------ src/main/java/pojo/IdField.java | 16 +++++----- 3 files changed, 36 insertions(+), 47 deletions(-) delete mode 100644 src/main/java/pojo/FieldNameAndValue.java diff --git a/src/main/java/pojo/FieldInfos.java b/src/main/java/pojo/FieldInfos.java index 37dc97f7..cca53833 100644 --- a/src/main/java/pojo/FieldInfos.java +++ b/src/main/java/pojo/FieldInfos.java @@ -1,6 +1,7 @@ package pojo; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; import jakarta.persistence.Transient; import java.lang.reflect.Field; @@ -12,35 +13,53 @@ public class FieldInfos { - private final List fieldInfoList; + private final List fieldList; public FieldInfos(Field[] fields) { if (Objects.isNull(fields)) { throw new IllegalArgumentException("fields 가 null 이어서는 안됩니다."); } - this.fieldInfoList = Arrays.stream(fields).collect(Collectors.toList()); + this.fieldList = Arrays.stream(fields).collect(Collectors.toList()); } public List getFieldDataList() { - return fieldInfoList; + return fieldList; } public Field getIdField() { - return fieldInfoList.stream() - .filter(field -> field.isAnnotationPresent(Id.class)) + return fieldList.stream() + .filter(this::isIdField) .findFirst() .orElseThrow(() -> new IllegalStateException("Id 필드가 존재하지 않습니다.")); } public List getColumnFields() { - return fieldInfoList.stream() - .filter(field -> !field.isAnnotationPresent(Transient.class) && !field.isAnnotationPresent(Id.class)) + return fieldList.stream() + .filter(field -> !isIdField(field) && !isTransientField(field) && !isJoinColumnField(field)) .collect(Collectors.toCollection(LinkedList::new)); } public List getIdAndColumnFields() { - return fieldInfoList.stream() - .filter(field -> !field.isAnnotationPresent(Transient.class)) + return fieldList.stream() + .filter(field -> !isTransientField(field) && !isJoinColumnField(field)) .collect(Collectors.toCollection(LinkedList::new)); } + + public Field getJoinColumnField() { + return fieldList.stream() + .filter(this::isJoinColumnField) + .findFirst().orElse(null); + } + + private boolean isIdField(Field field) { + return field.isAnnotationPresent(Id.class); + } + + private boolean isTransientField(Field field) { + return field.isAnnotationPresent(Transient.class); + } + + private boolean isJoinColumnField(Field field) { + return field.isAnnotationPresent(JoinColumn.class); + } } diff --git a/src/main/java/pojo/FieldNameAndValue.java b/src/main/java/pojo/FieldNameAndValue.java deleted file mode 100644 index 4e192bf4..00000000 --- a/src/main/java/pojo/FieldNameAndValue.java +++ /dev/null @@ -1,30 +0,0 @@ -package pojo; - -import java.util.Objects; - -public class FieldNameAndValue { - - private final FieldName fieldName; - private final FieldValue fieldValue; - - public FieldNameAndValue(FieldName fieldName, FieldValue fieldValue) { - this.fieldName = fieldName; - this.fieldValue = fieldValue; - } - - public FieldName getFieldName() { - return fieldName; - } - - public FieldValue getFieldValue() { - return fieldValue; - } - - public String joinNameAndValueWithDelimiter(String delimiter) { - return String.join(delimiter, getFieldName().getName(), String.valueOf(getFieldValue().getValue())); - } - - public boolean isNotBlankOrEmpty() { - return Objects.nonNull(fieldName) && Objects.nonNull(fieldValue); - } -} diff --git a/src/main/java/pojo/IdField.java b/src/main/java/pojo/IdField.java index bd3b746d..79ad9547 100644 --- a/src/main/java/pojo/IdField.java +++ b/src/main/java/pojo/IdField.java @@ -7,16 +7,16 @@ public class IdField implements FieldData { - private final FieldInfo fieldInfo; + private final EntityColumn entityColumn; private final H2GenerationType generationType; public IdField(Field field, Object entity) { - this.fieldInfo = new FieldInfo(field, entity); + this.entityColumn = new EntityColumn(field, entity); this.generationType = getGenerationType(); } - public FieldInfo getFieldInfoTemp() { - return fieldInfo; + public EntityColumn getFieldInfoTemp() { + return entityColumn; } public String getGenerationTypeStrategy() { @@ -29,8 +29,8 @@ public boolean isGenerationTypeAutoOrIdentity() { } private H2GenerationType getGenerationType() { - if (fieldInfo.getField().isAnnotationPresent(GeneratedValue.class)) { - return H2GenerationType.from(fieldInfo.getField().getAnnotation(GeneratedValue.class).strategy()); + if (entityColumn.getField().isAnnotationPresent(GeneratedValue.class)) { + return H2GenerationType.from(entityColumn.getField().getAnnotation(GeneratedValue.class).strategy()); } return null; } @@ -57,11 +57,11 @@ public boolean isNullableField() { @Override public String getFieldNameData() { - return fieldInfo.getFieldName().getName(); + return entityColumn.getFieldName().getName(); } @Override public Object getFieldValueData() { - return fieldInfo.getFieldValue().getValue(); + return entityColumn.getFieldValue().getValue(); } } From 588fac749195ee2d20e3938332280fe209bd3a38 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:03:38 +0900 Subject: [PATCH 04/30] =?UTF-8?q?feat:=20step1=20-=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/entity/Order.java | 69 +++++++++++++++++++++++++++++ src/main/java/entity/OrderItem.java | 65 +++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/main/java/entity/Order.java create mode 100644 src/main/java/entity/OrderItem.java diff --git a/src/main/java/entity/Order.java b/src/main/java/entity/Order.java new file mode 100644 index 00000000..564fe092 --- /dev/null +++ b/src/main/java/entity/Order.java @@ -0,0 +1,69 @@ +package entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import java.util.List; +import java.util.Objects; + +@Entity +@Table(name = "orders") +public class Order { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "order_number") + private String orderNumber; + + @OneToMany(fetch = FetchType.EAGER) + @JoinColumn(name = "order_id") + private List orderItems; + + public Order() { + } + + public Order(Long id, String orderNumber, List orderItems) { + this.id = id; + this.orderNumber = orderNumber; + this.orderItems = orderItems; + } + + public Long getId() { + return id; + } + + public String getOrderNumber() { + return orderNumber; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + Order order = (Order) object; + return Objects.equals(id, order.id) && Objects.equals(orderNumber, order.orderNumber) && Objects.equals(orderItems, order.orderItems); + } + + @Override + public int hashCode() { + return Objects.hash(id, orderNumber, orderItems); + } + + @Override + public String toString() { + return "Order{" + + "id=" + id + + ", orderNumber='" + orderNumber + '\'' + + ", orderItems=" + orderItems + + '}'; + } +} diff --git a/src/main/java/entity/OrderItem.java b/src/main/java/entity/OrderItem.java new file mode 100644 index 00000000..9ead37e8 --- /dev/null +++ b/src/main/java/entity/OrderItem.java @@ -0,0 +1,65 @@ +package entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import java.util.Objects; + +@Entity +@Table(name = "order_items") +public class OrderItem { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String product; + + private Integer quantity; + + public OrderItem() { + } + + public OrderItem(Long id, String product, Integer quantity) { + this.id = id; + this.product = product; + this.quantity = quantity; + } + + public Long getId() { + return id; + } + + public String getProduct() { + return product; + } + + public Integer getQuantity() { + return quantity; + } + + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + OrderItem orderItem = (OrderItem) object; + return Objects.equals(id, orderItem.id) && Objects.equals(product, orderItem.product) && Objects.equals(quantity, orderItem.quantity); + } + + @Override + public int hashCode() { + return Objects.hash(id, product, quantity); + } + + @Override + public String toString() { + return "OrderItem{" + + "id=" + id + + ", product='" + product + '\'' + + ", quantity=" + quantity + + '}'; + } +} From 63e564670be77135bc144cf60201bf21774d00d7 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:03:53 +0900 Subject: [PATCH 05/30] =?UTF-8?q?docs:=20step1=20-=20=EA=B0=95=EC=9D=98=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=20=EB=B0=8F=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=AC=B8=EC=84=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/docs/lecture.md | 28 ++++++++++++++++++++++++++++ src/main/java/docs/step1.md | 20 ++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/docs/lecture.md create mode 100644 src/main/java/docs/step1.md diff --git a/src/main/java/docs/lecture.md b/src/main/java/docs/lecture.md new file mode 100644 index 00000000..db0c94d1 --- /dev/null +++ b/src/main/java/docs/lecture.md @@ -0,0 +1,28 @@ +## 3/16 + +### 구현 +```markdown +- Query +- Entity +-> 이렇게 2개의 패키지로 나누고, 1주차 미션과 약간 별개일 수 있다. + +- SessionImpl 에 의해 session 을 계속 넣어서 다닌다. + - `return new StatefulPersistenceContext(this);` +- StatefulPersistenceContext + - `entitiesByKey` + - `collectionsByKey` +- AnnotationBinder 참고 + +- InFlightMetadataCollector + - metadata 수집 + - 캐싱 설정(default = true) + +``` + +### logging +```markdown +- logging.level.org.hibernate.boot=trace +- logging.level.org.hibernate=trace => 직접 api 실행해서 로깅 확인 +- Spring 은 로깅해서 확인해보는 것을 추천 => Spring 공부! +``` + diff --git a/src/main/java/docs/step1.md b/src/main/java/docs/step1.md new file mode 100644 index 00000000..0b020f16 --- /dev/null +++ b/src/main/java/docs/step1.md @@ -0,0 +1,20 @@ +# 1단계 - OneToMany (FetchType.EAGER) + +## 요구사항 1 - Join Query 만들기 + +- Sql 쿼리 문을 수정해 보자 +```java +public class CustomSelect { + +} + +``` + +## 요구사항 2 - Join Query 를 만들어 Entity 화 해보기 + +- FetchType.EAGER 인 경우 +```java +public class SimplePersistenceContext implements PersistenceContext { + +} +``` From 3cc9e123beaeea30855037b3a44fb1850e965ff7 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:04:26 +0900 Subject: [PATCH 06/30] =?UTF-8?q?test:=20step1=20-=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0,=20=EC=A4=91=EB=B3=B5=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/persistence/JpaTest.java | 28 +++++++++++++++++ .../context/SimplePersistenceContextTest.java | 27 ++++------------- .../entity/CustomJpaRepositoryTest.java | 28 ++++------------- .../entity/EntityLoaderImplTest.java | 27 ++++------------- .../entity/EntityPersisterImplTest.java | 30 ++++++------------- 5 files changed, 55 insertions(+), 85 deletions(-) create mode 100644 src/test/java/persistence/JpaTest.java diff --git a/src/test/java/persistence/JpaTest.java b/src/test/java/persistence/JpaTest.java new file mode 100644 index 00000000..dd1e4980 --- /dev/null +++ b/src/test/java/persistence/JpaTest.java @@ -0,0 +1,28 @@ +package persistence; + +import database.DatabaseServer; +import dialect.Dialect; +import dialect.H2Dialect; +import jdbc.JdbcTemplate; +import persistence.context.PersistenceContext; +import persistence.context.SimplePersistenceContext; +import persistence.entity.EntityLoader; +import persistence.entity.EntityPersister; +import persistence.entity.JpaRepository; +import persistence.entity.SimpleEntityEntry; +import persistence.entity.SimpleEntityManager; +import pojo.EntityMetaData; + +public abstract class JpaTest { + + protected static Dialect dialect = new H2Dialect(); + protected static EntityMetaData entityMetaData; + protected static DatabaseServer server; + protected static JdbcTemplate jdbcTemplate; + protected static EntityPersister entityPersister; + protected static EntityLoader entityLoader; + protected static SimpleEntityManager simpleEntityManager; + protected static PersistenceContext persistenceContext = new SimplePersistenceContext(); + protected static SimpleEntityEntry entityEntry; + protected static JpaRepository jpaRepository; +} diff --git a/src/test/java/persistence/context/SimplePersistenceContextTest.java b/src/test/java/persistence/context/SimplePersistenceContextTest.java index 8527746f..0b716a82 100644 --- a/src/test/java/persistence/context/SimplePersistenceContextTest.java +++ b/src/test/java/persistence/context/SimplePersistenceContextTest.java @@ -1,9 +1,6 @@ package persistence.context; -import database.DatabaseServer; import database.H2; -import dialect.Dialect; -import dialect.H2Dialect; import entity.Person3; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; @@ -12,9 +9,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import persistence.entity.EntityLoader; +import persistence.JpaTest; import persistence.entity.EntityLoaderImpl; -import persistence.entity.EntityPersister; import persistence.entity.EntityPersisterImpl; import persistence.entity.EntitySnapshot; import persistence.entity.SimpleEntityEntry; @@ -33,37 +29,26 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; -class SimplePersistenceContextTest { +class SimplePersistenceContextTest extends JpaTest { - static Dialect dialect = new H2Dialect(); - static EntityMetaData entityMetaData = new EntityMetaData(Person3.class); - - static DatabaseServer server; - static JdbcTemplate jdbcTemplate; - static EntityPersister entityPersister; - static EntityLoader entityLoader; - static SimpleEntityManager simpleEntityManager; - static PersistenceContext persistenceContext; - static SimpleEntityEntry entityEntry; - - Person3 person; + static Person3 person = new Person3(1L, "test", 20, "test@test.com"); @BeforeAll static void init() throws SQLException { server = new H2(); server.start(); - jdbcTemplate = new JdbcTemplate(server.getConnection()); + + entityMetaData = new EntityMetaData(Person3.class, person); entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - persistenceContext = new SimplePersistenceContext(); entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); + simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); } @BeforeEach void setUp() { - person = new Person3(1L, "test", 20, "test@test.com"); createTable(); } diff --git a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java index b251b3dd..6363ac98 100644 --- a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java +++ b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java @@ -1,9 +1,6 @@ package persistence.entity; -import database.DatabaseServer; import database.H2; -import dialect.Dialect; -import dialect.H2Dialect; import entity.Person3; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; @@ -12,8 +9,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import persistence.context.PersistenceContext; -import persistence.context.SimplePersistenceContext; +import persistence.JpaTest; import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import pojo.EntityMetaData; @@ -24,39 +20,27 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -class CustomJpaRepositoryTest { +class CustomJpaRepositoryTest extends JpaTest { - static Dialect dialect = new H2Dialect(); - static EntityMetaData entityMetaData = new EntityMetaData(Person3.class); - - static DatabaseServer server; - static JdbcTemplate jdbcTemplate; - static EntityPersister entityPersister; - static EntityLoader entityLoader; - static SimpleEntityManager simpleEntityManager; - static PersistenceContext persistenceContext; - static JpaRepository jpaRepository; - static EntityEntry entityEntry; - - Person3 person; + static Person3 person = new Person3(1L, "test", 20, "test@test.com"); @BeforeAll static void init() throws SQLException { server = new H2(); server.start(); - jdbcTemplate = new JdbcTemplate(server.getConnection()); + + entityMetaData = new EntityMetaData(Person3.class, person); entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - persistenceContext = new SimplePersistenceContext(); entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); + simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); jpaRepository = new CustomJpaRepository(simpleEntityManager); } @BeforeEach void setUp() { - person = new Person3(1L, "test", 20, "test@test.com"); createTable(); } diff --git a/src/test/java/persistence/entity/EntityLoaderImplTest.java b/src/test/java/persistence/entity/EntityLoaderImplTest.java index 17ab4dce..4ca321af 100644 --- a/src/test/java/persistence/entity/EntityLoaderImplTest.java +++ b/src/test/java/persistence/entity/EntityLoaderImplTest.java @@ -1,9 +1,6 @@ package persistence.entity; -import database.DatabaseServer; import database.H2; -import dialect.Dialect; -import dialect.H2Dialect; import entity.Person3; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; @@ -12,8 +9,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import persistence.context.PersistenceContext; -import persistence.context.SimplePersistenceContext; +import persistence.JpaTest; import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import pojo.EntityMetaData; @@ -24,37 +20,26 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -class EntityLoaderImplTest { +class EntityLoaderImplTest extends JpaTest { - static Dialect dialect = new H2Dialect(); - static EntityMetaData entityMetaData = new EntityMetaData(Person3.class); - - static DatabaseServer server; - static JdbcTemplate jdbcTemplate; - static EntityPersister entityPersister; - static EntityLoader entityLoader; - static SimpleEntityManager simpleEntityManager; - static PersistenceContext persistenceContext; - static EntityEntry entityEntry; - - Person3 person; + static Person3 person = new Person3(1L, "test", 20, "test@test.com"); @BeforeAll static void init() throws SQLException { server = new H2(); server.start(); - jdbcTemplate = new JdbcTemplate(server.getConnection()); + + entityMetaData = new EntityMetaData(Person3.class, person); entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - persistenceContext = new SimplePersistenceContext(); entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); + simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); } @BeforeEach void setUp() { - person = new Person3(1L, "test", 20, "test@test.com"); createTable(); } diff --git a/src/test/java/persistence/entity/EntityPersisterImplTest.java b/src/test/java/persistence/entity/EntityPersisterImplTest.java index a0d4a3aa..c2efedfd 100644 --- a/src/test/java/persistence/entity/EntityPersisterImplTest.java +++ b/src/test/java/persistence/entity/EntityPersisterImplTest.java @@ -1,9 +1,6 @@ package persistence.entity; -import database.DatabaseServer; import database.H2; -import dialect.Dialect; -import dialect.H2Dialect; import entity.Person3; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; @@ -12,8 +9,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import persistence.context.PersistenceContext; -import persistence.context.SimplePersistenceContext; +import persistence.JpaTest; import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import persistence.sql.dml.UpdateQueryBuilder; @@ -26,37 +22,26 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; -class EntityPersisterImplTest { +class EntityPersisterImplTest extends JpaTest { - static Dialect dialect = new H2Dialect(); - static EntityMetaData entityMetaData = new EntityMetaData(Person3.class); - - static DatabaseServer server; - static JdbcTemplate jdbcTemplate; - static EntityPersister entityPersister; - static EntityLoader entityLoader; - static SimpleEntityManager simpleEntityManager; - static PersistenceContext persistenceContext; - static EntityEntry entityEntry; - - Person3 person; + static Person3 person = new Person3(1L, "test", 20, "test@test.com"); @BeforeAll static void init() throws SQLException { server = new H2(); server.start(); - jdbcTemplate = new JdbcTemplate(server.getConnection()); + + entityMetaData = new EntityMetaData(Person3.class, person); entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - persistenceContext = new SimplePersistenceContext(); entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); + simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); } @BeforeEach void setUp() { - person = new Person3(1L, "test", 20, "test@test.com"); createTable(); } @@ -73,8 +58,11 @@ static void destroy() { @DisplayName("insert 테스트") @Test void insertTest() { + dropTable(); + createTable(); entityPersister.insert(person); Person3 person3 = simpleEntityManager.find(person, person.getClass(), person.getId()); + assertAll( () -> assertThat(person3.getId()).isEqualTo(person.getId()), () -> assertThat(person3.getName()).isEqualTo(person.getName()), From 4600e062c9c3826e1e00b3efe4a03d7483a0f8ed Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:04:47 +0900 Subject: [PATCH 07/30] =?UTF-8?q?test:=20step1=20-=20join=20select=20?= =?UTF-8?q?=EB=AC=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/sql/JoinQueryBuilderTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/test/java/persistence/sql/JoinQueryBuilderTest.java diff --git a/src/test/java/persistence/sql/JoinQueryBuilderTest.java b/src/test/java/persistence/sql/JoinQueryBuilderTest.java new file mode 100644 index 00000000..db424a6b --- /dev/null +++ b/src/test/java/persistence/sql/JoinQueryBuilderTest.java @@ -0,0 +1,97 @@ +package persistence.sql; + +import database.H2; +import entity.Order; +import entity.OrderItem; +import jdbc.JdbcTemplate; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import persistence.JpaTest; +import persistence.sql.dml.CustomSelectQueryBuilder; +import pojo.EntityJoinMetaData; +import pojo.EntityMetaData; + +import java.sql.SQLException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class JoinQueryBuilderTest extends JpaTest { + + static OrderItem orderItem1 = new OrderItem(1L, "A", 1); + static OrderItem orderItem2 = new OrderItem(2L, "B", 10); + static OrderItem orderItem3 = new OrderItem(3L, "C", 5); + static Order order = new Order(1L, "test1", List.of(orderItem1, orderItem2, orderItem3)); + + @BeforeAll + static void init() throws SQLException { + server = new H2(); + server.start(); + jdbcTemplate = new JdbcTemplate(server.getConnection()); + } + + @BeforeEach + void setUp() { + createTable(); + insertData(); + } + + @AfterEach + void remove() { + dropTable(); + } + + @AfterAll + static void destroy() { + server.stop(); + } + + @DisplayName("join 시 select 문 생성") + @Test + void selectSqlWithJoinColumn() { + EntityMetaData entityMetaData = new EntityMetaData(OrderItem.class, orderItem1); + EntityJoinMetaData entityJoinMetaData = new EntityJoinMetaData(entityMetaData, Order.class, order); + + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityJoinMetaData); + String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery1(order, Order.class, 1); + System.out.println("selectJoinQuery = " + selectJoinQuery); + + String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;"; + assertThat(selectJoinQuery).isEqualTo(resultQuery); + } + + private void createTable() { + String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; + String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; + + jdbcTemplate.execute(createOrderSql); + jdbcTemplate.execute(createOrderItemSql); + } + + private void insertData() { + String insertOrderSql = "insert into orders (id, order_number) values (" + order.getId() + ", '" + order.getOrderNumber() + "');"; + String insertOrderItemSql1 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem1.getId() + ", '" + orderItem1.getProduct() + "' , " + orderItem1.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql2 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem2.getId() + ", '" + orderItem2.getProduct() + "' , " + orderItem2.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql3 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem3.getId() + ", '" + orderItem3.getProduct() + "' , " + orderItem3.getQuantity() + ", " + order.getId() + ");"; + + jdbcTemplate.execute(insertOrderSql); + jdbcTemplate.execute(insertOrderItemSql1); + jdbcTemplate.execute(insertOrderItemSql2); + jdbcTemplate.execute(insertOrderItemSql3); + } + + private void dropTable() { + String dropOrderTable = "drop table orders;"; + String dropOrderItemTable = "drop table order_items;"; + + jdbcTemplate.execute(dropOrderItemTable); + jdbcTemplate.execute(dropOrderTable); + } +} From 0047ab48a8a796ec015ac62d5434cd06a8573999 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:05:05 +0900 Subject: [PATCH 08/30] =?UTF-8?q?chore:=20step1=20-=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=9C=A0=ED=8B=B8=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EC=9D=B4=EA=B4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/utils/StringUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/utils/StringUtils.java b/src/main/java/utils/StringUtils.java index 2e6e7c34..f715ad48 100644 --- a/src/main/java/utils/StringUtils.java +++ b/src/main/java/utils/StringUtils.java @@ -9,4 +9,8 @@ private StringUtils() { public static boolean isBlankOrEmpty(String target) { return target == null || target.isBlank() || target.isEmpty(); } + + public static String joinNameAndValue(String delimiter, String name, String value) { + return String.join(delimiter, name, value); + } } From 82c0547dc75a55db4b4a9e897d2d9ec6e4a9a631 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:05:28 +0900 Subject: [PATCH 09/30] =?UTF-8?q?feat:=20step1=20-=20CustomSelectQueryBuil?= =?UTF-8?q?der=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/dml/CustomSelectQueryBuilder.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java diff --git a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java new file mode 100644 index 00000000..123e3005 --- /dev/null +++ b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java @@ -0,0 +1,72 @@ +package persistence.sql.dml; + +import pojo.EntityJoinMetaData; +import pojo.EntityMetaData; +import pojo.FieldInfos; +import pojo.IdField; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; + +import static constants.CommonConstants.COMMA; + +public class CustomSelectQueryBuilder { + + private static final String FIND_BY_ID_JOIN_QUERY = "SELECT %s FROM %s LEFT JOIN %s ON %s = %s WHERE %s = %s;"; + + private final EntityJoinMetaData entityJoinMetaData; //Order + private final EntityMetaData owner; //OrderItem + + public CustomSelectQueryBuilder(EntityJoinMetaData entityJoinMetaData) { + this.entityJoinMetaData = entityJoinMetaData; + this.owner = entityJoinMetaData.getOwner(); + } + + public String findByIdJoinQuery1(Object entity, Class clazz, Object condition) { + Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); + IdField idField = new IdField(field, entity); + + return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), + entityJoinMetaData.getEntityName(), owner.getEntityName(), + entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), + owner.getEntityName() + "." + entityJoinMetaData.joinColumnName(), + entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); + } + + /** + * 아래 방법을 고민 중이라서 한번 피드백 부탁드립니다. + */ + public String findByIdJoinQuery2(Object entity, Class clazz, Object condition) { + Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); + IdField idField = new IdField(field, entity); + + Field joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); + Class subClass = (Class) ((ParameterizedType) joinColumnField.getGenericType()).getActualTypeArguments()[0]; + EntityMetaData owner = new EntityMetaData(subClass, null); + + return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), + entityJoinMetaData.getEntityName(), owner.getEntityName(), + entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), + owner.getEntityName() + "." + entityJoinMetaData.joinColumnName(), + entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); + } + + private String getSelectData() { + String entityData = entityJoinMetaData.getEntityColumns() + .stream() + .map(entityColumn -> entityColumn.getFieldName().getName()) + .map(name -> entityJoinMetaData.getEntityName() + "." + name) + .reduce((o1, o2) -> String.join(COMMA, o1, o2)) + .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); + + String ownerEntityData = owner.getEntityColumns() + .stream() + .map(entityColumn -> entityColumn.getFieldName().getName()) + .map(name -> owner.getEntityName() + "." + name) + .reduce((o1, o2) -> String.join(COMMA, o1, o2)) + .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); + + return entityData + COMMA + ownerEntityData; + } + +} From 69ab69fbfbb8fb8a66dc1bf4b30ac050f7f9dd3e Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 00:06:14 +0900 Subject: [PATCH 10/30] =?UTF-8?q?feat:=20step1=20-=20EntityMetaData,=20Ent?= =?UTF-8?q?ityJoinMetaData=20=EC=9D=98=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityClass.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/pojo/EntityClass.java diff --git a/src/main/java/pojo/EntityClass.java b/src/main/java/pojo/EntityClass.java new file mode 100644 index 00000000..8ed33fa4 --- /dev/null +++ b/src/main/java/pojo/EntityClass.java @@ -0,0 +1,10 @@ +package pojo; + +import java.util.List; + +public interface EntityClass { + + String getEntityName(); + + List getEntityColumns(); +} From 75a7a281c50221156b950d310b8bb3dc874a6b6a Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 22:36:22 +0900 Subject: [PATCH 11/30] =?UTF-8?q?refactor:=20step1=20-=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/JoinColumnField.java | 73 ------------------------- 1 file changed, 73 deletions(-) delete mode 100644 src/main/java/pojo/JoinColumnField.java diff --git a/src/main/java/pojo/JoinColumnField.java b/src/main/java/pojo/JoinColumnField.java deleted file mode 100644 index 054940f5..00000000 --- a/src/main/java/pojo/JoinColumnField.java +++ /dev/null @@ -1,73 +0,0 @@ -package pojo; - -import jakarta.persistence.Column; - -import java.lang.reflect.Field; -import java.util.Objects; - -public class JoinColumnField implements FieldData { - - private final EntityColumn entityColumn; - - public JoinColumnField(Field field, Object entity) { - this.entityColumn = new EntityColumn(field, entity); - } - - public String getFieldLength(boolean isVarcharType) { - return Objects.nonNull(getColumnLength(isVarcharType)) ? "(" + getColumnLength(isVarcharType) + ")" : null; - } - - public String getColumnNullConstraint() { - if (!isColumnField() || entityColumn.getField().getAnnotation(Column.class).nullable()) { - return Constraints.NULL.getName(); - } - return Constraints.NOT_NULL.getName(); - } - - private String getColumnLength(boolean isVarcharType) { - if (isColumnField() && isVarcharType) { - return String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); - } - - if (isColumnField() && !isVarcharType) { - return getLengthOrDefaultValue(255); - } - - return null; - } - - private String getLengthOrDefaultValue(int defaultLengthValue) { - return entityColumn.getField().getAnnotation(Column.class).length() == defaultLengthValue ? null - : String.valueOf(entityColumn.getField().getAnnotation(Column.class).length()); - } - - @Override - public boolean isIdField() { - return false; - } - - @Override - public boolean isColumnField() { - return true; - } - - @Override - public boolean isNotTransientField() { - return true; - } - - @Override - public boolean isNullableField() { - return entityColumn.getField().getAnnotation(Column.class).nullable(); - } - - @Override - public String getFieldNameData() { - return entityColumn.getFieldName().getName(); - } - - @Override - public Object getFieldValueData() { - return entityColumn.getFieldValue().getValue(); - } -} From de82331402fe5d3d8458f69788ce9e66fd316e6b Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 20 Mar 2024 22:36:40 +0900 Subject: [PATCH 12/30] =?UTF-8?q?chore:=20step1=20-=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/persistence/context/SimplePersistenceContext.java | 3 --- src/main/java/pojo/EntityJoinMetaData.java | 5 +---- src/main/java/pojo/EntityMetaData.java | 3 --- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/persistence/context/SimplePersistenceContext.java b/src/main/java/persistence/context/SimplePersistenceContext.java index a8da017a..ba6b4176 100644 --- a/src/main/java/persistence/context/SimplePersistenceContext.java +++ b/src/main/java/persistence/context/SimplePersistenceContext.java @@ -1,7 +1,5 @@ package persistence.context; -import dialect.Dialect; -import dialect.H2Dialect; import persistence.entity.EntityCacheKey; import persistence.entity.EntitySnapshot; import pojo.FieldInfos; @@ -13,7 +11,6 @@ public class SimplePersistenceContext implements PersistenceContext { - private final Dialect dialect = new H2Dialect(); private final Map entitiesByKey = new HashMap<>(); private final Map entitySnapshotsByKey = new HashMap<>(); diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index df3a63b8..a57ade21 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -10,12 +10,9 @@ import java.util.List; import java.util.stream.Collectors; -/** - * ex) Order - */ public class EntityJoinMetaData implements EntityClass { - private final EntityMetaData owner; //orderItem + private final EntityMetaData owner; private final Class clazz; private final Object entity; private final String entityName; diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index be394ea6..601a060a 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -6,9 +6,6 @@ import java.util.List; import java.util.stream.Collectors; -/** - * ex) OrderItem - */ public class EntityMetaData implements EntityClass { private final Class clazz; From a87eb1ee01e1031b3fe9a807de95c843ae6875a8 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 22:59:09 +0900 Subject: [PATCH 13/30] =?UTF-8?q?refactor:=20step1=20-=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityClass.java | 10 -- .../persistence/sql/JoinQueryBuilderTest.java | 97 ------------------- 2 files changed, 107 deletions(-) delete mode 100644 src/main/java/pojo/EntityClass.java delete mode 100644 src/test/java/persistence/sql/JoinQueryBuilderTest.java diff --git a/src/main/java/pojo/EntityClass.java b/src/main/java/pojo/EntityClass.java deleted file mode 100644 index 8ed33fa4..00000000 --- a/src/main/java/pojo/EntityClass.java +++ /dev/null @@ -1,10 +0,0 @@ -package pojo; - -import java.util.List; - -public interface EntityClass { - - String getEntityName(); - - List getEntityColumns(); -} diff --git a/src/test/java/persistence/sql/JoinQueryBuilderTest.java b/src/test/java/persistence/sql/JoinQueryBuilderTest.java deleted file mode 100644 index db424a6b..00000000 --- a/src/test/java/persistence/sql/JoinQueryBuilderTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package persistence.sql; - -import database.H2; -import entity.Order; -import entity.OrderItem; -import jdbc.JdbcTemplate; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import persistence.JpaTest; -import persistence.sql.dml.CustomSelectQueryBuilder; -import pojo.EntityJoinMetaData; -import pojo.EntityMetaData; - -import java.sql.SQLException; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -class JoinQueryBuilderTest extends JpaTest { - - static OrderItem orderItem1 = new OrderItem(1L, "A", 1); - static OrderItem orderItem2 = new OrderItem(2L, "B", 10); - static OrderItem orderItem3 = new OrderItem(3L, "C", 5); - static Order order = new Order(1L, "test1", List.of(orderItem1, orderItem2, orderItem3)); - - @BeforeAll - static void init() throws SQLException { - server = new H2(); - server.start(); - jdbcTemplate = new JdbcTemplate(server.getConnection()); - } - - @BeforeEach - void setUp() { - createTable(); - insertData(); - } - - @AfterEach - void remove() { - dropTable(); - } - - @AfterAll - static void destroy() { - server.stop(); - } - - @DisplayName("join 시 select 문 생성") - @Test - void selectSqlWithJoinColumn() { - EntityMetaData entityMetaData = new EntityMetaData(OrderItem.class, orderItem1); - EntityJoinMetaData entityJoinMetaData = new EntityJoinMetaData(entityMetaData, Order.class, order); - - CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityJoinMetaData); - String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery1(order, Order.class, 1); - System.out.println("selectJoinQuery = " + selectJoinQuery); - - String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;"; - assertThat(selectJoinQuery).isEqualTo(resultQuery); - } - - private void createTable() { - String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; - String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; - - jdbcTemplate.execute(createOrderSql); - jdbcTemplate.execute(createOrderItemSql); - } - - private void insertData() { - String insertOrderSql = "insert into orders (id, order_number) values (" + order.getId() + ", '" + order.getOrderNumber() + "');"; - String insertOrderItemSql1 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem1.getId() + ", '" + orderItem1.getProduct() + "' , " + orderItem1.getQuantity() + ", " + order.getId() + ");"; - String insertOrderItemSql2 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem2.getId() + ", '" + orderItem2.getProduct() + "' , " + orderItem2.getQuantity() + ", " + order.getId() + ");"; - String insertOrderItemSql3 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem3.getId() + ", '" + orderItem3.getProduct() + "' , " + orderItem3.getQuantity() + ", " + order.getId() + ");"; - - jdbcTemplate.execute(insertOrderSql); - jdbcTemplate.execute(insertOrderItemSql1); - jdbcTemplate.execute(insertOrderItemSql2); - jdbcTemplate.execute(insertOrderItemSql3); - } - - private void dropTable() { - String dropOrderTable = "drop table orders;"; - String dropOrderItemTable = "drop table order_items;"; - - jdbcTemplate.execute(dropOrderItemTable); - jdbcTemplate.execute(dropOrderTable); - } -} From b42326339a37d0fa1ba5d4f3c6e4d08015947144 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:01:52 +0900 Subject: [PATCH 14/30] =?UTF-8?q?refactor:=20step1=20-=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EC=97=90=20=EC=B1=85=EC=9E=84=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=EC=9E=84=ED=95=98=EB=8A=94=20=EB=B0=A9=EB=B2=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/persistence/entity/CustomJpaRepository.java | 4 ++-- src/main/java/pojo/IdField.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/persistence/entity/CustomJpaRepository.java b/src/main/java/persistence/entity/CustomJpaRepository.java index 16408ecf..c63ba430 100644 --- a/src/main/java/persistence/entity/CustomJpaRepository.java +++ b/src/main/java/persistence/entity/CustomJpaRepository.java @@ -28,8 +28,8 @@ private boolean isNew(T entity) { try { Field field = new FieldInfos(entity.getClass().getDeclaredFields()).getIdField(); IdField idField = new IdField(field, entity); - return idField.getFieldInfoTemp().getFieldValue() == null - || isBlankOrEmpty(idField.getFieldInfoTemp().getFieldValue().getValue()); + return idField.getEntityColumn().getFieldValue() == null + || isBlankOrEmpty(idField.getEntityColumn().getFieldValue().getValue()); } catch (IllegalArgumentException e) { return false; } diff --git a/src/main/java/pojo/IdField.java b/src/main/java/pojo/IdField.java index 79ad9547..7c0b318c 100644 --- a/src/main/java/pojo/IdField.java +++ b/src/main/java/pojo/IdField.java @@ -15,7 +15,7 @@ public IdField(Field field, Object entity) { this.generationType = getGenerationType(); } - public EntityColumn getFieldInfoTemp() { + public EntityColumn getEntityColumn() { return entityColumn; } @@ -29,7 +29,7 @@ public boolean isGenerationTypeAutoOrIdentity() { } private H2GenerationType getGenerationType() { - if (entityColumn.getField().isAnnotationPresent(GeneratedValue.class)) { + if (entityColumn.hasGenerationType()) { return H2GenerationType.from(entityColumn.getField().getAnnotation(GeneratedValue.class).strategy()); } return null; From 0a3c3a1ef96aa735969f0174f67c65ec578e7631 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:02:13 +0900 Subject: [PATCH 15/30] =?UTF-8?q?refactor:=20step1=20-=20GenerationType=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityColumn.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/pojo/EntityColumn.java b/src/main/java/pojo/EntityColumn.java index df99568b..a3bf9dfc 100644 --- a/src/main/java/pojo/EntityColumn.java +++ b/src/main/java/pojo/EntityColumn.java @@ -1,5 +1,7 @@ package pojo; +import jakarta.persistence.GeneratedValue; + import java.lang.reflect.Field; import java.util.Objects; @@ -29,4 +31,8 @@ public FieldName getFieldName() { public FieldValue getFieldValue() { return fieldValue; } + + public boolean hasGenerationType() { + return field.isAnnotationPresent(GeneratedValue.class); + } } From 431489099258053de4723c052f8fb99d94d66b2d Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:02:53 +0900 Subject: [PATCH 16/30] =?UTF-8?q?refactor:=20step1=20-=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=82=B4=EC=9A=A9=EC=9D=84=20=EC=B0=B8=EA=B3=A0?= =?UTF-8?q?=ED=95=98=EC=97=AC=20EntityMetaData,=20EntityJoinMetaData=20?= =?UTF-8?q?=EC=97=AD=ED=95=A0=EC=9D=84=20=EC=9E=AC=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityJoinMetaData.java | 49 ++++++++++------------ src/main/java/pojo/EntityMetaData.java | 30 ++++++++++--- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index a57ade21..cb18f104 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -10,61 +10,58 @@ import java.util.List; import java.util.stream.Collectors; -public class EntityJoinMetaData implements EntityClass { +import static utils.StringUtils.isBlankOrEmpty; + +public class EntityJoinMetaData { - private final EntityMetaData owner; private final Class clazz; private final Object entity; private final String entityName; - private final List entityColumns; + private final String joinColumnName; + private final List fieldNames; private final boolean lazy; - public EntityJoinMetaData(EntityMetaData owner, Class clazz, Object entity) { + public EntityJoinMetaData(Class clazz, Object entity, Field field) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } - this.owner = owner; this.clazz = clazz; this.entity = entity; this.entityName = getEntityNameInfo(); - this.entityColumns = getEntityColumnsInfo(); - this.lazy = isLazy(); - } - - public EntityMetaData getOwner() { - return owner; + this.joinColumnName = getJoinColumnNameInfo(field); + this.fieldNames = getFieldNamesInfo(); + this.lazy = isLazy(field); } - @Override public String getEntityName() { return entityName; } - @Override - public List getEntityColumns() { - return entityColumns; + public String getJoinColumnName() { + return joinColumnName; } - public String joinColumnName() { - return new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField().getAnnotation(JoinColumn.class).name(); + public List getFieldNames() { + return fieldNames; } private String getEntityNameInfo() { - return clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() - : clazz.getSimpleName().toLowerCase(); + return clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name()) + ? clazz.getAnnotation(Table.class).name() : clazz.getSimpleName().toLowerCase(); } - private List getEntityColumnsInfo() { + public String getJoinColumnNameInfo(Field field) { + return field.getAnnotation(JoinColumn.class).name(); + } + + private List getFieldNamesInfo() { return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() - .map(field -> new EntityColumn(field, entity)) + .map(FieldName::new) .collect(Collectors.toList()); } - private boolean isLazy() { - Field joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); - + private boolean isLazy(Field field) { //일단 OneToMany 만 고려 - FetchType fetchType = joinColumnField.getAnnotation(OneToMany.class).fetch(); - return !fetchType.equals(FetchType.EAGER); + return !field.getAnnotation(OneToMany.class).fetch().equals(FetchType.EAGER); } } diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 601a060a..1406501b 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -3,15 +3,22 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; -public class EntityMetaData implements EntityClass { +import static utils.StringUtils.isBlankOrEmpty; + +//Order +public class EntityMetaData { private final Class clazz; private final Object entity; private final String entityName; private final List entityColumns; + private final EntityJoinMetaData entityJoinMetaData; //OrderItem public EntityMetaData(Class clazz, Object entity) { if (!clazz.isAnnotationPresent(Entity.class)) { @@ -21,21 +28,24 @@ public EntityMetaData(Class clazz, Object entity) { this.entity = entity; this.entityName = getEntityNameInfo(); this.entityColumns = getEntityColumnsInfo(); + this.entityJoinMetaData = getEntityJoinMetaDataInfo(); } - @Override public String getEntityName() { return entityName; } - @Override public List getEntityColumns() { return entityColumns; } + public EntityJoinMetaData getEntityJoinMetaData() { + return entityJoinMetaData; + } + private String getEntityNameInfo() { - return clazz.isAnnotationPresent(Table.class) ? clazz.getAnnotation(Table.class).name() - : clazz.getSimpleName().toLowerCase(); + return clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name()) + ? clazz.getAnnotation(Table.class).name() : clazz.getSimpleName().toLowerCase(); } private List getEntityColumnsInfo() { @@ -43,4 +53,14 @@ private List getEntityColumnsInfo() { .map(field -> new EntityColumn(field, entity)) .collect(Collectors.toList()); } + + private EntityJoinMetaData getEntityJoinMetaDataInfo() { + Optional joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); + if (joinColumnField.isEmpty()) { + return null; + } + + Class joinClass = (Class) ((ParameterizedType) joinColumnField.get().getGenericType()).getActualTypeArguments()[0]; + return new EntityJoinMetaData(joinClass, null, joinColumnField.get()); + } } From 32f34f28d7c6d7f2a0be6923d4b2b5998aaa6cc1 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:04:45 +0900 Subject: [PATCH 17/30] =?UTF-8?q?refactor:=20step1=20-=20isTransient=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B2=B4=ED=81=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=9D=84=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=8B=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/FieldInfos.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/pojo/FieldInfos.java b/src/main/java/pojo/FieldInfos.java index cca53833..ab45b529 100644 --- a/src/main/java/pojo/FieldInfos.java +++ b/src/main/java/pojo/FieldInfos.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; public class FieldInfos { @@ -19,7 +20,7 @@ public FieldInfos(Field[] fields) { if (Objects.isNull(fields)) { throw new IllegalArgumentException("fields 가 null 이어서는 안됩니다."); } - this.fieldList = Arrays.stream(fields).collect(Collectors.toList()); + this.fieldList = Arrays.stream(fields).filter(field -> !isTransientField(field)).collect(Collectors.toList()); } public List getFieldDataList() { @@ -35,20 +36,20 @@ public Field getIdField() { public List getColumnFields() { return fieldList.stream() - .filter(field -> !isIdField(field) && !isTransientField(field) && !isJoinColumnField(field)) + .filter(field -> !isIdField(field) && !isJoinColumnField(field)) .collect(Collectors.toCollection(LinkedList::new)); } public List getIdAndColumnFields() { return fieldList.stream() - .filter(field -> !isTransientField(field) && !isJoinColumnField(field)) + .filter(field -> !isJoinColumnField(field)) .collect(Collectors.toCollection(LinkedList::new)); } - public Field getJoinColumnField() { + public Optional getJoinColumnField() { return fieldList.stream() .filter(this::isJoinColumnField) - .findFirst().orElse(null); + .findFirst(); } private boolean isIdField(Field field) { From 0e8b45e825db404106de176d1af4bc0625e6f728 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:05:45 +0900 Subject: [PATCH 18/30] =?UTF-8?q?refactor:=20step1=20-=20EntityMetaData=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=20=EC=8B=9C=20EntityJoinMetaData=20?= =?UTF-8?q?=EB=8F=84=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B0=8F=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EC=A7=84=ED=96=89=ED=95=98=EA=B3=A0,=20?= =?UTF-8?q?=EC=9D=B4=EC=97=90=20=EB=94=B0=EB=9D=BC=20CustomSelectQueryBuil?= =?UTF-8?q?der=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/dml/CustomSelectQueryBuilder.java | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java index 123e3005..63d2854a 100644 --- a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java @@ -3,10 +3,10 @@ import pojo.EntityJoinMetaData; import pojo.EntityMetaData; import pojo.FieldInfos; +import pojo.FieldName; import pojo.IdField; import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; import static constants.CommonConstants.COMMA; @@ -14,55 +14,37 @@ public class CustomSelectQueryBuilder { private static final String FIND_BY_ID_JOIN_QUERY = "SELECT %s FROM %s LEFT JOIN %s ON %s = %s WHERE %s = %s;"; - private final EntityJoinMetaData entityJoinMetaData; //Order - private final EntityMetaData owner; //OrderItem + private final EntityMetaData entityMetaData; + private final EntityJoinMetaData entityJoinMetaData; - public CustomSelectQueryBuilder(EntityJoinMetaData entityJoinMetaData) { - this.entityJoinMetaData = entityJoinMetaData; - this.owner = entityJoinMetaData.getOwner(); + public CustomSelectQueryBuilder(EntityMetaData entityMetaData) { + this.entityMetaData = entityMetaData; + this.entityJoinMetaData = entityMetaData.getEntityJoinMetaData(); } - public String findByIdJoinQuery1(Object entity, Class clazz, Object condition) { + public String findByIdJoinQuery(Object entity, Class clazz, Object condition) { Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); IdField idField = new IdField(field, entity); return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), - entityJoinMetaData.getEntityName(), owner.getEntityName(), - entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), - owner.getEntityName() + "." + entityJoinMetaData.joinColumnName(), - entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); - } - - /** - * 아래 방법을 고민 중이라서 한번 피드백 부탁드립니다. - */ - public String findByIdJoinQuery2(Object entity, Class clazz, Object condition) { - Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); - IdField idField = new IdField(field, entity); - - Field joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); - Class subClass = (Class) ((ParameterizedType) joinColumnField.getGenericType()).getActualTypeArguments()[0]; - EntityMetaData owner = new EntityMetaData(subClass, null); - - return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), - entityJoinMetaData.getEntityName(), owner.getEntityName(), - entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), - owner.getEntityName() + "." + entityJoinMetaData.joinColumnName(), - entityJoinMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); + entityMetaData.getEntityName(), entityJoinMetaData.getEntityName(), + entityMetaData.getEntityName() + "." + idField.getFieldNameData(), + entityJoinMetaData.getEntityName() + "." + entityJoinMetaData.getJoinColumnName(), + entityMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); } private String getSelectData() { - String entityData = entityJoinMetaData.getEntityColumns() + String entityData = entityMetaData.getEntityColumns() .stream() .map(entityColumn -> entityColumn.getFieldName().getName()) - .map(name -> entityJoinMetaData.getEntityName() + "." + name) + .map(name -> entityMetaData.getEntityName() + "." + name) .reduce((o1, o2) -> String.join(COMMA, o1, o2)) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); - String ownerEntityData = owner.getEntityColumns() + String ownerEntityData = entityJoinMetaData.getFieldNames() .stream() - .map(entityColumn -> entityColumn.getFieldName().getName()) - .map(name -> owner.getEntityName() + "." + name) + .map(FieldName::getName) + .map(name -> entityJoinMetaData.getEntityName() + "." + name) .reduce((o1, o2) -> String.join(COMMA, o1, o2)) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); From ab58ef362cc0b7ee930b4edea1e181038a454843 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:06:21 +0900 Subject: [PATCH 19/30] =?UTF-8?q?test:=20step1=20-=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EC=83=81=ED=99=94=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B0=9D=EC=B2=B4=20=EC=B4=88=EA=B8=B0=ED=99=94=20?= =?UTF-8?q?=EB=B0=A9=EB=B2=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/persistence/JpaTest.java | 17 +++- .../context/SimplePersistenceContextTest.java | 11 +-- .../entity/CustomJpaRepositoryTest.java | 9 +- .../entity/EntityLoaderImplTest.java | 8 +- .../entity/EntityPersisterImplTest.java | 10 +- .../sql/CustomSelectQueryBuilderTest.java | 94 +++++++++++++++++++ 6 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java diff --git a/src/test/java/persistence/JpaTest.java b/src/test/java/persistence/JpaTest.java index dd1e4980..a156a410 100644 --- a/src/test/java/persistence/JpaTest.java +++ b/src/test/java/persistence/JpaTest.java @@ -6,23 +6,36 @@ import jdbc.JdbcTemplate; import persistence.context.PersistenceContext; import persistence.context.SimplePersistenceContext; +import persistence.entity.CustomJpaRepository; import persistence.entity.EntityLoader; +import persistence.entity.EntityLoaderImpl; import persistence.entity.EntityPersister; +import persistence.entity.EntityPersisterImpl; import persistence.entity.JpaRepository; import persistence.entity.SimpleEntityEntry; import persistence.entity.SimpleEntityManager; import pojo.EntityMetaData; +import pojo.EntityStatus; public abstract class JpaTest { protected static Dialect dialect = new H2Dialect(); - protected static EntityMetaData entityMetaData; protected static DatabaseServer server; protected static JdbcTemplate jdbcTemplate; protected static EntityPersister entityPersister; protected static EntityLoader entityLoader; protected static SimpleEntityManager simpleEntityManager; - protected static PersistenceContext persistenceContext = new SimplePersistenceContext(); + protected static PersistenceContext persistenceContext; protected static SimpleEntityEntry entityEntry; protected static JpaRepository jpaRepository; + + protected static void initForTest(EntityMetaData entityMetaData) { + entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); + entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); + entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); + + persistenceContext = new SimplePersistenceContext(); + simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); + jpaRepository = new CustomJpaRepository(simpleEntityManager); + } } diff --git a/src/test/java/persistence/context/SimplePersistenceContextTest.java b/src/test/java/persistence/context/SimplePersistenceContextTest.java index 0b716a82..e4727847 100644 --- a/src/test/java/persistence/context/SimplePersistenceContextTest.java +++ b/src/test/java/persistence/context/SimplePersistenceContextTest.java @@ -10,11 +10,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import persistence.JpaTest; -import persistence.entity.EntityLoaderImpl; -import persistence.entity.EntityPersisterImpl; import persistence.entity.EntitySnapshot; -import persistence.entity.SimpleEntityEntry; -import persistence.entity.SimpleEntityManager; import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import pojo.EntityMetaData; @@ -32,6 +28,7 @@ class SimplePersistenceContextTest extends JpaTest { static Person3 person = new Person3(1L, "test", 20, "test@test.com"); + static EntityMetaData entityMetaData; @BeforeAll static void init() throws SQLException { @@ -40,11 +37,7 @@ static void init() throws SQLException { jdbcTemplate = new JdbcTemplate(server.getConnection()); entityMetaData = new EntityMetaData(Person3.class, person); - entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); - entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); - - simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); + initForTest(entityMetaData); } @BeforeEach diff --git a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java index 6363ac98..233ec83e 100644 --- a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java +++ b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java @@ -13,7 +13,6 @@ import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import pojo.EntityMetaData; -import pojo.EntityStatus; import java.sql.SQLException; import java.util.Map; @@ -23,6 +22,7 @@ class CustomJpaRepositoryTest extends JpaTest { static Person3 person = new Person3(1L, "test", 20, "test@test.com"); + static EntityMetaData entityMetaData; @BeforeAll static void init() throws SQLException { @@ -31,12 +31,7 @@ static void init() throws SQLException { jdbcTemplate = new JdbcTemplate(server.getConnection()); entityMetaData = new EntityMetaData(Person3.class, person); - entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); - entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); - - simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); - jpaRepository = new CustomJpaRepository(simpleEntityManager); + initForTest(entityMetaData); } @BeforeEach diff --git a/src/test/java/persistence/entity/EntityLoaderImplTest.java b/src/test/java/persistence/entity/EntityLoaderImplTest.java index 4ca321af..a5cbe501 100644 --- a/src/test/java/persistence/entity/EntityLoaderImplTest.java +++ b/src/test/java/persistence/entity/EntityLoaderImplTest.java @@ -13,7 +13,6 @@ import persistence.sql.ddl.CreateQueryBuilder; import persistence.sql.ddl.DropQueryBuilder; import pojo.EntityMetaData; -import pojo.EntityStatus; import java.sql.SQLException; @@ -23,6 +22,7 @@ class EntityLoaderImplTest extends JpaTest { static Person3 person = new Person3(1L, "test", 20, "test@test.com"); + static EntityMetaData entityMetaData; @BeforeAll static void init() throws SQLException { @@ -31,11 +31,7 @@ static void init() throws SQLException { jdbcTemplate = new JdbcTemplate(server.getConnection()); entityMetaData = new EntityMetaData(Person3.class, person); - entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); - entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); - - simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); + initForTest(entityMetaData); } @BeforeEach diff --git a/src/test/java/persistence/entity/EntityPersisterImplTest.java b/src/test/java/persistence/entity/EntityPersisterImplTest.java index c2efedfd..154c84c8 100644 --- a/src/test/java/persistence/entity/EntityPersisterImplTest.java +++ b/src/test/java/persistence/entity/EntityPersisterImplTest.java @@ -14,7 +14,6 @@ import persistence.sql.ddl.DropQueryBuilder; import persistence.sql.dml.UpdateQueryBuilder; import pojo.EntityMetaData; -import pojo.EntityStatus; import java.sql.SQLException; @@ -25,6 +24,7 @@ class EntityPersisterImplTest extends JpaTest { static Person3 person = new Person3(1L, "test", 20, "test@test.com"); + static EntityMetaData entityMetaData; @BeforeAll static void init() throws SQLException { @@ -33,11 +33,7 @@ static void init() throws SQLException { jdbcTemplate = new JdbcTemplate(server.getConnection()); entityMetaData = new EntityMetaData(Person3.class, person); - entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); - entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); - entityEntry = new SimpleEntityEntry(EntityStatus.LOADING); - - simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); + initForTest(entityMetaData); } @BeforeEach @@ -58,8 +54,6 @@ static void destroy() { @DisplayName("insert 테스트") @Test void insertTest() { - dropTable(); - createTable(); entityPersister.insert(person); Person3 person3 = simpleEntityManager.find(person, person.getClass(), person.getId()); diff --git a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java new file mode 100644 index 00000000..ef93eef5 --- /dev/null +++ b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java @@ -0,0 +1,94 @@ +package persistence.sql; + +import database.H2; +import entity.Order; +import entity.OrderItem; +import jdbc.JdbcTemplate; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import persistence.JpaTest; +import persistence.sql.dml.CustomSelectQueryBuilder; +import pojo.EntityMetaData; + +import java.sql.SQLException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class CustomSelectQueryBuilderTest extends JpaTest { + + static OrderItem orderItem1 = new OrderItem(1L, "A", 1); + static OrderItem orderItem2 = new OrderItem(2L, "B", 10); + static OrderItem orderItem3 = new OrderItem(3L, "C", 5); + static Order order = new Order(1L, "test1", List.of(orderItem1, orderItem2, orderItem3)); + + @BeforeAll + static void init() throws SQLException { + server = new H2(); + server.start(); + jdbcTemplate = new JdbcTemplate(server.getConnection()); + } + + @BeforeEach + void setUp() { + createTable(); + insertData(); + } + + @AfterEach + void remove() { + dropTable(); + } + + @AfterAll + static void destroy() { + server.stop(); + } + + @DisplayName("OneToMany 를 갖고 있는 Entity 클래스의 select 쿼리는 join 문 포함 필요") + @Test + void selectSqlWithJoinColumn() { + EntityMetaData entityMetaData = new EntityMetaData(Order.class, order); + + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); + String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery(order, Order.class, 1); + + String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;"; + assertThat(selectJoinQuery).isEqualTo(resultQuery); + } + + private void createTable() { + String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; + String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; + + jdbcTemplate.execute(createOrderSql); + jdbcTemplate.execute(createOrderItemSql); + } + + private void insertData() { + String insertOrderSql = "insert into orders (id, order_number) values (" + order.getId() + ", '" + order.getOrderNumber() + "');"; + String insertOrderItemSql1 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem1.getId() + ", '" + orderItem1.getProduct() + "' , " + orderItem1.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql2 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem2.getId() + ", '" + orderItem2.getProduct() + "' , " + orderItem2.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql3 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem3.getId() + ", '" + orderItem3.getProduct() + "' , " + orderItem3.getQuantity() + ", " + order.getId() + ");"; + + jdbcTemplate.execute(insertOrderSql); + jdbcTemplate.execute(insertOrderItemSql1); + jdbcTemplate.execute(insertOrderItemSql2); + jdbcTemplate.execute(insertOrderItemSql3); + } + + private void dropTable() { + String dropOrderTable = "drop table orders;"; + String dropOrderItemTable = "drop table order_items;"; + + jdbcTemplate.execute(dropOrderItemTable); + jdbcTemplate.execute(dropOrderTable); + } +} From a1290061db10b4b55b09e7e4f704d94818244ea6 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Thu, 21 Mar 2024 23:06:36 +0900 Subject: [PATCH 20/30] =?UTF-8?q?chore:=20step1=20-=20inline=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/persistence/entity/EntityEntry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/persistence/entity/EntityEntry.java b/src/main/java/persistence/entity/EntityEntry.java index c7a2ecae..68e42dcc 100644 --- a/src/main/java/persistence/entity/EntityEntry.java +++ b/src/main/java/persistence/entity/EntityEntry.java @@ -19,5 +19,6 @@ public interface EntityEntry { void postRemove(); void preReadOnly(); + void postReadOnly(); } From 9f25689d632827f5030e83d60dff8a70d222185d Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 25 Mar 2024 22:55:23 +0900 Subject: [PATCH 21/30] =?UTF-8?q?refactor:=20step1=20-=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=83=81=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/constants/CommonConstants.java | 2 ++ .../sql/dml/CustomSelectQueryBuilder.java | 19 +++++++++++-------- src/main/java/pojo/EntityJoinMetaData.java | 14 ++++++++++---- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/constants/CommonConstants.java b/src/main/java/constants/CommonConstants.java index ee6ffc6d..2d74188f 100644 --- a/src/main/java/constants/CommonConstants.java +++ b/src/main/java/constants/CommonConstants.java @@ -9,4 +9,6 @@ private CommonConstants() { public static final String COMMA = ", "; public static final String EQUAL = " = "; public static final String AND = " and "; + public static final String PERIOD = "."; + public static final String UNDER_SCORE = "_"; } diff --git a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java index 63d2854a..cf04d98d 100644 --- a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java @@ -9,6 +9,7 @@ import java.lang.reflect.Field; import static constants.CommonConstants.COMMA; +import static constants.CommonConstants.PERIOD; public class CustomSelectQueryBuilder { @@ -22,29 +23,31 @@ public CustomSelectQueryBuilder(EntityMetaData entityMetaData) { this.entityJoinMetaData = entityMetaData.getEntityJoinMetaData(); } - public String findByIdJoinQuery(Object entity, Class clazz, Object condition) { + public String findByIdJoinQuery(Object entity, Class clazz) { Field field = new FieldInfos(clazz.getDeclaredFields()).getIdField(); IdField idField = new IdField(field, entity); - return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), - entityMetaData.getEntityName(), entityJoinMetaData.getEntityName(), - entityMetaData.getEntityName() + "." + idField.getFieldNameData(), - entityJoinMetaData.getEntityName() + "." + entityJoinMetaData.getJoinColumnName(), - entityMetaData.getEntityName() + "." + idField.getFieldNameData(), condition); + String metaDataEntityName = entityMetaData.getEntityName(); + String joinMetaDataEntityName = entityJoinMetaData.getEntityName(); + + return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), metaDataEntityName, joinMetaDataEntityName, + metaDataEntityName + PERIOD + idField.getFieldNameData(), + joinMetaDataEntityName + PERIOD + entityJoinMetaData.getJoinColumnName(), + metaDataEntityName + PERIOD + idField.getFieldNameData(), idField.getFieldValueData()); } private String getSelectData() { String entityData = entityMetaData.getEntityColumns() .stream() .map(entityColumn -> entityColumn.getFieldName().getName()) - .map(name -> entityMetaData.getEntityName() + "." + name) + .map(name -> entityMetaData.getEntityName() + PERIOD + name) .reduce((o1, o2) -> String.join(COMMA, o1, o2)) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); String ownerEntityData = entityJoinMetaData.getFieldNames() .stream() .map(FieldName::getName) - .map(name -> entityJoinMetaData.getEntityName() + "." + name) + .map(name -> entityJoinMetaData.getEntityName() + PERIOD + name) .reduce((o1, o2) -> String.join(COMMA, o1, o2)) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index cb18f104..bd5b9aae 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.stream.Collectors; +import static constants.CommonConstants.UNDER_SCORE; import static utils.StringUtils.isBlankOrEmpty; public class EntityJoinMetaData { @@ -21,14 +22,14 @@ public class EntityJoinMetaData { private final List fieldNames; private final boolean lazy; - public EntityJoinMetaData(Class clazz, Object entity, Field field) { + public EntityJoinMetaData(Class clazz, Object entity, Field field, IdField entityMetaDataIdField) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } this.clazz = clazz; this.entity = entity; this.entityName = getEntityNameInfo(); - this.joinColumnName = getJoinColumnNameInfo(field); + this.joinColumnName = getJoinColumnNameInfo(field, entityMetaDataIdField); this.fieldNames = getFieldNamesInfo(); this.lazy = isLazy(field); } @@ -45,13 +46,18 @@ public List getFieldNames() { return fieldNames; } + public boolean isLazy() { + return lazy; + } + private String getEntityNameInfo() { return clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name()) ? clazz.getAnnotation(Table.class).name() : clazz.getSimpleName().toLowerCase(); } - public String getJoinColumnNameInfo(Field field) { - return field.getAnnotation(JoinColumn.class).name(); + public String getJoinColumnNameInfo(Field field, IdField entityMetaDataIdField) { + return field.isAnnotationPresent(JoinColumn.class) && !isBlankOrEmpty(field.getAnnotation(JoinColumn.class).name()) + ? field.getAnnotation(JoinColumn.class).name() : getEntityNameInfo() + UNDER_SCORE + entityMetaDataIdField.getFieldNameData(); } private List getFieldNamesInfo() { From 6b169ca9932404cbdd1eea078ded0a67cc949d60 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 25 Mar 2024 22:55:54 +0900 Subject: [PATCH 22/30] =?UTF-8?q?refactor:=20step1=20-=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityColumn.java | 8 ++++++-- src/main/java/pojo/EntityMetaData.java | 9 +++++++-- src/main/java/pojo/IdField.java | 7 +------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/pojo/EntityColumn.java b/src/main/java/pojo/EntityColumn.java index a3bf9dfc..61ecfb43 100644 --- a/src/main/java/pojo/EntityColumn.java +++ b/src/main/java/pojo/EntityColumn.java @@ -1,6 +1,7 @@ package pojo; import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import java.lang.reflect.Field; import java.util.Objects; @@ -32,7 +33,10 @@ public FieldValue getFieldValue() { return fieldValue; } - public boolean hasGenerationType() { - return field.isAnnotationPresent(GeneratedValue.class); + public H2GenerationType getGenerationType() { + if (field.isAnnotationPresent(GeneratedValue.class)) { + return H2GenerationType.from(field.getAnnotation(GeneratedValue.class).strategy()); + } + return null; } } diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 1406501b..2d204ac7 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -55,12 +55,17 @@ private List getEntityColumnsInfo() { } private EntityJoinMetaData getEntityJoinMetaDataInfo() { - Optional joinColumnField = new FieldInfos(clazz.getDeclaredFields()).getJoinColumnField(); + FieldInfos fieldInfos = new FieldInfos(clazz.getDeclaredFields()); + + Optional joinColumnField = fieldInfos.getJoinColumnField(); if (joinColumnField.isEmpty()) { return null; } + Field field = fieldInfos.getIdField(); + IdField idField = new IdField(field, entity); + Class joinClass = (Class) ((ParameterizedType) joinColumnField.get().getGenericType()).getActualTypeArguments()[0]; - return new EntityJoinMetaData(joinClass, null, joinColumnField.get()); + return new EntityJoinMetaData(joinClass, null, joinColumnField.get(), idField); } } diff --git a/src/main/java/pojo/IdField.java b/src/main/java/pojo/IdField.java index 7c0b318c..f066c9f6 100644 --- a/src/main/java/pojo/IdField.java +++ b/src/main/java/pojo/IdField.java @@ -1,7 +1,5 @@ package pojo; -import jakarta.persistence.GeneratedValue; - import java.lang.reflect.Field; import java.util.Objects; @@ -29,10 +27,7 @@ public boolean isGenerationTypeAutoOrIdentity() { } private H2GenerationType getGenerationType() { - if (entityColumn.hasGenerationType()) { - return H2GenerationType.from(entityColumn.getField().getAnnotation(GeneratedValue.class).strategy()); - } - return null; + return entityColumn.getGenerationType(); } @Override From f29d118bbef26f8e76d7c2822eb33a2024385851 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 25 Mar 2024 22:56:18 +0900 Subject: [PATCH 23/30] =?UTF-8?q?feat:=20step1=20-=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=202=EB=B2=88=EC=9D=84=20=EC=9C=84=ED=95=B4?= =?UTF-8?q?=20loader=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/persistence/entity/EntityLoader.java | 2 ++ .../persistence/entity/EntityLoaderImpl.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/main/java/persistence/entity/EntityLoader.java b/src/main/java/persistence/entity/EntityLoader.java index 139b1ac6..694d4a0b 100644 --- a/src/main/java/persistence/entity/EntityLoader.java +++ b/src/main/java/persistence/entity/EntityLoader.java @@ -7,4 +7,6 @@ public interface EntityLoader { T findById(Class clazz, Object entity, Object condition); List findAll(Class clazz); + + List findByIdWithAssociation(Class clazz, Object entity, Object condition); } diff --git a/src/main/java/persistence/entity/EntityLoaderImpl.java b/src/main/java/persistence/entity/EntityLoaderImpl.java index 933f35e5..cd0a1035 100644 --- a/src/main/java/persistence/entity/EntityLoaderImpl.java +++ b/src/main/java/persistence/entity/EntityLoaderImpl.java @@ -2,7 +2,9 @@ import jdbc.JdbcTemplate; import jdbc.RowMapperImpl; +import persistence.sql.dml.CustomSelectQueryBuilder; import persistence.sql.dml.SelectQueryBuilder; +import pojo.EntityJoinMetaData; import pojo.EntityMetaData; import java.util.List; @@ -27,4 +29,24 @@ public List findAll(Class clazz) { SelectQueryBuilder selectQueryBuilder = new SelectQueryBuilder(entityMetaData); return jdbcTemplate.query(selectQueryBuilder.findAllQuery(), new RowMapperImpl<>(clazz)); } + + //연관관계가 있는 경우 & eager 타입만 고려 + @Override + public List findByIdWithAssociation(Class clazz, Object entity, Object condition) { + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); + + EntityJoinMetaData entityJoinMetaData = entityMetaData.getEntityJoinMetaData(); + if (!entityJoinMetaData.isLazy()) { + return eagerTypeQuery(customSelectQueryBuilder, clazz, entity); + } + + //TODO 추후 lazy 타입 넣을 예정 + return jdbcTemplate.query(customSelectQueryBuilder.findByIdJoinQuery(entity, clazz), new RowMapperImpl<>(clazz)); + + } + + private List eagerTypeQuery(CustomSelectQueryBuilder customSelectQueryBuilder, Class clazz, Object entity) { + return jdbcTemplate.query(customSelectQueryBuilder.findByIdJoinQuery(entity, clazz), new RowMapperImpl<>(clazz)); + + } } From 3334c99e3b9723b73be08e48f6e7dbef43c7477a Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 25 Mar 2024 22:56:58 +0900 Subject: [PATCH 24/30] =?UTF-8?q?test:=20step1=20-=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20static=20=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20join=20=EC=97=94=ED=8B=B0=ED=8B=B0=ED=99=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/persistence/JpaTest.java | 11 +++++++++++ .../context/SimplePersistenceContextTest.java | 1 - .../entity/CustomJpaRepositoryTest.java | 1 - .../entity/EntityLoaderImplTest.java | 3 +-- .../entity/EntityPersisterImplTest.java | 1 - .../sql/CustomSelectQueryBuilderTest.java | 18 ++++++++++++------ 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/test/java/persistence/JpaTest.java b/src/test/java/persistence/JpaTest.java index a156a410..860c69da 100644 --- a/src/test/java/persistence/JpaTest.java +++ b/src/test/java/persistence/JpaTest.java @@ -3,6 +3,9 @@ import database.DatabaseServer; import dialect.Dialect; import dialect.H2Dialect; +import entity.Order; +import entity.OrderItem; +import entity.Person3; import jdbc.JdbcTemplate; import persistence.context.PersistenceContext; import persistence.context.SimplePersistenceContext; @@ -17,6 +20,8 @@ import pojo.EntityMetaData; import pojo.EntityStatus; +import java.util.List; + public abstract class JpaTest { protected static Dialect dialect = new H2Dialect(); @@ -29,6 +34,12 @@ public abstract class JpaTest { protected static SimpleEntityEntry entityEntry; protected static JpaRepository jpaRepository; + protected static Person3 person = new Person3(1L, "test", 20, "test@test.com"); + protected static OrderItem orderItem1 = new OrderItem(1L, "A", 1); + protected static OrderItem orderItem2 = new OrderItem(2L, "B", 10); + protected static OrderItem orderItem3 = new OrderItem(3L, "C", 5); + protected static Order order = new Order(1L, "test1", List.of(orderItem1, orderItem2, orderItem3)); + protected static void initForTest(EntityMetaData entityMetaData) { entityPersister = new EntityPersisterImpl(jdbcTemplate, entityMetaData); entityLoader = new EntityLoaderImpl(jdbcTemplate, entityMetaData); diff --git a/src/test/java/persistence/context/SimplePersistenceContextTest.java b/src/test/java/persistence/context/SimplePersistenceContextTest.java index e4727847..fb6db31e 100644 --- a/src/test/java/persistence/context/SimplePersistenceContextTest.java +++ b/src/test/java/persistence/context/SimplePersistenceContextTest.java @@ -27,7 +27,6 @@ class SimplePersistenceContextTest extends JpaTest { - static Person3 person = new Person3(1L, "test", 20, "test@test.com"); static EntityMetaData entityMetaData; @BeforeAll diff --git a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java index 233ec83e..2cfc9065 100644 --- a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java +++ b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java @@ -21,7 +21,6 @@ class CustomJpaRepositoryTest extends JpaTest { - static Person3 person = new Person3(1L, "test", 20, "test@test.com"); static EntityMetaData entityMetaData; @BeforeAll diff --git a/src/test/java/persistence/entity/EntityLoaderImplTest.java b/src/test/java/persistence/entity/EntityLoaderImplTest.java index a5cbe501..6a56c27c 100644 --- a/src/test/java/persistence/entity/EntityLoaderImplTest.java +++ b/src/test/java/persistence/entity/EntityLoaderImplTest.java @@ -21,7 +21,6 @@ class EntityLoaderImplTest extends JpaTest { - static Person3 person = new Person3(1L, "test", 20, "test@test.com"); static EntityMetaData entityMetaData; @BeforeAll @@ -49,7 +48,7 @@ static void destroy() { server.stop(); } - @DisplayName("findById 테스트") + @DisplayName("findById 테스트 - 연관관계가 없는 경우") @Test void findByIdTest() { entityPersister.insert(person); diff --git a/src/test/java/persistence/entity/EntityPersisterImplTest.java b/src/test/java/persistence/entity/EntityPersisterImplTest.java index 154c84c8..b8fec2f0 100644 --- a/src/test/java/persistence/entity/EntityPersisterImplTest.java +++ b/src/test/java/persistence/entity/EntityPersisterImplTest.java @@ -23,7 +23,6 @@ class EntityPersisterImplTest extends JpaTest { - static Person3 person = new Person3(1L, "test", 20, "test@test.com"); static EntityMetaData entityMetaData; @BeforeAll diff --git a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java index ef93eef5..ef03f11f 100644 --- a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java +++ b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java @@ -2,7 +2,6 @@ import database.H2; import entity.Order; -import entity.OrderItem; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -21,16 +20,16 @@ class CustomSelectQueryBuilderTest extends JpaTest { - static OrderItem orderItem1 = new OrderItem(1L, "A", 1); - static OrderItem orderItem2 = new OrderItem(2L, "B", 10); - static OrderItem orderItem3 = new OrderItem(3L, "C", 5); - static Order order = new Order(1L, "test1", List.of(orderItem1, orderItem2, orderItem3)); + static EntityMetaData entityMetaData; @BeforeAll static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); + + entityMetaData = new EntityMetaData(Order.class, order); + initForTest(entityMetaData); } @BeforeEach @@ -55,12 +54,19 @@ void selectSqlWithJoinColumn() { EntityMetaData entityMetaData = new EntityMetaData(Order.class, order); CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); - String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery(order, Order.class, 1); + String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery(order, Order.class); String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;"; assertThat(selectJoinQuery).isEqualTo(resultQuery); } + @DisplayName("findById 테스트 - 연관관계가 있는 경우") + @Test + void findByIdWithAssociationTest() { + List savedOrderList = entityLoader.findByIdWithAssociation(order.getClass(), order, order.getId()); + assertThat(savedOrderList).hasSize(3); + } + private void createTable() { String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; From 2939e5b6c10b152c9e19b21068fef762b92ec52f Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 1 Apr 2024 21:58:14 +0900 Subject: [PATCH 25/30] =?UTF-8?q?refactor:=20step1=20-=20=EC=82=BC?= =?UTF-8?q?=ED=95=AD=EC=97=B0=EC=82=B0=EC=9E=90=20if=20=EB=AC=B8=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/pojo/EntityJoinMetaData.java | 14 ++++++++++---- src/main/java/pojo/EntityMetaData.java | 17 +++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index bd5b9aae..5d70adac 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -51,13 +51,19 @@ public boolean isLazy() { } private String getEntityNameInfo() { - return clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name()) - ? clazz.getAnnotation(Table.class).name() : clazz.getSimpleName().toLowerCase(); + if (clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name())) { + return clazz.getAnnotation(Table.class).name(); + } + + return clazz.getSimpleName().toLowerCase(); } public String getJoinColumnNameInfo(Field field, IdField entityMetaDataIdField) { - return field.isAnnotationPresent(JoinColumn.class) && !isBlankOrEmpty(field.getAnnotation(JoinColumn.class).name()) - ? field.getAnnotation(JoinColumn.class).name() : getEntityNameInfo() + UNDER_SCORE + entityMetaDataIdField.getFieldNameData(); + if (field.isAnnotationPresent(JoinColumn.class) && !isBlankOrEmpty(field.getAnnotation(JoinColumn.class).name())) { + return field.getAnnotation(JoinColumn.class).name(); + } + + return getEntityNameInfo() + UNDER_SCORE + entityMetaDataIdField.getFieldNameData(); } private List getFieldNamesInfo() { diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 2d204ac7..60e42b95 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -15,7 +15,6 @@ public class EntityMetaData { private final Class clazz; - private final Object entity; private final String entityName; private final List entityColumns; private final EntityJoinMetaData entityJoinMetaData; //OrderItem @@ -25,10 +24,9 @@ public EntityMetaData(Class clazz, Object entity) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } this.clazz = clazz; - this.entity = entity; this.entityName = getEntityNameInfo(); - this.entityColumns = getEntityColumnsInfo(); - this.entityJoinMetaData = getEntityJoinMetaDataInfo(); + this.entityColumns = null; + this.entityJoinMetaData = null; } public String getEntityName() { @@ -44,17 +42,20 @@ public EntityJoinMetaData getEntityJoinMetaData() { } private String getEntityNameInfo() { - return clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name()) - ? clazz.getAnnotation(Table.class).name() : clazz.getSimpleName().toLowerCase(); + if (clazz.isAnnotationPresent(Table.class) && !isBlankOrEmpty(clazz.getAnnotation(Table.class).name())) { + return clazz.getAnnotation(Table.class).name(); + } + + return clazz.getSimpleName().toLowerCase(); } - private List getEntityColumnsInfo() { + public List getEntityColumnsInfo(Object entity) { return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() .map(field -> new EntityColumn(field, entity)) .collect(Collectors.toList()); } - private EntityJoinMetaData getEntityJoinMetaDataInfo() { + public EntityJoinMetaData getEntityJoinMetaDataInfo(Object entity) { FieldInfos fieldInfos = new FieldInfos(clazz.getDeclaredFields()); Optional joinColumnField = fieldInfos.getJoinColumnField(); From d61fa702e9b1c3cc30e5cc151e8edd44025e24bc Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 1 Apr 2024 21:58:48 +0900 Subject: [PATCH 26/30] =?UTF-8?q?test:=20step1=20-=20EntityMetaData=20?= =?UTF-8?q?=EB=82=B4=20entityName=20=EA=B4=80=EB=A0=A8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/pojo/EntityMetaDataTest.java | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/java/pojo/EntityMetaDataTest.java diff --git a/src/test/java/pojo/EntityMetaDataTest.java b/src/test/java/pojo/EntityMetaDataTest.java new file mode 100644 index 00000000..f68a81b0 --- /dev/null +++ b/src/test/java/pojo/EntityMetaDataTest.java @@ -0,0 +1,25 @@ +package pojo; + +import entity.Person2; +import entity.Person3; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class EntityMetaDataTest { + + @DisplayName("Table 어노테이션이 없거나 있어도 name 필드가 없을 경우 class 의 simpleName 을 반환한다.") + @Test + void entityNameInfo_ShouldReturnSimpleName() { + EntityMetaData entityMetaData = new EntityMetaData(Person2.class, null); + assertThat(entityMetaData.getEntityName()).isEqualTo("person2"); + } + + @DisplayName("Table 어노테이션이 있고 name 필드가 있을 경우 name 을 반환한다.") + @Test + void entityNameInfo_ShouldReturnTableName() { + EntityMetaData entityMetaData = new EntityMetaData(Person3.class, null); + assertThat(entityMetaData.getEntityName()).isEqualTo("users"); + } +} From efb21b1dee6ce00ec2cdc690f12ebcfc8c757628 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 8 Apr 2024 23:15:35 +0900 Subject: [PATCH 27/30] =?UTF-8?q?refactor:=20step1=20-=20EntityMetaData=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=8A=94=20class=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EB=A7=8C=20=EA=B0=80=EC=A7=80=EA=B3=A0=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/entity/EntityLoaderImpl.java | 4 +-- .../sql/ddl/CreateQueryBuilder.java | 5 ++-- .../sql/dml/CustomSelectQueryBuilder.java | 11 ++++---- src/main/java/pojo/EntityJoinMetaData.java | 4 +-- src/main/java/pojo/EntityMetaData.java | 26 +++++++------------ 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/main/java/persistence/entity/EntityLoaderImpl.java b/src/main/java/persistence/entity/EntityLoaderImpl.java index cd0a1035..e63d8c7c 100644 --- a/src/main/java/persistence/entity/EntityLoaderImpl.java +++ b/src/main/java/persistence/entity/EntityLoaderImpl.java @@ -33,9 +33,9 @@ public List findAll(Class clazz) { //연관관계가 있는 경우 & eager 타입만 고려 @Override public List findByIdWithAssociation(Class clazz, Object entity, Object condition) { - CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData, entity); - EntityJoinMetaData entityJoinMetaData = entityMetaData.getEntityJoinMetaData(); + EntityJoinMetaData entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(entity); if (!entityJoinMetaData.isLazy()) { return eagerTypeQuery(customSelectQueryBuilder, clazz, entity); } diff --git a/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java b/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java index 8f875eac..013750d9 100644 --- a/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java +++ b/src/main/java/persistence/sql/ddl/CreateQueryBuilder.java @@ -5,7 +5,6 @@ import jakarta.persistence.Transient; import pojo.ColumnField; import pojo.EntityMetaData; -import pojo.FieldInfos; import pojo.IdField; import java.lang.reflect.Field; @@ -33,8 +32,8 @@ public CreateQueryBuilder(Dialect dialect, EntityMetaData entityMetaData) { } public String createTable(Object entity) { - FieldInfos fieldInfos = new FieldInfos(entity.getClass().getDeclaredFields()); - return String.format(CREATE_TABLE_QUERY, entityMetaData.getEntityName(), createClause(fieldInfos.getFieldDataList(), entity)); + return String.format(CREATE_TABLE_QUERY, entityMetaData.getEntityName(), + createClause(entityMetaData.getFieldInfos().getFieldDataList(), entity)); } //create 시에 JoinColumn 어노테이션이 있는 필드는 -> JoinColumn 의 name 을 그 필드 객체 생성 시 만들어줘야 한다. diff --git a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java index cf04d98d..afbe9992 100644 --- a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java @@ -18,9 +18,9 @@ public class CustomSelectQueryBuilder { private final EntityMetaData entityMetaData; private final EntityJoinMetaData entityJoinMetaData; - public CustomSelectQueryBuilder(EntityMetaData entityMetaData) { + public CustomSelectQueryBuilder(EntityMetaData entityMetaData, Object entity) { this.entityMetaData = entityMetaData; - this.entityJoinMetaData = entityMetaData.getEntityJoinMetaData(); + this.entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(entity); } public String findByIdJoinQuery(Object entity, Class clazz) { @@ -30,14 +30,14 @@ public String findByIdJoinQuery(Object entity, Class clazz) { String metaDataEntityName = entityMetaData.getEntityName(); String joinMetaDataEntityName = entityJoinMetaData.getEntityName(); - return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(), metaDataEntityName, joinMetaDataEntityName, + return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(entity), metaDataEntityName, joinMetaDataEntityName, metaDataEntityName + PERIOD + idField.getFieldNameData(), joinMetaDataEntityName + PERIOD + entityJoinMetaData.getJoinColumnName(), metaDataEntityName + PERIOD + idField.getFieldNameData(), idField.getFieldValueData()); } - private String getSelectData() { - String entityData = entityMetaData.getEntityColumns() + private String getSelectData(Object entity) { + String entityData = entityMetaData.createEntityColumnsInfo(entity) .stream() .map(entityColumn -> entityColumn.getFieldName().getName()) .map(name -> entityMetaData.getEntityName() + PERIOD + name) @@ -53,5 +53,4 @@ private String getSelectData() { return entityData + COMMA + ownerEntityData; } - } diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index 5d70adac..4c6fc28c 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -16,18 +16,16 @@ public class EntityJoinMetaData { private final Class clazz; - private final Object entity; private final String entityName; private final String joinColumnName; private final List fieldNames; private final boolean lazy; - public EntityJoinMetaData(Class clazz, Object entity, Field field, IdField entityMetaDataIdField) { + public EntityJoinMetaData(Class clazz, Field field, IdField entityMetaDataIdField) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } this.clazz = clazz; - this.entity = entity; this.entityName = getEntityNameInfo(); this.joinColumnName = getJoinColumnNameInfo(field, entityMetaDataIdField); this.fieldNames = getFieldNamesInfo(); diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 60e42b95..168b55f9 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -16,29 +16,23 @@ public class EntityMetaData { private final Class clazz; private final String entityName; - private final List entityColumns; - private final EntityJoinMetaData entityJoinMetaData; //OrderItem + private final FieldInfos fieldInfos; - public EntityMetaData(Class clazz, Object entity) { + public EntityMetaData(Class clazz) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } this.clazz = clazz; this.entityName = getEntityNameInfo(); - this.entityColumns = null; - this.entityJoinMetaData = null; + this.fieldInfos = new FieldInfos(clazz.getDeclaredFields()); } public String getEntityName() { return entityName; } - public List getEntityColumns() { - return entityColumns; - } - - public EntityJoinMetaData getEntityJoinMetaData() { - return entityJoinMetaData; + public FieldInfos getFieldInfos() { + return fieldInfos; } private String getEntityNameInfo() { @@ -49,15 +43,13 @@ private String getEntityNameInfo() { return clazz.getSimpleName().toLowerCase(); } - public List getEntityColumnsInfo(Object entity) { - return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() + public List createEntityColumnsInfo(Object entity) { + return fieldInfos.getIdAndColumnFields().stream() .map(field -> new EntityColumn(field, entity)) .collect(Collectors.toList()); } - public EntityJoinMetaData getEntityJoinMetaDataInfo(Object entity) { - FieldInfos fieldInfos = new FieldInfos(clazz.getDeclaredFields()); - + public EntityJoinMetaData createEntityJoinMetaDataInfo(Object entity) { Optional joinColumnField = fieldInfos.getJoinColumnField(); if (joinColumnField.isEmpty()) { return null; @@ -67,6 +59,6 @@ public EntityJoinMetaData getEntityJoinMetaDataInfo(Object entity) { IdField idField = new IdField(field, entity); Class joinClass = (Class) ((ParameterizedType) joinColumnField.get().getGenericType()).getActualTypeArguments()[0]; - return new EntityJoinMetaData(joinClass, null, joinColumnField.get(), idField); + return new EntityJoinMetaData(joinClass, joinColumnField.get(), idField); } } From 0f6ac2a03d9428c32e95de847f7bc73b40efcfeb Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Mon, 8 Apr 2024 23:16:23 +0900 Subject: [PATCH 28/30] =?UTF-8?q?test:=20step1=20-=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EC=97=B0=EA=B4=80=EA=B4=80=EA=B3=84=EA=B0=80=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20findById=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20loader=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EC=9D=B4=EA=B4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/persistence/JpaTest.java | 31 +++++++++++ .../context/SimplePersistenceContextTest.java | 5 +- .../entity/CustomJpaRepositoryTest.java | 5 +- .../entity/EntityLoaderImplTest.java | 23 ++++++-- .../entity/EntityPersisterImplTest.java | 5 +- .../sql/CustomSelectQueryBuilderTest.java | 54 +++---------------- src/test/java/pojo/EntityMetaDataTest.java | 4 +- 7 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/test/java/persistence/JpaTest.java b/src/test/java/persistence/JpaTest.java index 860c69da..5bdb2407 100644 --- a/src/test/java/persistence/JpaTest.java +++ b/src/test/java/persistence/JpaTest.java @@ -49,4 +49,35 @@ protected static void initForTest(EntityMetaData entityMetaData) { simpleEntityManager = new SimpleEntityManager(entityPersister, entityLoader, persistenceContext, entityEntry); jpaRepository = new CustomJpaRepository(simpleEntityManager); } + + protected static void createOrderAndOrderItemTable() { + String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; + String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; + + jdbcTemplate.execute(createOrderSql); + jdbcTemplate.execute(createOrderItemSql); + } + + protected static void insertOrderAndOrderItemData() { + String insertOrderSql = "insert into orders (id, order_number) values (" + order.getId() + ", '" + order.getOrderNumber() + "');"; + String insertOrderItemSql1 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem1.getId() + ", '" + orderItem1.getProduct() + "' , " + orderItem1.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql2 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem2.getId() + ", '" + orderItem2.getProduct() + "' , " + orderItem2.getQuantity() + ", " + order.getId() + ");"; + String insertOrderItemSql3 = "insert into order_items (id, product, quantity, order_id) " + + "values (" + orderItem3.getId() + ", '" + orderItem3.getProduct() + "' , " + orderItem3.getQuantity() + ", " + order.getId() + ");"; + + jdbcTemplate.execute(insertOrderSql); + jdbcTemplate.execute(insertOrderItemSql1); + jdbcTemplate.execute(insertOrderItemSql2); + jdbcTemplate.execute(insertOrderItemSql3); + } + + protected static void dropOrderAndOrderItemTable() { + String dropOrderTable = "drop table orders;"; + String dropOrderItemTable = "drop table order_items;"; + + jdbcTemplate.execute(dropOrderItemTable); + jdbcTemplate.execute(dropOrderTable); + } } diff --git a/src/test/java/persistence/context/SimplePersistenceContextTest.java b/src/test/java/persistence/context/SimplePersistenceContextTest.java index fb6db31e..4cee5f2a 100644 --- a/src/test/java/persistence/context/SimplePersistenceContextTest.java +++ b/src/test/java/persistence/context/SimplePersistenceContextTest.java @@ -34,13 +34,12 @@ static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); - - entityMetaData = new EntityMetaData(Person3.class, person); - initForTest(entityMetaData); } @BeforeEach void setUp() { + entityMetaData = new EntityMetaData(Person3.class); + initForTest(entityMetaData); createTable(); } diff --git a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java index 2cfc9065..225184ce 100644 --- a/src/test/java/persistence/entity/CustomJpaRepositoryTest.java +++ b/src/test/java/persistence/entity/CustomJpaRepositoryTest.java @@ -28,13 +28,12 @@ static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); - - entityMetaData = new EntityMetaData(Person3.class, person); - initForTest(entityMetaData); } @BeforeEach void setUp() { + entityMetaData = new EntityMetaData(Person3.class); + initForTest(entityMetaData); createTable(); } diff --git a/src/test/java/persistence/entity/EntityLoaderImplTest.java b/src/test/java/persistence/entity/EntityLoaderImplTest.java index 6a56c27c..f539b89c 100644 --- a/src/test/java/persistence/entity/EntityLoaderImplTest.java +++ b/src/test/java/persistence/entity/EntityLoaderImplTest.java @@ -1,6 +1,7 @@ package persistence.entity; import database.H2; +import entity.Order; import entity.Person3; import jdbc.JdbcTemplate; import org.junit.jupiter.api.AfterAll; @@ -15,6 +16,7 @@ import pojo.EntityMetaData; import java.sql.SQLException; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; @@ -28,13 +30,12 @@ static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); - - entityMetaData = new EntityMetaData(Person3.class, person); - initForTest(entityMetaData); } @BeforeEach void setUp() { + entityMetaData = new EntityMetaData(Person3.class); + initForTest(entityMetaData); createTable(); } @@ -61,6 +62,21 @@ void findByIdTest() { ); } + @DisplayName("findById 테스트 - 연관관계가 있는 경우") + @Test + void findByIdWithAssociationTest() { + entityMetaData = new EntityMetaData(Order.class); + initForTest(entityMetaData); + + createOrderAndOrderItemTable(); + insertOrderAndOrderItemData(); + + List savedOrderList = entityLoader.findByIdWithAssociation(order.getClass(), order, order.getId()); + assertThat(savedOrderList).hasSize(3); + + dropOrderAndOrderItemTable(); + } + @DisplayName("findAll 테스트") @Test void findAllTest() { @@ -76,6 +92,7 @@ void findAllTest() { } private void createTable() { + dropTable(); CreateQueryBuilder createQueryBuilder = new CreateQueryBuilder(dialect, entityMetaData); jdbcTemplate.execute(createQueryBuilder.createTable(person)); } diff --git a/src/test/java/persistence/entity/EntityPersisterImplTest.java b/src/test/java/persistence/entity/EntityPersisterImplTest.java index b8fec2f0..30d4321a 100644 --- a/src/test/java/persistence/entity/EntityPersisterImplTest.java +++ b/src/test/java/persistence/entity/EntityPersisterImplTest.java @@ -30,13 +30,12 @@ static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); - - entityMetaData = new EntityMetaData(Person3.class, person); - initForTest(entityMetaData); } @BeforeEach void setUp() { + entityMetaData = new EntityMetaData(Person3.class); + initForTest(entityMetaData); createTable(); } diff --git a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java index ef03f11f..e7574ab3 100644 --- a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java +++ b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java @@ -14,7 +14,6 @@ import pojo.EntityMetaData; import java.sql.SQLException; -import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -27,20 +26,19 @@ static void init() throws SQLException { server = new H2(); server.start(); jdbcTemplate = new JdbcTemplate(server.getConnection()); - - entityMetaData = new EntityMetaData(Order.class, order); - initForTest(entityMetaData); } @BeforeEach void setUp() { - createTable(); - insertData(); + entityMetaData = new EntityMetaData(Order.class); + initForTest(entityMetaData); + createOrderAndOrderItemTable(); + insertOrderAndOrderItemData(); } @AfterEach void remove() { - dropTable(); + dropOrderAndOrderItemTable(); } @AfterAll @@ -51,50 +49,12 @@ static void destroy() { @DisplayName("OneToMany 를 갖고 있는 Entity 클래스의 select 쿼리는 join 문 포함 필요") @Test void selectSqlWithJoinColumn() { - EntityMetaData entityMetaData = new EntityMetaData(Order.class, order); + entityMetaData = new EntityMetaData(Order.class); - CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData, order); String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery(order, Order.class); String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;"; assertThat(selectJoinQuery).isEqualTo(resultQuery); } - - @DisplayName("findById 테스트 - 연관관계가 있는 경우") - @Test - void findByIdWithAssociationTest() { - List savedOrderList = entityLoader.findByIdWithAssociation(order.getClass(), order, order.getId()); - assertThat(savedOrderList).hasSize(3); - } - - private void createTable() { - String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; - String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; - - jdbcTemplate.execute(createOrderSql); - jdbcTemplate.execute(createOrderItemSql); - } - - private void insertData() { - String insertOrderSql = "insert into orders (id, order_number) values (" + order.getId() + ", '" + order.getOrderNumber() + "');"; - String insertOrderItemSql1 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem1.getId() + ", '" + orderItem1.getProduct() + "' , " + orderItem1.getQuantity() + ", " + order.getId() + ");"; - String insertOrderItemSql2 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem2.getId() + ", '" + orderItem2.getProduct() + "' , " + orderItem2.getQuantity() + ", " + order.getId() + ");"; - String insertOrderItemSql3 = "insert into order_items (id, product, quantity, order_id) " + - "values (" + orderItem3.getId() + ", '" + orderItem3.getProduct() + "' , " + orderItem3.getQuantity() + ", " + order.getId() + ");"; - - jdbcTemplate.execute(insertOrderSql); - jdbcTemplate.execute(insertOrderItemSql1); - jdbcTemplate.execute(insertOrderItemSql2); - jdbcTemplate.execute(insertOrderItemSql3); - } - - private void dropTable() { - String dropOrderTable = "drop table orders;"; - String dropOrderItemTable = "drop table order_items;"; - - jdbcTemplate.execute(dropOrderItemTable); - jdbcTemplate.execute(dropOrderTable); - } } diff --git a/src/test/java/pojo/EntityMetaDataTest.java b/src/test/java/pojo/EntityMetaDataTest.java index f68a81b0..9a4e653c 100644 --- a/src/test/java/pojo/EntityMetaDataTest.java +++ b/src/test/java/pojo/EntityMetaDataTest.java @@ -12,14 +12,14 @@ class EntityMetaDataTest { @DisplayName("Table 어노테이션이 없거나 있어도 name 필드가 없을 경우 class 의 simpleName 을 반환한다.") @Test void entityNameInfo_ShouldReturnSimpleName() { - EntityMetaData entityMetaData = new EntityMetaData(Person2.class, null); + EntityMetaData entityMetaData = new EntityMetaData(Person2.class); assertThat(entityMetaData.getEntityName()).isEqualTo("person2"); } @DisplayName("Table 어노테이션이 있고 name 필드가 있을 경우 name 을 반환한다.") @Test void entityNameInfo_ShouldReturnTableName() { - EntityMetaData entityMetaData = new EntityMetaData(Person3.class, null); + EntityMetaData entityMetaData = new EntityMetaData(Person3.class); assertThat(entityMetaData.getEntityName()).isEqualTo("users"); } } From 19431c03143972aa7775a83703568450186a467b Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 10 Apr 2024 21:49:44 +0900 Subject: [PATCH 29/30] =?UTF-8?q?refactor:=20step1=20-=20EntityJoinMetaDat?= =?UTF-8?q?a=20=EC=97=90=EC=84=9C=EB=8F=84=20entity=20=EB=A5=BC=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=EB=A1=9C=20=EB=B0=9B?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/entity/EntityLoaderImpl.java | 5 +-- .../sql/dml/CustomSelectQueryBuilder.java | 8 ++-- src/main/java/pojo/EntityJoinMetaData.java | 38 ++++++++----------- src/main/java/pojo/EntityMetaData.java | 7 +--- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/main/java/persistence/entity/EntityLoaderImpl.java b/src/main/java/persistence/entity/EntityLoaderImpl.java index e63d8c7c..1910fb7c 100644 --- a/src/main/java/persistence/entity/EntityLoaderImpl.java +++ b/src/main/java/persistence/entity/EntityLoaderImpl.java @@ -33,9 +33,9 @@ public List findAll(Class clazz) { //연관관계가 있는 경우 & eager 타입만 고려 @Override public List findByIdWithAssociation(Class clazz, Object entity, Object condition) { - CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData, entity); + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); - EntityJoinMetaData entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(entity); + EntityJoinMetaData entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(); if (!entityJoinMetaData.isLazy()) { return eagerTypeQuery(customSelectQueryBuilder, clazz, entity); } @@ -47,6 +47,5 @@ public List findByIdWithAssociation(Class clazz, Object entity, Object private List eagerTypeQuery(CustomSelectQueryBuilder customSelectQueryBuilder, Class clazz, Object entity) { return jdbcTemplate.query(customSelectQueryBuilder.findByIdJoinQuery(entity, clazz), new RowMapperImpl<>(clazz)); - } } diff --git a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java index afbe9992..611e31ef 100644 --- a/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java +++ b/src/main/java/persistence/sql/dml/CustomSelectQueryBuilder.java @@ -18,9 +18,9 @@ public class CustomSelectQueryBuilder { private final EntityMetaData entityMetaData; private final EntityJoinMetaData entityJoinMetaData; - public CustomSelectQueryBuilder(EntityMetaData entityMetaData, Object entity) { + public CustomSelectQueryBuilder(EntityMetaData entityMetaData) { this.entityMetaData = entityMetaData; - this.entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(entity); + this.entityJoinMetaData = entityMetaData.createEntityJoinMetaDataInfo(); } public String findByIdJoinQuery(Object entity, Class clazz) { @@ -32,7 +32,7 @@ public String findByIdJoinQuery(Object entity, Class clazz) { return String.format(FIND_BY_ID_JOIN_QUERY, getSelectData(entity), metaDataEntityName, joinMetaDataEntityName, metaDataEntityName + PERIOD + idField.getFieldNameData(), - joinMetaDataEntityName + PERIOD + entityJoinMetaData.getJoinColumnName(), + joinMetaDataEntityName + PERIOD + entityJoinMetaData.getJoinColumnNameInfo(idField), metaDataEntityName + PERIOD + idField.getFieldNameData(), idField.getFieldValueData()); } @@ -44,7 +44,7 @@ private String getSelectData(Object entity) { .reduce((o1, o2) -> String.join(COMMA, o1, o2)) .orElseThrow(() -> new IllegalStateException("Id 혹은 Column 타입이 없습니다.")); - String ownerEntityData = entityJoinMetaData.getFieldNames() + String ownerEntityData = entityJoinMetaData.getFieldNamesInfo() .stream() .map(FieldName::getName) .map(name -> entityJoinMetaData.getEntityName() + PERIOD + name) diff --git a/src/main/java/pojo/EntityJoinMetaData.java b/src/main/java/pojo/EntityJoinMetaData.java index 4c6fc28c..78d2aeec 100644 --- a/src/main/java/pojo/EntityJoinMetaData.java +++ b/src/main/java/pojo/EntityJoinMetaData.java @@ -17,18 +17,18 @@ public class EntityJoinMetaData { private final Class clazz; private final String entityName; - private final String joinColumnName; - private final List fieldNames; + private final FieldInfos fieldInfos; + private final Field joinField; private final boolean lazy; - public EntityJoinMetaData(Class clazz, Field field, IdField entityMetaDataIdField) { + public EntityJoinMetaData(Class clazz, Field field) { if (!clazz.isAnnotationPresent(Entity.class)) { throw new IllegalStateException("Entity 클래스가 아닙니다."); } this.clazz = clazz; this.entityName = getEntityNameInfo(); - this.joinColumnName = getJoinColumnNameInfo(field, entityMetaDataIdField); - this.fieldNames = getFieldNamesInfo(); + this.fieldInfos = new FieldInfos(clazz.getDeclaredFields()); + this.joinField = field; this.lazy = isLazy(field); } @@ -36,14 +36,6 @@ public String getEntityName() { return entityName; } - public String getJoinColumnName() { - return joinColumnName; - } - - public List getFieldNames() { - return fieldNames; - } - public boolean isLazy() { return lazy; } @@ -56,22 +48,22 @@ private String getEntityNameInfo() { return clazz.getSimpleName().toLowerCase(); } - public String getJoinColumnNameInfo(Field field, IdField entityMetaDataIdField) { - if (field.isAnnotationPresent(JoinColumn.class) && !isBlankOrEmpty(field.getAnnotation(JoinColumn.class).name())) { - return field.getAnnotation(JoinColumn.class).name(); + private boolean isLazy(Field field) { + //일단 OneToMany 만 고려 + return !field.getAnnotation(OneToMany.class).fetch().equals(FetchType.EAGER); + } + + public String getJoinColumnNameInfo(IdField entityMetaDataIdField) { + if (joinField.isAnnotationPresent(JoinColumn.class) && !isBlankOrEmpty(joinField.getAnnotation(JoinColumn.class).name())) { + return joinField.getAnnotation(JoinColumn.class).name(); } return getEntityNameInfo() + UNDER_SCORE + entityMetaDataIdField.getFieldNameData(); } - private List getFieldNamesInfo() { - return new FieldInfos(clazz.getDeclaredFields()).getIdAndColumnFields().stream() + public List getFieldNamesInfo() { + return fieldInfos.getIdAndColumnFields().stream() .map(FieldName::new) .collect(Collectors.toList()); } - - private boolean isLazy(Field field) { - //일단 OneToMany 만 고려 - return !field.getAnnotation(OneToMany.class).fetch().equals(FetchType.EAGER); - } } diff --git a/src/main/java/pojo/EntityMetaData.java b/src/main/java/pojo/EntityMetaData.java index 168b55f9..f6a9a843 100644 --- a/src/main/java/pojo/EntityMetaData.java +++ b/src/main/java/pojo/EntityMetaData.java @@ -49,16 +49,13 @@ public List createEntityColumnsInfo(Object entity) { .collect(Collectors.toList()); } - public EntityJoinMetaData createEntityJoinMetaDataInfo(Object entity) { + public EntityJoinMetaData createEntityJoinMetaDataInfo() { Optional joinColumnField = fieldInfos.getJoinColumnField(); if (joinColumnField.isEmpty()) { return null; } - Field field = fieldInfos.getIdField(); - IdField idField = new IdField(field, entity); - Class joinClass = (Class) ((ParameterizedType) joinColumnField.get().getGenericType()).getActualTypeArguments()[0]; - return new EntityJoinMetaData(joinClass, joinColumnField.get(), idField); + return new EntityJoinMetaData(joinClass, joinColumnField.get()); } } From 6509320b8ec73edb1475714431d7e316f67e4830 Mon Sep 17 00:00:00 2001 From: parkje0927 Date: Wed, 10 Apr 2024 21:50:12 +0900 Subject: [PATCH 30/30] =?UTF-8?q?test:=20step1=20-=20EntityJoinMetaData=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/persistence/JpaTest.java | 4 ++-- .../java/persistence/sql/CustomSelectQueryBuilderTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/persistence/JpaTest.java b/src/test/java/persistence/JpaTest.java index 5bdb2407..91d36821 100644 --- a/src/test/java/persistence/JpaTest.java +++ b/src/test/java/persistence/JpaTest.java @@ -52,7 +52,7 @@ protected static void initForTest(EntityMetaData entityMetaData) { protected static void createOrderAndOrderItemTable() { String createOrderSql = "create table orders(id bigint auto_increment primary key, order_number varchar(255) null);"; - String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; + String createOrderItemSql = "create table order_items(id bigint auto_increment primary key, product varchar(255) null, quantity int null, order_id bigint null, foreign key (order_id) references orders (id));"; jdbcTemplate.execute(createOrderSql); jdbcTemplate.execute(createOrderItemSql); @@ -74,8 +74,8 @@ protected static void insertOrderAndOrderItemData() { } protected static void dropOrderAndOrderItemTable() { - String dropOrderTable = "drop table orders;"; String dropOrderItemTable = "drop table order_items;"; + String dropOrderTable = "drop table orders;"; jdbcTemplate.execute(dropOrderItemTable); jdbcTemplate.execute(dropOrderTable); diff --git a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java index e7574ab3..858f786d 100644 --- a/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java +++ b/src/test/java/persistence/sql/CustomSelectQueryBuilderTest.java @@ -51,7 +51,7 @@ static void destroy() { void selectSqlWithJoinColumn() { entityMetaData = new EntityMetaData(Order.class); - CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData, order); + CustomSelectQueryBuilder customSelectQueryBuilder = new CustomSelectQueryBuilder(entityMetaData); String selectJoinQuery = customSelectQueryBuilder.findByIdJoinQuery(order, Order.class); String resultQuery = "SELECT orders.id, orders.order_number, order_items.id, order_items.product, order_items.quantity FROM orders LEFT JOIN order_items ON orders.id = order_items.order_id WHERE orders.id = 1;";