<!doctype html>

[Java & DataBase] JDBC, JPA, Mybatis 차이점


스프링이나 이클립스를 통한 자바 개발을 진행하는 개발자라면 데이터베이스와 연동하는 과정을 개발해 본 경험이 있을 것입니다.

그중 관계형 데이터베이스(RDBMS)를 이용할 때 JDBC, JPA, Mybatis를 이용해 볼 수 있는데 구체적으로 어떠한 차이점이 있는지 궁금증이 생겨 개념 정리 및 학습을 진행하기 위해 한번 정리해볼까 합니다.

 

순서는 제가 학습했던 순서인 JDBC -> Mybatis -> JPA 순으로 설명하겠습니다

 

1. JDBC(Java Database Connectivity)


 

JDBC는 말 그대로 자바 프로그램이 데이터베이스와 연결할 수 있는 기능을 제공하는 프로그래밍 인터페이스입니다.

 

이러한 JDBC를 이용하기 위해서는 java.sql.Driver, java.sql.Connection, java.sql.Statement, ResultSet, PreparedStatement, CallableStatement 등을 이용 볼 수 있습니다. 실제 개발을 진행할 때는 import java.sql.*; 로 한 번에 가져와서 구현했습니다.

 

간단한 순서는 다음과 같습니다.

  1. Driver Load
  2. Connector Object Create -> Connection
  3. Statement Object Create
  4. Return Value Save Result Set Object
  5. Close

 

import java.sql.*;

public class ConnectDB{
  public static void main(String[] args)
  {
    try{
      //Connection Object Create
      Connection conn = null;

      //Connection
      String url = "jdbc:mysql://localhost/DB_Name";

      conn = DriverManager.getConnection(url, "DB_user", "DB_user_password");
      System.out.println("연결 성공");
    }
    catch(ClassNotFoundException e) // Error Case 1
    {
      System.out.println("Driver Loading Fail");
    }
    catch(SQLException e) // Error Case 2
    {
      System.out.println("Error : " + e);
    }
    finally
    {
      try
      {
        if(conn != null && !conn.isClosed())
        {
          conn.close();
        }
      }
      catch(SQLException e) // Error Case 3
      {
        e.printStackTrace();
      }
    }
   }
}

이러한 불편은 점차 jdbc의 역사가 발전해나가며 SQL Mapper와 ORM을 이용할 경우 코드상 활용하는 방식이 간결해졌습니다

추가로 jdbc를 이용할 때 Driver는 DataBase회사별 다르기 때문에 "com.mysql.jdbc.Driver" 와 같이 직접 찾아서 입력해야 합니다.

 

쿼리를 요청하는 코드를 확인해보겠습니다.

 

import java.sql.*;

