JPA를 하면서 가장 시간이 많이 필요한 곳이 Entity를 설계하는 것이다.
많은 테이블을 Entity로 변환하는 작업은 만만치 않은 작업이다.
그래서 처음에 시작할 때, 이클립스 plug-in을 찾아서 해보았지만, 프로젝트 상황에 맞게 딱 떨어지는 것을 찾기가 어려웠다.
주로 JAVA 코딩을 하는 본인은 Table의 속성값들을 조회해서, Entity를 생성하는 서비스를 만들어서 사용하기로 했다.
혹시, JPA를 시작하는데, Entity를 만드는데 어려움을 겪고 있는 개발자들에게 도움이 되었으면 한다.
복합키 구현은 IdClass로 진행하였다.
개발된 소스코드는 Oracle 기준으로 작성되었음을 미리 고지한다.
방법은 이렇게 tableName을 파라메터로 DB에서 table관련 정보를 조회하여, JPA entity구조에 맞게 String으로 생성하는 것이다.
그럼 시작해보자
1. DB에서 TABLE의 컬럼 속성 값 조회
Entity를 만들기 위해서 Table의 속성값을 조회하는 것부터 시작한다.
개발한 SQL은 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
SELECT T1.COLUMN_ID
, T1.COLUMN_NAME
, T2.COMMENTS
, T1.DATA_TYPE
, T1.NULLABLE
, (CASE WHEN T1.DATA_TYPE = 'NUMBER' THEN 'BigDecimal'
WHEN T1.DATA_TYPE = 'DATE' THEN 'localDateTime'
ELSE 'String' END) AS CONVERT_DATA_TYPE -- 사용하고 있는 DATA 종류에 따라서 사용
, LOWER(SBUSTR(TRIM(T1.COLUMN_NAME), 1, 1)) || SUBSTR(REPLACE(INITCAP(TRIM(T1.COMUMN_NAME)), '_'), 2, LENGTH(TRIM(T1.COLUMN_NAME))) AS COL_NAME -- UNDER SCORE -> CAMEL CASE로 변환
, TRIM(PK_COLUMN) AS PK_COLUMN
, TRIM(REPLACE(FNC_LONG2CHAR_DEFAULT(T1.TABLE_NAME, T1.COLUMN_NAME), '''','')) AS DEFAULT_VALUE -- DEFAULT 변수값
FROM ALL_TAB_COLS T1
LEFT OUTER JOIN ALL_COL_COMMENTS T2
ON T1.OWNER = T2.OWNER AND T1.TABLE_NAME = T2.TABLE_NAME AND T1.COLUMN_NAME = T2.COLUMN_NAME
LEFT OUTER JOIN (SELECT K.COLUMN_NAME AS PK_COLUMN
FROM ALL_IND_COLUMNS K
WHERE K.INDEX_NAME = ( SELECT INDEX_NAME
FROM ALL_INDEXES
WHERE TABLE_NAME = #{tableNmae}
AND INDEX_NAME LIKE '%PK%' -- 생성된 PK는 명칭에 PK들어가 있다고 가정
AND ROWNUM = 1 )
AND INDEX_OWNER NOT IN ('','') -- 현재 사용하고 있는 OWNER에서 제외할 것들 작성. 없다면 해당 조건 없어도 됨.
) T3
ON T1.COLUMN_NAME = T3.PK_COLUMN
WHERE T1.TABLE_NAME = #{tableName}
AND T1.COLUMN_ID IS NOT NULL
AND T1.COLUMN_NAME NOT LIKE 'FST_REG%' -- 컬럼 중에 공통적인 컬럼은 제외하기 위한 조건. 공통 컬럼은 보통 BaseEntity로 생성해서 사용함
AND T1.OWNER NOT IN ('', '') -- 현재 사용하고 있는 OWNER에서 제외할 것들 작성. 없다면 해당 조건 없어도 됨.
ORDER BY CASE WHEN PK_COLUMN IS NOT NULL THEN 0 ELSE 1 END, COLUM_ID
|
cs |
DB에서 관리하고 있는 테이블 관련 속성테이블을 이용해서 생성한 Entity에 해당하는 테이블의 Column 정보를 조회한다.
ALL_TAB_COLS, ALL_COL_COMMENTS, ALL_IND_COLUMNS, ALL_INDEXES 테이블을 이용했다.
(각각 테이블이 의미하는 것은 여기에서는 Skip한다.)
명칭 | 의미 | 비고 |
COLUMN_ID | DB컬럼ID (참고용) | |
COLUMN_NAME | DB컬럼명 (참고용) | |
COMMENTS | 컬럼의 주석 | |
DATA_TYPE | 컬럼의 데이터 유형 | |
NULLABLE | 컬럼의 Null값 허용 여부 | |
CONVERT_DATA_TYPE | JAVA entity에서 사용할 데이터 유형 | |
COL_NAME | JAVA entity에서 사용할 camelCase로 | |
PK_COLUMN | PK컬럼 여부 | |
DEFAULT_VALUE | 컬럼의 default 값 | Default값을 조회할 때, CLOB으로 FNC_LONG2CHAR_DEFAULT function을 만들어서 사용 function은 링크 블로그 참조 applejara.tistory.com/427 |
2. 조회된 값을 이용하여 Entity구조 생성
DB에서 정상적으로 Entity를 생성할 데이터가 조회가 되면, JPA에서 사용하는 annotation을 사용하여, 생성한다.
백문이 불여일견이라고!!! 소스코드를 보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
public MetaJpaResponse getJpaModelInfo(String tableNmae, String entityName) {
// 이부분에서 SQL을 호출하여 데이터 조회
List<MetaJpaVO> metaJpaVos = metaDao.selectListModelEntityInfo(tableName);
StringBuffer sb1 = new StringBuffer(); // Entity 정보를 생성할 StringBuffer
StringBuffer sb2 = new StringBuffer(); // Entity가 복합키일 경우, EntityKey 정보를 생성할 StringBuffer
if(CollectionUtils.isNotEmpty(metaJpaVos) {
int pkCount = 0; // 복합키를 생성할지 판단하기 PK수 변수
int defaultCount = 0; // @PerPersist를 사용할지 판단하기 위해서 default값을 가진 컬럼의 개수를 판단하기 위한 PK 변수
for(MetaJpaVO metaJpaVo : metaJpaVos) {
if(!StringUtil.isEmpty(metaJpaVo.getPkColumn())) {
++pkCount;
}
if(!StringUtil.isEmpty(metaJpaVo.getDefaultValue())) {
++defaultCount;
}
}
// Entity 생성
sb1.append("\n@Getter\n@Setter\n@Entity(name =\"" + tableName + "\")\n");
if(pkCount > 1) {
sb1.append("@IdClass(" + entityName + "Key.class)\n");
}
sb1.append("public class " + entityName + (pkCount > 1 ? " implements Serializable" : "") + "{\n\n");
if(pkCount > 1) {
sb1.append(" private static final long seialVersionUID = " + new SimpleDateFormat("yyyyMMddHHmmssS") + ";\n\n");
}
// 컬럼 생성
for(MetaJpaVO metaJpaVo : metaJpaVos) {
if(!StringUtil.isEmpty(metaJpaVo.getPkColumn())) {
sb1.append(" @Id\n");
else if("N".equals(metaJpaVo.getNullable())) {
sb1.append(" @NotNull\n");
}
sb1.append(" @Column\n");
sb1.append(" private " + metaJpaVo.getConvertDataType() + " " + metaJpaVo.getColName() + "; /* " + metaJpaVo.getComments() + "*/");
}
// prepersist 생성
if(defaultCount > 0) {
sb1.append("@PrePersist\n private void prePersist() {\n");
for(MetaJpaVometaJpaVo : metaJpaVos) {
if(!StringUtil.isEmpty(metaJpaVo.getDefaultValue())) {
sb1.append(" this." + metaJpaVo.getColName() + " = ");
switch(metaJpaVo.getConvertDataType()) {
case "String" :
sb1.append("\"" + metaJpaVo.getDefaultValue() + "\");\n");
break;
case "BigDecimal":
sb1.append("new BigDecimal(\"" + metaJpaVo.getDefaultValue() + "\");\n");
break;
default:
sb1.append(metaJpaVo.getDefaultValue() + "\");\n");
}
}
sb1.append(" }\n\n");
}
sb1.append("}");
// Entity 복합키 생성
if(pkCount > 1) {
sb2.append("@EqualsAndHashCode\n@NoArgsConstructor\n@Getter\n@Setter\npublic class ");
sb2.append(entityName + "Key implements Serializable {\n\n");
for(MetaJpaVO metaJpaVo : metaJpaVos) {
if(!StringUtil.isEmpty(metaJpaVo.getPkColumn())) {
sb2.append(" private " + metaJpaVo.getConvertDataType() + " " + metaJpaVo.getColName() + "; /* " + metaJpaVo.getComments() + "*/");
}
}
sb2.append("}");
}
return MetaJpaResponse.builder().modelEntity(sb1.toString().modelKey(sb2.toString).build();
}
return null;
}
|
cs |
자바 소스코드는 그렇게 복잡할 것이 없다. 각 조건에 따른 Entity 구문을 생성하기 위한 처리 밖에 없다.
MetaJpaResponse 는 서비스에서 생성된 데이터를 return하기 위해 정의한 Class이다.
(private String modelEntity, private String modelKey가
MetaJpaVO는 SQL의 결과값을 조회하기 위해 정의한 Class이다.
(VO에 필요한 컬럼은 위의 SQL조회 컬럼을 참고하면 된다.)
소스코드가 복잡하지 않아서 그리 어렵지 않게 할 수 있을거라 생각한다.
조금이라도 JPA를 적용하는데 도움이 되었으면 한다.
'프로그래밍(JPA)' 카테고리의 다른 글
Entity Relation 시, ID Generator로 생성한 ID FK로 자동 할당하기 (0) | 2020.09.19 |
---|---|
Entity Custom ID Generator 만들기 (1) | 2020.09.06 |