43
Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자 2011 JCO 11th Conference | Session ${track_#}-${session_#} | Javacommunity.Org 강동혁 (한솔헬스케어) [email protected] Revision: 20110612

Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Embed Size (px)

Citation preview

Page 1: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Java Annotation과 MyBatis로나만의 ORM Framework을 만들어보자

2011 JCO 11th Conference | Session ${track_#}-${session_#} | Javacommunity.Org

강동혁 (한솔헬스케어)[email protected]: 20110612

Page 2: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

하기 싫은 일

Class.forName("com.mysql.jdbc.Driver");Connection c = DriverManager.getConnection();PreparedStatement stmt = c.prepareStatement

("select title, year_made from movies "+ "where year_made >= ?"+ " and year_made < ?");

for(int decadeStart = 1920; decadeStart < 2000; decadeStart += 10){stmt.setInt(1, decadeStart);stmt.setInt(2, decadeStart + 10);ResultSet rs = stmt.executeQuery();while(rs.next()){System.out.println(rs.getString(1) + " (" + rs.getInt(2) + ")");

}}

2

Page 3: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

귀찮은 일

<insert id="insertDoctor" parameterType="domain.Doctor">insert into Doctor (id,username,password,email,bio)values (#{id},#{username},#{password},#{email},#{bio})

</insert><update id="updateDoctor" parameterType="domain.Doctor">

update Doctor setusername = #{username}, password = #{password},email = #{email}, bio = #{bio}

where id = #{id}</update><delete id="deleteDoctor” parameterType="int">

delete from Doctor where id = #{id}</delete><select id="selectDoctor" resultType="domain.Doctor">

select * from Doctor</select>

3

Page 4: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

머리 아픈 일

<hibernate-mapping>

<class name="hello.Message“ table="MESSAGES">

<id name="id" column="MESSAGE_ID">

<generator class="increment"/>

</id>

<property name="text" column="MESSAGE_TEXT"/>

<many-to-one name="nextMessage" cascade="all" column="NEXT_MESSAGE_ID"/>

</class>

</hibernate-mapping>

SELECT new list(mother, offspr, mate.name)

FROM DomesticCat AS mother

INNER JOIN mother.mate AS mate

LEFT OUTER JOIN mother.kittens AS offspr4

Page 5: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

하고 싶은 일

• SQL 작성 최소화

• Object oriented programming

• 로직에 집중

• 단순한 설정 - 쉽고, 직관적

• 학습의 최소화

5

Page 6: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

ORM? Hibernate?

6

Page 7: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Hibernate + MyBatis?

7

Page 8: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

고려해야 할 점

• 설정

– object, relation 매핑

• SQL 생성

– object 를 SQL 의 파라미터로 전달

– SQL 결과를 object 로 리턴

8

Page 9: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

구현 전략

• object 와 relation 의 1:1 매핑 구현– MyBatis 를 이용하여 기본적인 insert, update,

delete, select 문을 runtime 자동 생성

– 생성된 SQL문을 MyBatis SQL repository 에저장

– Association 은 다루지 않음

– Join, subquery 구문은 MyBatis로 처리

• 설정은 Java Annotation 사용

• 일단 MySQL 먼저

9

Page 10: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Java Annotation

• 자바 5.0 에서 소개• 자바 소스 코드에 추가되는 문법적인 메타데이터• 클래스, 메소드, 변수, 파라미터, 패키지에 첨언하

여 컴파일러의 의해 .class 파일에 포함되어 컴파일혹은 런타임 시 이용

• 번거로운 설정 작업들과 반복적인 코드를 줄여줌

• Built-in Annotations– @Override, @Deprecated, @SupressWarnings

• Custom Annotations– @Controller, @Service, @Entity, @Column

10

Page 11: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Custom Annotation

• @interface 선언public @interface MyAnnotation {

String value();}

• Annotation 추가import MyAnnotation@MyAnnotation(value=“my annotation”)public void myMethod(int arg) {

// do something}

11

Page 12: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Annotation API

• Class 와 Field

– getAnnotation(Class<A> annotationClass)특정 타입에 대한 annotation 을 리턴

– getAnnotations()모든 타입의 annotation 을 리턴

– isAnnotationPresent(Class annotationClass)특정 타입에 대한 annotation 이 존재하면true 리턴

12

Page 13: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

MyBatis

• 구) iBatis

• SQL문과 객체를 매핑

• SQL문을 XML 파일에 저장

• JDBC 코드 제거

13

출처) www.mybatis.org

Page 14: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

MyBatis 예제

• Mapped Statement<mapper namespace="org.mybatis.example.HospitalMapper">

<select id="selectHospital" parameterType="int" resultType="Hospital">

select * from Hospital where id = #{id}</select>

</mapper>

• Java DAO codepublic interface HospitalMapper {

public Hospital selectHospital(int id);}HospitalMapper mapper = session.getMapper(HospitalMapper.class);Hospital hospital = mapper.selectHospital(101);

14

Page 15: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

순서

1. Table 생성2. Annotation 정의3. Mapping class 생성4. EntityManager 작성 – CRUD interface5. SQL Generation 클래스 작성

– InsertSqlSource– UpdateSqlSource– DeleteSqlSource– SelectOneSqlSource– SelectListSqlSource

15

Page 16: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

1. Table 생성

Create table hospital (

hospitalid INT NOT NULL

PRIMARY KEY AUTO_INCREMENT,

name VARCHAR(30),

regdttm DATETIME

)

16

Page 17: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

2. Annotation 정의

public @interface Table {

String value() default "";

}

public @interface Column {

String name() default "";

boolean primaryKey() default false;

boolean autoIncrement() default false;

}

17

Page 18: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

3. Mapping Class

@Table(“hospital”)Public class Hospital {

@Column(primaryKey=true,autoIncrement=true)

private Integer hospitalid;@Column private String name;@Column(name=“regdttm”)

private Date regdttm;}

18

Page 19: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

4. EntityManager

void insert(Object parameter)

void update(Object parameter)

void delete(Object parameter)

Object load(Object parameter)

List list(Object parameter)

List list(Object parameter, String orderby)

List list(Object parameter, String orderby,

int rows)

19

Page 20: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

EntityManager 사용예

EntityManager entityManager = new EntityManager();

Hospital hospital = new Hospital();hospital.setHospitalid(12345);hospital.setName(“JCO병원”);hospital.setRegdttm(new Date());entityManager.insert(hospital);

hospital = entityManager.load(hospital);

hospital.setName(“JCO병원”);entityManager.update(hospital);

entityManager.delete(hospital);

20

Page 21: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

5. SQL Generation

• INSERT– 컬럼 중 auto_increment 로 선언된 컬럼은 insert 문

에서 제외

• UPDATE– Primary Key 가 아닌 컬럼들만 update– PK로 where 조건절 생성

• DELETE– parameter 객체에서 null 값이 아닌 field 로 where 조

건절 생성

• SELECT– parameter 객체에서 null 값이 아닌 field 로 where 조

건절 생성

21

Page 22: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

5-1. Insert문 생성

• 컬럼 중 auto_increment 로 선언된 컬럼은insert 문에서 제외

<생성되는 SQL>

INSERT INTO hospital (name, regdttm) VALUES (#{name}, #{regdttm})

22

Page 23: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

EntityManager.insert()

1: Class<?> clazz = object.getClass();

2: String statementName = PREFIX_INSERT +

3: clazz.getSimpleName();

4: if (!configuration.hasStatement(statementName)) {

5: addMappedStatement(

6: statementName,

7: new InsertSqlSource(sqlSourceParser,clazz),

8: SqlCommandType.INSERT,null);

9: }

10: getSqlSession().insert(statementName, object);

23

Page 24: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

InsertSqlSource

1: List<String> columnNames =

2: AnnotationUtil.getNonAutoIncrementColumnNames(clazz);

3: String sql = String.format(

4: “INSERT INTO %1$s ( %2$s ) VALUES ( %3$s ) ",

5: AnnotationUtil.getTableName(clazz),

6: StringUtil.join(columnNames, ","),

7: StringUtil.join(columnNames, "#{%1$s}",","));

8: parse(sqlSourceParser, sql, clazz);

24

Page 25: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

AnnotationUtil.getTableName

Table t = clazz.getAnnotation(Table.class);

return t.value();

25

Page 26: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

AnnotationUtil.getNonAutoIncrementColumnNames

1: List<String> names = new LinkedList<String>();

2: for (Field field : clazz.getDeclaredFields()) {

3: if (field.isAnnotationPresent(Column.class)) {

4: Column c = field.getAnnotation(Column.class);

5: if (!c.autoIncrement())

6: names.add("".equals(c.name()) ? field.getName():c.name());

7: }

8: }

9: return names;

26

Page 27: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

5-2. Update문 생성

• Primary Key 가 아닌 컬럼들만 update

• PK로 where 조건절 생성

<생성되는 SQL>

UPDATE hospital

SET name = #{name}, regdttm = #{regdttm}

WHERE hospitalid = #{hospitalid}

27

Page 28: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

EntityManager.update()

1: Class<?> clazz = object.getClass();

2: String statementName = PREFIX_UPDATE +

3: clazz.getSimpleName();

4: if (!configuration.hasStatement(statementName)) {

5: addMappedStatement(

6: statementName,

7: new UpdateSqlSource(sqlSourceParser,clazz),

8: SqlCommandType.UPDATE,null);

9: }

10: getSqlSession().update(statementName, object);

28

Page 29: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

UpdateSqlSource

String sql = String.format(

"UPDATE %1$s SET %2$s WHERE %3$s",

AnnotationUtil.getTableName(clazz),

StringUtil.join(

AnnotationUtil.getNonPrimaryKeyColumnNames(clazz),

"%1$s = #{%1$s}", ", "),

StringUtil.join(

AnnotationUtil.getPrimaryKeyColumnNames(clazz),

"%1$s = #{%1$s}"," AND "));

parse(sqlSourceParser, sql, clazz);

29

Page 30: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

AnnotationUtil.getNonPrimaryKeyColumnNames

1: List<String> names = new LinkedList<String>();

2: for (Field field : clazz.getDeclaredFields()) {

3: if (field.isAnnotationPresent(Column.class)) {

4: Column c = field.getAnnotation(Column.class);

5: if (!c.primaryKey())

6: names.add("".equals(c.name()) ? field.getName() :

7: c.name());

8: }

9: }

10: return names;

30

Page 31: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

AnnotationUtil.getPrimaryKeyColumnNames

List<String> names = new LinkedList<String>();

for (Field field : clazz.getDeclaredFields()) {

if (field.isAnnotationPresent(Column.class)) {

Column c = field.getAnnotation(Column.class);

if (c.primaryKey())

names.add("".equals(c.name()) ? field.getName() : c.name());

}

}

return names;

31

Page 32: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

5-3. Delete문 생성

• parameter 객체에서 null 값이 아닌 field 로where 조건절 생성

예)

Hospital hospital = new Hospital();

hospital.setName(“JCO병원”);

entityManager.delete(hospital);

<생성되는 SQL>

DELETE FROM hospital

WHERE name = #{name}

32

Page 33: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

EntityManager.delete()

1: Class<?> clazz = object.getClass();

2: String statementName = PREFIX_DELETE + clazz.getSimpleName();

3: if (!configuration.hasStatement(statementName)) {

4: addMappedStatement(

5: statementName,

6: new DeleteSqlSource(sqlSourceParser,clazz),

7: SqlCommandType.DELETE,null);

8: }

9: getSqlSession().delete(statementName, object);

33

Page 34: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

DeleteSqlSource

1: public DeleteSqlSource(SqlSourceBuilder sqlSourceParser,

2: Class<?> clazz) {

3: super(sqlSourceParser);

4: staticSql = "DELETE FROM”

5: +AnnotationUtil.getTableName(clazz);

6: }

7: public BoundSql getBoundSql(Object parameterObject) {

8: String sql = staticSql + " WHERE " +

9: StringUtil.join(

10: AnnotationUtil.getNotNullColumnNames(parameterObject),

11: "%1$s = #{%1$s}"," AND ");

12: return getBoundSql(sql,parameterObject);

13: }

34

Page 35: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

5-4. Select문 생성

• parameter 객체에서 null 값이 아닌 field 로 where 조건절 생성

예)

Hospital hospital = new Hospital();

hospital.setName(“JCO병원”);

hospital = (Hospital)entityManager.load(hospital);

<생성되는 SQL>

SELECT hospitalid, name, regdttm

FROM hospital

WHERE name = #{name}

35

Page 36: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

EntityManager.load()

1: Class<?> clazz = object.getClass();

2: String statementName = PREFIX_LOAD + clazz.getSimpleName();

3: if (!configuration.hasStatement(statementName)) {

4: addMappedStatement(statementName,

5: new SelectOneSqlSource(sqlSourceParser,clazz),

6: SqlCommandType.SELECT,clazz);

7: }

8: Object result = getSqlSession().selectOne(statementName, object);

9: if (result != null)

10: BeanUtils.copyProperties(result, object);

11: return result;

36

Page 37: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

SelectOneSqlSource

1: public SelectOneSqlSource(SqlSourceBuilder sqlSourceParser, Class<?> clazz) {

2: super(sqlSourceParser);

3: staticSql = String.format("SELECT %1$s FROM %2$s ",

4: StringUtil.join(AnnotationUtil.getColumnNames(clazz),", "),

5: AnnotationUtil.getTableName(clazz));

6: }

7:

8: public BoundSql getBoundSql(Object parameterObject) {

9: String sql = staticSql + " WHERE " +

10: StringUtil.join(

11: AnnotationUtil.getNotNullColumnNames(parameterObject),

12: "%1$s = #{%1$s}"," AND ") +

13: " LIMIT 1";

14: return getBoundSql(sql,parameterObject);

15: }

37

Page 38: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

복습

1. Table 생성2. Annotation 정의3. Mapping class 생성4. EntityManager 작성 – CRUD interface5. SQL Generation 클래스 작성

– InsertSqlSource– UpdateSqlSource– DeleteSqlSource– SelectOneSqlSource– SelectListSqlSource

38

Page 39: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Effect

• 비타민MD 사이트에 일부 적용

• 만약 전체 적용한다면

69% SQL문 제거 가능

39

SQL 적용 전 적용 후

insert 112 7

update 80 2

delete 79 0

select 327 176

총 598 185

Page 40: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Future Work

• Annotation 추가

– Like 구문 지원

– DDL 상의 default 값, not null 등의 constraint 지원

• Transaction

• Caching

• Oracle, MSSQL, DB2 등 다른 DBMS 지원

40

Page 41: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Contact

http://code.google.com/p/mybatis-orm

[email protected]

41

Page 42: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

Do It Yourself and Have Fun.

42

Page 43: Java Annotation과 MyBatis로 나만의 ORM Framework을 만들어보자

이 저작물은 크리에이티브 커먼스 코리아 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.

This work is Licensed under Creative Commons Korea Attribution 2.0 License.