public class SelectCase
{
  public static void main(String[] args)
  {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    
    try{
      // 1. Drvier Load
      Class.forName("com.mysql.jdbc.Driver");
      
      // 2. Connection
      String url = "jdbc:mysql://localhost/DB_Name";
      conn = DriverManager.getConnection(url, "DB_User", "DB_Password");
      
      // 3. Statement Object Create
      stmt = conn.createStatement();
      
      // 4. Write Query
        // 1) Select 할 때 * 로 모든 칼럼을 가져오기보단 특정 컬럼을 가져오는것이 좋다.
        // 2) 원하는 결과는 쿼리로써 정리하고 후작업은 권장되지않음
        // 3) 쿼리를 한 줄로 쓰기 어려운 경우 들여쓰기를 사용해도 되지만 띄어쓰기에 유의해야한다.
      String sql = "SELECT name, owner, data_format(birth, '%Y년%m월%d일' date FROM table)";
      
      // 5. Execute Query
      rs = stmt.executeQuery(sql);
      
      // 6. 실행결과 출력하기
      while(rs.next())
      {
        //Record colunm은 1부터 시작하며 DataType에 맞게 getInt, getString등을 이용
        String name = rs.getString(1);
        String owner = rs.getString(2);
        String date = rs.getString(3);
        
        System.out.println(name + " " + owner + " " + date);
      }
    }
    catch( ClassNotFoundException e){
      System.out.println("드라이버 로딩 실패");
    }
    catch( SQLException e){
      System.out.println("에러 " + e);
    }
    finally // Close Phase
    {
      try
      {
          if( conn != null && !conn.isClosed())
          {
            conn.close();
          }
          if( stmt != null && !stmt.isClosed())
          {
            stmt.close();
          }
          if( rs != null && !rs.isClosed())
          {
            rs.close();
          }
      }
      catch( SQLException e)
      {
        e.printStackTrace();
      }
  }
}

 

 

 

물론 간결하다고 항상 좋은 것은 아니고 장, 단점이 존재합니다

위 코드는 MySQL 일 경우의 과정이고 이 JDBC 역시 Java에서 제공하는 API이기에 개발자 입장에서는 데이터베이스마다 사용 쿼리를 각 DB마다 작성해주어야 합니다(DB의존적인 쿼리문).

 

또한 연결, 연결 해제 등의 과정의 코드를 반복적으로 작성해야 하기에 코드가 많이 길어집니다.

 

이 방식도 역시나 기존 애플리케이션의 디비 연동 과정 보다도 간소화되었지만 여기서 끝이 아닌 새로운 2가지 기술이 또 등장합니다.

 

SQL Mapper , ORM

 

SQL Mapper

SQL Mapper의 대표적인 기술로는 아래에서 설명할 MyBatis가 있으며 쿼리문을 파일로 관리할 수 있다는 점에서 장점이 있습니다.

또 위 JDBC의 단점인 반복적인 코드를 줄이고 응답 결과 자체를 객체로 반환합니다.

 

ORM(Object-Relational Mapping)

ORM은 SQL문을 작성하지 않아도 동적으로 생성해주며 DB마다 다르게 사용하는 SQL도 동일하게 작성 가능합니다.

단점으로는 러닝 커브가 존재하고, SQL 작성이 없지만 테스트 과정이나 기타 여러 상황을 고려해 개발자는 알아야 합니다.

자세한 내용은 밑에 3. JPA에서 더 설명하겠습니다

 

2. Mybatis


 

  • Mybatis는 Persistence framework입니다.
  • MyBatis는 위 사용했던 JDBC를 통해 데이터베이스에 액세스 하는 작업을 캡슐화하고 일반 SQL 쿼리, 저장 프로 시저 및 고급 맵핑을 지원해 중복작업을 제거합니다.
  • 또 SQL Query문은 한 파일에 구성해서 프로그램 코드와 SQL을 분리해 개발할 수 있는 장점이 있습니다.
  • 이렇게 파일에 구성하기 때문에 복잡한 쿼리나 다이내믹한 쿼리에 강하며 단점으로는 비슷한 쿼리가 많아질 수 있는 단점이 있습니다.

 

JDBC와는 다르게 ResultType, ResultClass 등 VO를 사용하지 않고 결과를 DTO, MAP에 맵핑해 사용할 수 있습니다.

mybatis

공식 문서는 다음과 같습니다. - https://mybatis.org/mybatis-3/getting-started.html

 

 

3. JPA


아래 사진은 NoSQL인 MongoDB를 JPA인 MongoRepository를 활용하기 위해 제가 그렸던 Class Diagram입니다.

mongo

사실 간결하게 표현되어 이해하기 어려울 수 있으나 실제 공식문서를 들어가 보면 인터페이스들이 많이 엮여있는 구조로 되어있다는 걸 알 수 있습니다. (비어있는 interface도 존재합니다)

JPA는 위 Mybatis와 JDBC와는 다르게 사용 명세를 많이 참조해야 한다는 단점이 있었습니다.

개인적으로 프로젝트에 적용하기 위해 쿼리문을 작성해보고 이를 함수 명세로 표현하기 위해서 다시 JPA공식문서를 뒤져보는 작업에 시간을 많이 소요했습니다.

이러한 JPA는 익숙해지기에는 시간이 오래 걸렸으나 한번 익숙해진다면 개발 속도는 빨랐기에 저도 가장 많이 사용해보았습니다.

이런 JPA의 특징으로는 ORM 기술 표준의 인터페이스 모음이며 인터페이스이기 때문에 Hibernate, OpenJPA 등이 JPA를 구현합니다.

또 프로젝트의 규모가 커질 경우 속도 저하, 일관성을 무너뜨리는 문제점, 학습 비용이 비싼 점 등이 존재합니다.

추가로 제가 구현할 때 중첩 쿼리문처럼 복잡할 경우에는 @Query 어노테이션으로 직접 Query를 작성할 수 있어 매우 유연한 구현 방식을 제공하고, 팀원들과 협업 시에도 JPA를 이용하기 위한 함수 네이밍 기준이 있어 가독성도 좋았습니다.

 

코드를 하나씩 다 쳐보며 설명하고 싶으나 시간적인 여유가 없어 현재는 특징 정리 정도로만 하고 추후 재 업데이트를 진행할 예정입니다

 

Reference


 

 

'JAVA' 카테고리의 다른 글

[Java] 언어의 특징  (2) 2022.09.09
[JAVA]직렬화 - Serializable란?  (0) 2022.05.16
[JAVA]DAO, DTO, VO 정리.  (0) 2021.12.25

[JAVA]JAVA 언어의 특징


자바언어를 이용한 자바 프로그래밍을 생각해보면 객체지향 프로그래밍 OOP(Object Oriented Programming)이라 해도 상관없습니다. 그 이유는 자바언어의 특징을 소개하며 설명을 이어나가겠습니다.

자바 언어는 크게 5가지 특징을 가지는데 아래와 같습니다

