-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Step3 PR 입니다 #227
base: misudev
Are you sure you want to change the base?
Step3 PR 입니다 #227
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package persistence.sql.common; | ||
|
||
import jakarta.persistence.*; | ||
|
||
@Table(name = "users") | ||
@Entity | ||
public class Person { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(name = "nick_name") | ||
private String name; | ||
|
||
@Column(name = "old") | ||
private Integer age; | ||
|
||
@Column(nullable = false) | ||
private String email; | ||
|
||
@Transient | ||
private Integer index; | ||
|
||
public Person() { | ||
|
||
} | ||
|
||
public Person(Long id) { | ||
this.id = id; | ||
} | ||
|
||
public Person(Long id, String name, Integer age, String email, Integer index) { | ||
this.id = id; | ||
this.name = name; | ||
this.age = age; | ||
this.email = email; | ||
this.index = index; | ||
} | ||
|
||
public Person(String name, Integer age, String email, Integer index) { | ||
this.name = name; | ||
this.age = age; | ||
this.email = email; | ||
this.index = index; | ||
} | ||
|
||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package persistence.sql.dml; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Table; | ||
import persistence.sql.model.ColumnFactory; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
|
||
public class DMLHelper { | ||
private static final String INSERT_QUERY_FORMAT = "insert into %s (%s) values (%s);"; | ||
private static final String FIND_ALL_QUERY_FORMAT = "select * from %s;"; | ||
private static final String FIND_QUERY_FORMAT = "select * from %s %s;"; | ||
private static final String DELETE_QUERY_FORMAT = "delete from %s %s;"; | ||
|
||
public String getInsertQuery(Class<?> clazz, Object object) { | ||
return String.format(INSERT_QUERY_FORMAT, getTableName(clazz), columnsClause(clazz), valueClause(object)); | ||
} | ||
|
||
public String getFindAllQuery(Class<?> clazz) { | ||
return String.format(FIND_ALL_QUERY_FORMAT, getTableName(clazz)); | ||
} | ||
|
||
public String getFindByIdQuery(Class<?> clazz, Object id) { | ||
try { | ||
var idField = Arrays.stream(clazz.getDeclaredFields()) | ||
.filter(field -> { | ||
var column = ColumnFactory.createColumn(field); | ||
return column.isPresent() && column.get().isPK(); | ||
}) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("기본키가 존재하지 않습니다.")); | ||
|
||
var object = clazz.getDeclaredConstructor().newInstance(); | ||
idField.setAccessible(true); | ||
idField.set(object, id); | ||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이미 잘 만들어주신 |
||
|
||
return String.format(FIND_QUERY_FORMAT, getTableName(clazz), whereClause(object)); | ||
} catch (Exception e) { | ||
throw new IllegalArgumentException("생성 불가 한 class 입니다."); | ||
} | ||
} | ||
|
||
public String getDeleteQuery(Class<?> clazz, Object object) { | ||
return String.format( | ||
DELETE_QUERY_FORMAT, | ||
getTableName(clazz), | ||
whereClause(object) | ||
); | ||
} | ||
|
||
private String whereClause(Object object) { | ||
var clazz = object.getClass(); | ||
|
||
return "where " + Arrays.stream(clazz.getDeclaredFields()) | ||
.map(field -> { | ||
var columnValue = getColumnValue(field, object); | ||
var column = ColumnFactory.createColumn(field); | ||
if (columnValue.isEmpty() || column.isEmpty()) return null; | ||
return String.format("%s = %s", column.get().getName(), columnValue.get()); | ||
} | ||
) | ||
.filter(Objects::nonNull) | ||
.collect(Collectors.joining(" and ")); | ||
} | ||
|
||
private String getTableName(Class<?> clazz) { | ||
var annotations = clazz.getAnnotations(); | ||
return Arrays.stream(annotations) | ||
.filter(annotation -> annotation.annotationType().equals(Table.class)) | ||
.map(table -> ((Table) table).name()) | ||
.findFirst() | ||
.orElse(clazz.getSimpleName()); | ||
} | ||
Comment on lines
+74
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
public class TableName {
private final String value;
public TableName(Class<?> clazz) {
// 테이블 어노테이션이 있으면 그 값을 사용하고 아니면 클래스 명 사용
} |
||
|
||
private String columnsClause(Class<?> clazz) { | ||
var fields = clazz.getDeclaredFields(); | ||
return Arrays.stream(fields) | ||
.filter(this::isInsertQueryTarget) | ||
.map(ColumnFactory::createColumn) | ||
.filter(Optional::isPresent) | ||
.map(column -> column.get().getName()) | ||
.collect(Collectors.joining(", ")); | ||
} | ||
|
||
private String valueClause(Object object) { | ||
var clazz = object.getClass(); | ||
return Arrays.stream(clazz.getDeclaredFields()) | ||
.filter(this::isInsertQueryTarget) | ||
.map(field -> getColumnValue(field, object)) | ||
.filter(Optional::isPresent) | ||
.map(Optional::get) | ||
.collect(Collectors.joining(", ")); | ||
} | ||
private boolean isInsertQueryTarget(Field field) { | ||
var isId = false; | ||
var isGeneratedValue = false; | ||
var isColumn = false; | ||
|
||
for (Annotation annotation : field.getAnnotations()) { | ||
if (annotation instanceof Column) { | ||
isColumn = true; | ||
} else if (annotation instanceof Id) { | ||
isId = true; | ||
} else if (annotation instanceof GeneratedValue) { | ||
isGeneratedValue = true; | ||
} | ||
} | ||
|
||
return isColumn || (isId && !isGeneratedValue); | ||
} | ||
Comment on lines
+93
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이런 로직들도 모두
|
||
|
||
private Optional<String> getColumnValue(Field field, Object object) { | ||
try { | ||
field.setAccessible(true); | ||
var column = ColumnFactory.createColumn(field); | ||
if (column.isPresent()) { | ||
return Optional.ofNullable(column.get().getQueryValue(field.get(object))); | ||
} | ||
return Optional.empty(); | ||
} catch (IllegalAccessException e) { | ||
throw new IllegalStateException("접근할 수 없는 필드입니다."); | ||
} | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (반영하지 않으셔도 됩니다.) 지금도 충분히 잘 구현해주셔서 변경하지 않으셔도 되는데 제가 의도했던 바만 코멘트 추가로 남기겠습니다. PkColumn와 SimpleColumn을 생각해보았을때 PkColumn은 SimpleColumn에서 추가로 id와 관련된 기능이 추가된 클래스에요. 그래서 PkColumn이 SimpleColumn을 들고 있고 그 둘을 Column이라는 인터페이스로 묶는건 어떨까하여 드린 피드백이었습니다. 구현의 차이가 있을 것 같은데 추상화를 통해서 공통된 부분을 묶는다는 컨셉은 동일해요 😄 public interface Column public class PkColumn implements Column {
private final String strategy;
private final SimpleColumn;
// ... public class SimpleColumn implements Column {
private final String name;
private final Type type;
// ... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
package persistence.sql.ddl.model; | ||
|
||
import jakarta.persistence.Id; | ||
package persistence.sql.model; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
import java.util.Optional; | ||
|
||
public class Column { | ||
private String name; | ||
|
@@ -22,30 +22,16 @@ public Condition getCondition() { | |
return condition; | ||
} | ||
|
||
public Column(String name, Type type, Condition condition) { | ||
Column(String name, Type type, Condition condition) { | ||
this.name = name; | ||
this.type = type; | ||
this.condition = condition; | ||
} | ||
|
||
public static Column from(Field field) { | ||
var isPKColumn = Arrays.stream(field.getAnnotations()) | ||
.anyMatch(annotation -> annotation.annotationType().equals(Id.class)); | ||
|
||
if (isPKColumn) return PKColumn.from(field); | ||
var columnAnnotation = Arrays.stream(field.getAnnotations()) | ||
.filter(annotation -> annotation.annotationType().equals(jakarta.persistence.Column.class)) | ||
.map(annotation -> (jakarta.persistence.Column) annotation) | ||
.findFirst(); | ||
|
||
if (columnAnnotation.isEmpty()) return null; | ||
var column = columnAnnotation.get(); | ||
|
||
var name = column.name().isBlank() ? field.getName() : column.name(); | ||
var type = Type.from(field.getType()); | ||
var condition = Condition.from(column); | ||
|
||
return new Column(name, type, condition); | ||
Column(String name, Type type) { | ||
this.name = name; | ||
this.type = type; | ||
this.condition = Condition.DEFAULT_CONDITION; | ||
} | ||
|
||
public String getDDLColumnQuery() { | ||
|
@@ -57,4 +43,23 @@ public String getDDLColumnQuery() { | |
if (condition.isUnique()) sb.append(" ").append("UNIQUE"); | ||
return sb.toString(); | ||
} | ||
|
||
public String getQueryValue(Object value) { | ||
if (Objects.isNull(value)) return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (type == Type.VARCHAR) { | ||
return String.format("'%s'", value); | ||
} | ||
return String.valueOf(value); | ||
} | ||
|
||
public boolean isPK() { | ||
return false; | ||
} | ||
|
||
protected static Optional<jakarta.persistence.Column> getColumnAnnotation(Field field) { | ||
return Arrays.stream(field.getAnnotations()) | ||
.filter(annotation -> annotation.annotationType().equals(jakarta.persistence.Column.class)) | ||
.map(annotation -> (jakarta.persistence.Column) annotation) | ||
.findFirst(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,23 @@ | ||||||||||||||||||||||||||||||||||||||||||||
package persistence.sql.model; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Id; | ||||||||||||||||||||||||||||||||||||||||||||
import jakarta.persistence.Transient; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
import java.lang.reflect.Field; | ||||||||||||||||||||||||||||||||||||||||||||
import java.util.Arrays; | ||||||||||||||||||||||||||||||||||||||||||||
import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
public class ColumnFactory { | ||||||||||||||||||||||||||||||||||||||||||||
public static Optional<Column> createColumn(Field field) { | ||||||||||||||||||||||||||||||||||||||||||||
boolean isPKColumn = false; | ||||||||||||||||||||||||||||||||||||||||||||
var isTransient = false; | ||||||||||||||||||||||||||||||||||||||||||||
for(var annotation : field.getAnnotations()) { | ||||||||||||||||||||||||||||||||||||||||||||
if (annotation.annotationType().equals(Id.class)) isPKColumn = true; | ||||||||||||||||||||||||||||||||||||||||||||
if (annotation.annotationType().equals(Transient.class)) isTransient = true; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
if (isTransient) return Optional.empty(); | ||||||||||||||||||||||||||||||||||||||||||||
if (isPKColumn) return Optional.of(PKColumn.from(field)); | ||||||||||||||||||||||||||||||||||||||||||||
return Optional.of(SimpleColumn.from(field)); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+11
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
로 (반영하지 않으셔도 됩니다.) 이와는 별개로 Factory가 과연 필요할지는 고민이 필요할 것 같아요. |
||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package persistence.sql.model; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
|
||
public class SimpleColumn extends Column { | ||
public SimpleColumn(String name, Type type, Condition condition) { | ||
super(name, type, condition); | ||
} | ||
|
||
public SimpleColumn(String name, Type type) { | ||
super(name, type); | ||
} | ||
|
||
public static SimpleColumn from(Field field) { | ||
var columnAnnotation = getColumnAnnotation(field); | ||
if (columnAnnotation.isEmpty()) { | ||
return new SimpleColumn(field.getName(), Type.from(field.getType())); | ||
} | ||
|
||
var column = columnAnnotation.get(); | ||
var name = column.name().isBlank() ? field.getName() : column.name(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (반영하지 않으셔도 됩니다.) 삼항연산자보다는 if else를 직접 표현하는 것이 가독성이 더 올라갑니다. 이정도의 기능이라면 private static String parseColumnName(Field field, jakarta.persistence.Column column) {
if (column.name().isBlank()) {
return field.getName();
}
return column.name();
} |
||
var type = Type.from(field.getType()); | ||
var condition = Condition.from(column); | ||
|
||
return new SimpleColumn(name, type, condition); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
entity에서 Column을 추출해서 List로 들고있어도 되지 않을까요?
Columns
로 일급컬랙션을 만든다면 이런 로직들이 전부 이관되고, 이렇게List<Column>
을 사용하는 다양한 클래스들에서 사용될 수 있을 것 같아요.