orm을 맞이하는 우리의 자세
TRANSCRIPT
ORM 을 맞이하는 우리의 자세
정병태
랑겔한스 : 정병태
- Web Developer
- OKKY.KR Contributor
- https://github.com/1angerhans - http://okky.kr/user/info/26138 - [email protected]
‘나는 SQL 이 싫어요.’
ORMObject Relational Mapping
– Wikipedia 인용
“Object-relational mapping (ORM, O/RM, and O/R mapping) in computer science is a programming technique for converting data between
incompatible type systems in object-oriented programming languages.”
– Wikipedia 인용
“객체 관계 매핑(Object-relational mapping; ORM)은 데이터베이스와 객체 지향 프로그래밍 언어 간의 호환되지 않는 데이터를 변환하는 프로그래밍 기법이다.”
Object-relational impedance mismatch
- Data type differences - Structural and integrity differences - Manipulative difference - Transactional differences
https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch
문제의 해결 Without ORM
현실의 문제 #1
Vo, DTO 그리고 Table들
D.D.DDomain Driven Design
Domain Entity 설계
CREATE TABLE TBL_MSG ( F_GRP_ID INT(6) NOT NULL PRIMARY KEY , PTN_ID INT(6) , PRIORITY INT(6) , JOB_CODE INT(6) , FLAG_PRO INT(6) , CHAINNUM INT(6) , MASSNUM INT(6) , USERID VARCHAR(20) , CPCODE VARCHAR(20) , QTCODE VARCHAR(20) , MSGSEPER CHAR(1) , MSG_CNT INT(6) , CNTPSEC INT(6) ,
흔한 Legacy Table
Minimization
CREATE TABLE TBL_USER (
SEQ INT(6) NOT NULL PRIMARY KEY ,
USER_TYPE VARCHAR(20) ,
AUTH_TYPE VARCHAR(20) ,
USERID VARCHAR(20) ,
NAME VARCHAR(20) ,
PASS VARCHAR(20) ,
TEL VARCHAR(12) ,
CELL VARCHAR(12) ,
OFFICE_TEL VARCHAR(12) ,
EMAIL VARCHAR(200) ,
ZIPCODE VARCHAR(7) ,
ADDRESS_01 VARCHAR(200) ,
ADDRESS_02 VARCHAR(200) ,
COMPANY_NAME VARCHAR(20) ,
QUARTER_NAME VARCHAR(20) ,
STATUS VARCHAR(20) ,
MILEAGE INT(12) ,
REG_DATE DATE ,
PASS_CHG_DATE DATE ,
PASS_FAIL_CNT INT(6)
);
User
Contact
Company
Domains
Auth
Mileage
CREATE TABLE TBL_USER (
SEQ INT(6) NOT NULL PRIMARY KEY ,
USER_TYPE VARCHAR(20) ,
USERID VARCHAR(20) ,
NAME VARCHAR(20) ,
PASS VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE ,
PASS_CHG_DATE DATE ,
PASS_FAIL_CNT INT(6)
);
CREATE TABLE TBL_COMPANY (
SEQ INT(6) NOT NULL PRIMARY KEY ,
COMPANY_NAME VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE
);
CREATE TABLE TBL_AUTH (
SEQ INT(6) NOT NULL PRIMARY KEY ,
ROLE VARCHAR(20)
);
CREATE TABLE TBL_ADDRESS (
ZIPCODE VARCHAR(7) ,
ADDRESS_01 VARCHAR(200) ,
ADDRESS_02 VARCHAR(200)
);
CREATE TABLE TBL_CONTACT (
SEQ INT(6) NOT NULL PRIMARY KEY ,
TEL VARCHAR(12) ,
CELL VARCHAR(12)
);
CREATE TABLE TBL_MILEAGE (
SEQ INT(6) NOT NULL PRIMARY KEY ,
POINT INT(12)
);
public class User { Long seq; String userid; String userpass; String username; Date regDate;
Company company; Address address; Contact contact; Mileage mileage;
List<Auth> authorities;
// Getter & Setter }
public class Company { … }
public class Address { … }
public class Contact { … }
public class Mileage { … }
public class Auth { … }
현실의 문제 #2
DBA
CREATE TABLE TBL_USER (
SEQ INT(6) NOT NULL PRIMARY KEY ,
USER_TYPE VARCHAR(20) ,
USERID VARCHAR(20) ,
NAME VARCHAR(20) ,
PASS VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE ,
PASS_CHG_DATE DATE ,
PASS_FAIL_CNT INT(6)
);
CREATE TABLE TBL_COMPANY (
SEQ INT(6) NOT NULL PRIMARY KEY ,
COMPANY_NAME VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE
);
CREATE TABLE TBL_AUTH (
SEQ INT(6) NOT NULL PRIMARY KEY ,
ROLE VARCHAR(20)
);
CREATE TABLE TBL_ADDRESS (
ZIPCODE VARCHAR(7) ,
ADDRESS_01 VARCHAR(200) ,
ADDRESS_02 VARCHAR(200)
);
CREATE TABLE TBL_CONTACT (
SEQ INT(6) NOT NULL PRIMARY KEY ,
TEL VARCHAR(12) ,
CELL VARCHAR(12)
);
CREATE TABLE TBL_MILEAGE (
SEQ INT(6) NOT NULL PRIMARY KEY ,
POINT INT(12)
);
DBA 들이 나를 향해 이렇게 외친다.
public class MSG {
int grpnum; int chainnum; String userid; String usernm; String cpcode; String qtcode; String title; String sndTitle; String cComment; String dstaddr; String callback; String msgTemplet; String reservData;
흔한 Legacy Model
Mybatis Association
<select id="selectOne" resultMap=“userMap">
select u.seq, u.userid, u.pass, u.name, u.tel, u.cel, u.email, u.company_name, u.zipcode, u.address_01, u.address_02 …. from tbl_user u where u.seq = #{seq}
</select>
Mybatis
Mybatis Association<resultMap id="userMap" type=“User">
<id jdbcType="INTEGER" property="seq" column="seq"/> <result jdbcType="VARCHAR" property="userid" column="userid"/> <result jdbcType="VARCHAR" property="pass" column="pass"/>
<association property="company" javaType="Company" > <result jdbcType="VARCHAR" property=“company_name" column="cpname"/> </association>
<association property="contact" javaType="Contact" > <result jdbcType="VARCHAR" property=“company_name" column="cpname"/> </association>
<association property="address" javaType="Address" > <result jdbcType="VARCHAR" property=“zopcode" column=“zopcode"/> <result jdbcType="VARCHAR" property=“address_01” column=“address_01"/> <result jdbcType="VARCHAR" property=“address_01” column="address_01"/> </association>
<collection property="authorities" select="selectUserAuthList" fetchType="lazy" column="{seq=seq}" />
</resultMap>
– 미상
“인간의 욕심은 끝이 없고, 같은 실수를 반복한다.”
With ORM
Hibernate
@Entity@Table( name = “TBL_USER" )public class User { Integer seq; String userid; String userpass; String username; Date regDate;
Company company; Address address; Contact contact; Mileage mileage;
List<Auth> authorities;
// Getter & Setter
@Id@GeneratedValue(generator="increment")@GenericGenerator(name="increment", strategy = “increment”)@Column(name = “SEQ”)
Long getSeq() { return seq;
}
}
@Entity@Table( name = “TBL_COMPANY" )public class Company { … }
@Entity@Table( name = “TBL_ADDRESS" )public class Address { … }
@Entity@Table( name = “TBL_CONTACT" )public class Contact { … }
@Entity@Table( name = “TBL_MILEAGE" )public class Mileage { … }
@Entity@Table( name = “TBL_AUTH" )public class Auth { … }
CREATE TABLE TBL_USER (
SEQ INT(6) NOT NULL PRIMARY KEY ,
USER_TYPE VARCHAR(20) ,
USERID VARCHAR(20) ,
NAME VARCHAR(20) ,
PASS VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE ,
PASS_CHG_DATE DATE ,
PASS_FAIL_CNT INT(6)
);
CREATE TABLE TBL_COMPANY (
SEQ INT(6) NOT NULL PRIMARY KEY ,
COMPANY_NAME VARCHAR(20) ,
STATUS VARCHAR(20) ,
REG_DATE DATE
);
CREATE TABLE TBL_AUTH (
SEQ INT(6) NOT NULL PRIMARY KEY ,
ROLE VARCHAR(20)
);
CREATE TABLE TBL_ADDRESS (
ZIPCODE VARCHAR(7) ,
ADDRESS_01 VARCHAR(200) ,
ADDRESS_02 VARCHAR(200)
);
CREATE TABLE TBL_CONTACT (
SEQ INT(6) NOT NULL PRIMARY KEY ,
TEL VARCHAR(12) ,
CELL VARCHAR(12)
);
CREATE TABLE TBL_MILEAGE (
SEQ INT(6) NOT NULL PRIMARY KEY ,
POINT INT(12)
);
DBA 들이 나를 향해 이렇게 외친다.
CREATE TABLE TBL_USER (
SEQ INT(6) NOT NULL PRIMARY KEY ,
USER_TYPE VARCHAR(20) ,
AUTH_TYPE VARCHAR(20) ,
USERID VARCHAR(20) ,
NAME VARCHAR(20) ,
PASS VARCHAR(20) ,
TEL VARCHAR(12) ,
CELL VARCHAR(12) ,
OFFICE_TEL VARCHAR(12) ,
EMAIL VARCHAR(200) ,
ZIPCODE VARCHAR(7) ,
ADDRESS_01 VARCHAR(200) ,
ADDRESS_02 VARCHAR(200) ,
COMPANY_NAME VARCHAR(20) ,
QUARTER_NAME VARCHAR(20) ,
STATUS VARCHAR(20) ,
MILEAGE INT(12) ,
REG_DATE DATE ,
PASS_CHG_DATE DATE ,
PASS_FAIL_CNT INT(6)
);
User
Contact
Company
Domains
Auth
Mileage
@Entity@Table( name = “TBL_USER" )public class User {
@Id@GeneratedValue(generator="increment")@GenericGenerator(name="increment", strategy = “increment”)@Column(name = “SEQ”)
Integer seq;
@Column(name = “USERID”) String userid;
@Embedded
Company company;
@Embedded
Address address;
@Embedded
Contact contact;
@OneToOne( cascade = CascadeType.ALL, fetch = FetchType.LAZY) Mileage mileage;
@OneToMany( cascade = CascadeType.ALL, fetch = FetchType.LAZY) List<Auth> authorities;
}
@Embeddable
public class Company { … }
@Embeddable
public class Address { … }
@Embeddable
public class Contact { … }
@Embeddable
public class Mileage { … }
@Embeddable
public class Auth { … }
Criteria
Criteria
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("seq", seq))
.setProjection(
Projections.projectionList()
.add(Projections.property(“zipcode"))
.add(Projections.property(“address_01"))
.add(Projections.property(“address_02")))
.setResultTransformer(
Transformers.aliasToBean(Address.class));
Address address = criteria.uniqueResult();
HQL
HQL
Address address = session.createQuery(“select u.zipcode,
u.address_01, u.address_02 from User u where seq = :seq”, )
.setParameter("seq", seq)
.setResultTransformer(
Transformers.aliasToBean(Address.class))
.uniqueResult();
ORM 에 대한 단상
• Legacy DB 도 충분히 도입 가능하다.
• ORM을 염두한 디자인을 적용할 경우 생산성이 급격히 좋아진다.
• Criteria는 학습이 쉽지 않다.
• HQL을 쓸 때면 뭔가 손해보는 기분.. (방언 처리 라고는 하지만..)
• 퍼포먼스는 사실상 문제 없다.
• SQL Layer가 빠져야돼.
ORM 은 우리가 가지고 있는 문제를 해결한
‘결과’가 아닌
더욱 나은 방향으로 가기 위한 ‘과정’ 입니다.
– 저
No SQL
SQL이 필요 없다고 RDBMS가 필요 없는건 아닐껍니다.
공유의 시간