  • 객체 지향 프로그래밍(OOP)
  • 자동 메모리 관리(GC)(가비지 컬렉터)
  • 운영체제에 독립적
  • 멀티쓰레드 지원
  • 동적 로딩 지원

첫번째로 객체 지향 프로그래밍의 대표적인 특징은 아래 4가지가 존재합니다.

  • 상속 (Inheritance) : 부모클래스의 속성과 기능을 그대로 이어받아 사용할 수 있게 하고 기능의 일 부분을 변경해야 할 경우 상속받은 자식클래스에서 해당기능을 재정의(수정)해 사용할 수 있는 것입니다.

  • 다형성 (Polymorphism) : 다형성은 상속을 통해 기능을 확장하거나 변경하는 것을 가능하게 해줍니다. 이를 통해 코드의 재사용, 코드길이 감소, 유지보수가 용이해지는 특징이 있습니다. 여기서 크게 두가지 개념으로 오버라이딩(Overriding)과 오버로딩(Overloading)이 있습니다.

    • /*
      **오버라이딩 (Overriding)
      ** - 상위 클래스가 가지고 있는 메소드를 하위 클래스가 재정의해서 사용합니다.
      */
      
      //부모클래스
      class Parent{
        public String name;
        public int age;
        public void Speak(){
          System.out.println("일어나 ~ 학교 가야지 ~");
        }
      }
      //자식클래스
      class Child extends Parent{
        public String name;
        public int age;
        public void Speak(){
          System.out.println("10분만 더 잘게요 ~ ");
        }
      }
      public class Test{
        public static void main(String[] args){
          //자식 클래스의 객체 생성
          Child ch = new Child();
      
          //자식 객체의 변수 설정
          ch.name = "아무개";
          ch.age = 10;
      
          //오버라이딩 함수 호출
          ch.Speak();
        }
      }
      /*
      **오버로딩 (Overloading)
      ** - 같은 이름의 메서드 여러개를 가지면서 매개변수의 유형과 개수가 다르도록 하는 기술
      */
      
      class marine
      {
        public void attack(){
          System.out.println("일어나 ~ 학교 가야지 ~");
        }
        public void attack(int damage){
          System.out.println(damage + "만큼의 피해를 입힙니다.");
        }
        public void attack(String class_name, int damage){
          System.out.println(class_name+"이"+damage"만큼의 피해를 입힙니다.");
        }
      }
      public class Test{
        public static void main(String[] args){
          //marine 클래스의 객체 생성
          marine m = new marine();
      
          //매개변수가 없는 attack 메소드 호출
          m.attack();
      
          //매개변수가 1개 있는 attack 메소드 호출
          m.attack(10);
      
          //매개변수가 2개 있는 attack 메소드 호출
          m.attack("warrior", 30);
        }
      }
  • 캡슐화 (Encapsulation, information Hiding) : 캡슐화는 한 객체에 대해 그 객체가 특정 목적을 위해 필요한 변수나 메소드를 하나로 묶는것을 의미합니다. 즉 접근제한자로 변수를 설정해 함부로 변수의 값을 변경할 수 없게 만들고 대신 getter, setter와 같은 메소드를 통해서 접근할 수 있도록 구현할 수 있습니다.

  • 추상화 (Abstraction) : 공통의 속성, 기능들을 묶어 클래스로구성해 확장성을 넓히는 것을 의미합니다. 추상클래스(추상 메소드를 하나이상 포함한 클래스)를 구성하고 추후의 이 class 또는 interface를 상속받아 구현하여 사용하는 것입니다. 공통 기능으로 묶어서 처음 클래스 또는 인터페이스를 구성했기 때문에 설계에 따라서 확장성이 크게 올라갈 수 있습니다. ex)동물이라는 추상 클래스는 행동 이라는 추상 메소드를 가지고 있고 추후에 양서류 등등으로 나뉘어지더라도 이 행동이라는 추상메소드는 계속 상속받아 각 종류에따라서 다르게 구현될 수 있습니다.

그렇다면 이러한 객체지향 프로그래밍을 쓰는 이유는 무엇인가요? 라는 궁금증이 생길 수 있습니다.

객체지향 프로그래밍은 프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법입니다.

장단점은 무엇이 있을까? 생각해볼 수 있습니다.

  • 장점
    • 코드 재사용성 증가 - 상속을 통한 확장성 증가
    • 유지보수가 쉬워짐 - 절차 지향 프로그래밍 에서는 코드를 수정해야할 때 하나하나 수정해야하지만 객체지향프로그래밍은 상속받은 부모 클래스의 내용만 수정하면 하위 상속받은 자식클래스의 코드수정을 하지 않아도 됩니다.
    • 대형 프로젝트에 적합 - 클래스 단위로 모듈화시켜 개발할 수 있기 때문에 업무분담하기 쉬워집니다.
  • 단점
    • 처리속도가 상대적으로 느려집니다.
    • 객체가 많으면 용량이 커질 수 있습니다.
    • 설계시 많은 시간과 노력이 필요합니다.

두번째로 자동메모리관리(GC) 가비지 컬렉션을 설명하겠습니다.

Garbage collection(GC)는 메모리관리 기법 중 하나로 프로그램이 동적으로 할당했던 메모리 영역 중 필요없게 된 영역을 해제하는 기능입니다.

C,C++언어같은 경우 동적할당을 받아 배열또는 객체를 생성하게 될 경우 힙메모리 영역에 저장되고 이 부분의 메모리가 할당된 걸 해제하기위해 개발자가 직접 free해주어야합니다. 그러나 자바언어의 경우에는 모든 객체가 클래스기반이며 참조변수 같은경우에는 기본으로 힙에 저장되기때문에 애초에 힙에 저장되는 값들이 많습니다. 이러한 값들을 주기적으로 관리해주기위해 GC가 존재하기도하며 직접 개발자가 System.gc() 를 호출해서 해제할 수 있으나 권장되지 않는것으로 알고있습니다.

세번째로는 운영체제에 독립적입니다.

이말의 의미를 명확하게 이해하려면 JVM(Java Virtual Machine)을 이해하고 있어야 합니다. 자바의 개발 환경과 배포환경이 다를 경우 프로그램을 다시 컴파일 할 필요 없이 실행가능합니다. 그 이유는 JVM은 별도의 Java Compiler를 통해 사용자의 코드를 Byte코드로 변환하고 모든 자바 프로그램은 이론적으로 CPU나 운영체제의 종류와 무관하게 동일하게 동작합니다.

마지막으로 동적로딩, 멀티쓰레드 지원 입니다.

자바는 한 프로그램 내에서 여러 쓰레드가 동작할 수 있는 환경을 제공합니다. C,C++은 운영체제의 도움을 받아 멀티쓰레드를 지원하지만 자바는 이런 운영체제의 지원없이 멀티쓰레드를 이용한 프로그래밍이 가능합니다.

멀티쓰레드를 구현하는 방식음 다음과 같이 두가지가 있습니다.

1) Thread Class (화이트박스 방식)

  • 자바에서 쓰레드를 만들기위해 Thread Class 를 상속받아 쓰레드를 생성합니다.
    2) Runnable Interface (블랙박스 방식)
  • 위 화이트박스 방식과 비슷하게 Thread class와 같이 자바에서 스레드를 실행시키는 인터페이스 이며 다중상속이 지원되기 때문에 Thread class보다 많이 사용됩니다.

추가로 저는 C,C++에서 철학자 문제를 가지고 멀티쓰레드와 멀티 프로세스개념을 이해하며 학습한 적이 있는데 이러한 멀티쓰레드와 멀티프로세싱은 교착상태(데드락)문제를 중요하게 다루고있습니다. 여러개의 쓰레드 또는 프로세스가 공유자원을 이용할 때의 문제점을 다루는 것인데 그당시는 뮤텍스, 세마포어를 활용해 이러한 공유자원문제를 해결하는 방식을 학습했던 적이 있었습니다. 중요한 개념인 만큼 밑에 깃허브 링크를 남겨두겠습니다.

지금은 이런게 있구나 기억하고 넘어가시는게 좋을 것 같습니다.

철학자 문제 - https://github.com/ukjinlee66/42_Philosophers

동적로딩(Dynamic Loading)은 어플리케이션이 실행될 때 모든 객체가 처음부터 생성되지 않고 필요한 시점에서 동적로딩을 통해 생성되는 개념입니다. 이러한 동적 로딩은 클래스를 일부 변경하더라도 재 컴파일을 하지 않아도 되는 장점이 있고 적은 작업으로 처리할 수 있는 유연성을 동적로딩이 제공합니다. 그러나 말 그대로 시점에 따라서 그때그때마다 생성이된다면 당연히 그 시점에 메모리에서 불러오기에 속도가 정적로딩에 비해 느린 편이고 이런 느린 속도를 해결하기위해 Static 키워드를 사용합니다.

Reference

'JAVA' 카테고리의 다른 글

[JAVA & Database] JDBC, MyBatis, JPA 의 차이점  (1) 2022.09.26
[JAVA]직렬화 - Serializable란?  (0) 2022.05.16
[JAVA]DAO, DTO, VO 정리.  (0) 2021.12.25

JAVA-직렬화란?


Serializable(직렬화)

  • 직렬화(Serializable)는 무엇인가?
    • 직렬화는 자바 시스템 내부에서 사용되는 객체 또는 데이터들을 외부 자바 시스템에서도 사용할 수 있도록 바이트(byte)형태로 데이터를 변환하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 역직렬화를 포함합니다.
    • 시스템적으로 JVM의 Runtime Data Area(Heap and Stack Area)에 상주하고 있는 객체 데이터를 바이트 형태로 변환하는 기술과 직렬화된 바이트 형태의 데이터를 객체로 변환해 JVM으로 상주시키는 형태를 말하기도 합니다.
  • 그렇다면 이런 직렬화는 어떤 경우에 사용되는 것일까?
    • 생성한 객체를 가지고 파일에 저장할 때
    • 저장한 객체를 읽어올 때
    • 다른 서버에서 생성된 객체를 받을 때

즉 우리가 만든 클래스가 파일에 읽거나 쓰고, 다른 서버로 보내거나 받을 경우 반드시 이 직렬화를 이용하기 위해 Serializable interface를 implements해주어야 합니다.

public class Temp implements Serializable{
  //다음과 같은 방식으로 implements하여 사용한다.
}

이러한 Serializable interface를 구현하는 클래스들을 확인해보면 serialVersionUID라는 값이 존재합니다.

이 serialVersionUID는 필수값은 아니지만 호환 가능한 클래스는 serialVersionUID 값이 고정되어있습니다.

serialVersionUID가 선언되어 있지 않으면 클래스는 기본 해쉬값을 사용합니다.

public class HashMap<K, V> extends TempMap<K,V>
  implementes Map<K,V>, Serializable{
  private static final long serialVersionUID = 123456789L;
}

이런식으로 static final long 으로 선언하고 변수명 역시 serialVersionUID로 선언해야 자바에서 인식합니다.

이러한 UID의 값은 언제 사용될까요?

처음 Serializable의 정의쪽을 기억해봅시다. "다른 서버로 보내거나 받을 경우"어떠한 클래스를 보내고자 할 때 받는 쪽 서버에도 역시나 동일한 클래스를 가지고 있을겁니다. 그러나 새로받은 클래스와 원래 존재했던 클래스가 단순히 이름만을 가지고 자바가 같다고 파악하지 않고 위에서 설명한 이 serialVersionUID를 통해 버전을 확인하고 두 버전이 동일 할 경우에만 같은 클래스로 인식하게 됩니다. 또한 UID가 같더라도 클래스 내부 변수의 개수나 타입등이 다르다면 이 경우에도 다른 클래스라 인식합니다.

Reference

'JAVA' 카테고리의 다른 글

[JAVA & Database] JDBC, MyBatis, JPA 의 차이점  (1) 2022.09.26
[Java] 언어의 특징  (2) 2022.09.09
[JAVA]DAO, DTO, VO 정리.  (0) 2021.12.25

액션태그(Action Tag)란?

- 액션태그란 JSP페이지 내에서 어떤 동작을 하도록 지시하는 태그 입니다. 예를 들어 페이지 이동, 페이지 include, 자바 빈 생성 등이 있다.

대표적인 액션 태그로는 다음 3가지가 있다.

1. forward

- forward는 현재 페이지를 다른 페이지로 전환할 때 사용한다.

- JSP컨테이너는 현재 JSP페이지에서 forward 액션 태그를 만나면 그 전까지 출력 버퍼에 저장되어 있던 내용을 폐기하고 forward 액션 태그에 명시된 페이지로 제어권이 넘어간다.

forward를 사용하는 방법은 다음과 같다.

<jsp:forward page="파일명"/> <!-- 파일명에 명시된 곳으로 포워딩이 이루어진다 -->
OR
<jsp:forward page="파일명"></jsp:forward>

2. include

- include Action Tag는 jsp페이지의 특정 영역에 외부 파일의 내용을 포함하는 태그이다.

- 현재 JSP페이지에 포함할 수 있는 외부파일은 HTML, JSP, Servlet 등이 있다.

다음과 같은 흐름에서 First.jsp파일 내부 jsp include Action Tag 를 이용해 다른 jsp 페이지를 요청한다.

 

3. param

- param Action Tag는 위 include Action Tag와 함께 사용되며 다른 페이지를 include 를 통해 호출 하는 시점에 넘겨주고 싶은 값을 명시할 때 쓰입니다.

- param Action Tag는 단독 사용이 불가능하며, forward, include Tag내부에 사용됩니다.

- 만약 전달해야하는 정보가 많다면 여러번의 param Tag를 포함하여 정보를 전달할 수 있습니다.

<jsp:forward page="파일명">
	<!-- forward Tag 내부 전달할 정보를 표기한다. -->
	<jsp:param name="변수이름" value="변수값"/>
    <jsp:param name="변수이름2" value="변수값2"/>
</jsp:forward>

 

 

+ Recent posts