본문 바로가기
Spring/이론

Hibernate

by 모스키토끼 2020. 4. 10.

Data Persistence

  • 객체들을 영구적으로 보관하는 것
  • Web Application 관점에서는 객체지만 Database에서는 이러한 객체들이 테이블로 관리된다.

DB에 데이터를 유지하는 방법

  1. JDBC
  2. ORM
    - 객체와 객체 사이는 의존성이 존재(의존성 주입)
    객체지향 언어를 사용하면 그래프 형태로 객체를 관리할 수 있고
    Relational database systems에서는 테이블 형태로 관리할 수 있다.
    • 객체지향 언어와 DB를 같이 사용하면 객체 모델과 관계 모델에 불일치가 발생할 수 있다
    • 그 부분을 ORM framework의 Hibernate가 해결해준다.(Framework을 사용하여 불일치를 해결)

Mismatch가 발생되는 경우!

  • 객체지향언어에서 클래스가 2개이지만 RDBMS에서는 1개인 경우가 있다.
  • 객체지향 언어에서는 상속 개념이 있지만 RDBMS에는 상속 개념이 없다.

Association

  • Java의 객체 참조
    - reference는 방향성이 존재 -> 양 방향을 설정하기 위해서는 reference를 두 번 사용하여 association을 표현한다. 
  • RDBMS의 외래 키
    - RDBMS는 foreign key를 사용하여 association을 표현한다.
    => foreign키 자체로는 방향성이 있지만 foreign키를 바탕으로 join을 하면 방향성이 없어진다. 

※ 쿼리값으로 요청한 query를 던져주면 hibernate가 알아서 sql문을 생성하여 실행한다.

 

- One to One Relationship

=> 1:n의 특별한 경우가 1:1인 것

 

한 학생에 한 Address 존재

 

  • 클래스 관계 설정
    - 자료형으로 Address를 사용한 멤버 변수를 추가하여 참조 관계 형성
public class Student {

    private long studentId;
    private String studentName;
    private Address studentAddress;
}


public class Address {

    private long addressId;
    private String street;
    private String city;
    private String state;
    private String zipcode;
}

결과

 

DBMS상에서 일대일 관계로 student 테이블과 address 테이블이 만들어짐

 

- One to Many Relationship

 

한 학생은 여러 개의 Phone을 갖을 수 있다.

 

  • 클래스 관계 설정(2가지 방법)
    1. Phone 클래스에 Student 클래스를 만들어 참조 관계 형성
    2. Student 클래스에 Set 자료형을 사용하여 Phone 참조
      -
      객체 안에 set이나 array와 같은 변수를 설정해주면 테이블에서는 Join Table로서 별로의 테이블을 만들어 관리한다. 
//1번 방법
public class Student {

    private long studentId;
    private String studentName;
}


public class Phone {

    private long phoneId;
    private String phoneType;
    private String phoneNumber;
    private Student student;
}
//2번 방법
public class Student {

    private long studentId;
    private String studentName;
    private Set<Phone> studentPhoneNumbers ;
    …
}


public class Phone {

    private long phoneId;
    private String phoneType;
    private String phoneNumber;
    …
}

결과

 

학생 테이블
Phone 테이블
set으로 생겨난 Join Table

Hibernate

  • Java를 위한 ORM 솔루션
  • 객체를 테이블에 매핑해준다.
  • GPL의 rule를 완화시킨 LGPL이다.
    (LGPL: open source를 사용하여도 프로그램의 소스를 공개 안 해도 된다.)

Application Architecture

  • application은 단일 Configuration을 생성
  • SessionFactory의 단일 인스턴스를 빌드 한 다음 client request를 서비스하는 스레드에서 세션을 인스턴스 화한다.

  • Application Logic ---data request---> DAO ---Hiberante API---> Hibernate---JDBC API---JDBC API---> JDBC 
  • 이제는 Hibernate가 ResultSet를 domain object(도메인 객체)로 매핑을 해주어 DAO로 넘겨준다.
  • Application Logic -> User Interface로 객체를 넘겨줄 때 전부 넘겨주는 것이 아니라 필요한 것만 넘겨준다
    : data Xfer object(data transfer object, 필요한 것만 넘겨준다면 테스트할 때 용의 하다).
  • Hibernate는
    - SessionFactory,
    - hibernate.cfg.xml(= jdbc.property와 같은 역할을 해다.),
    - *. hbm.xml
     class mappings(객체에 annotation을 달거나 xml 설정, default값으로 할 수도 있고 내가 설정할 수도 있다.) 
    이 세 가지를 설정해 주어야 한다. 

domain object 중에서 저장하려고 하는 object를 Persistent Object라고 부른다.

 

1. Configuration Object

  • Configuration이라는 클래스가 Hibernate Mapping(*. hbm.xml.hbm.xml class mappings)와 Hibernate Config(*hibernate.cof.xml)를읽어 들이고  Session Factory를 만든다.
  • Session Factory는 Session을 만들어 JDBC에 접근한다.(Session에는 Transaction을 활용한다.)
    -> (같은 개념) Web app과 DB Sever 간에 DB Session을 만들어 DB와 연동이 가능하다.
  • database connection과 mapping을 이용하여 SessionFactory를 생성
  • JDBC는 Hibernate에 속한 것은 아니다.

hibernate.cfg.xml 예시

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM 
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
   <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
   <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
   <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/myDB</property>
   <property name="hibernate.connection.username">root</property>
   <property name="hibernate.connection.password">1234</property>

   <!-- show mysql queries output in console -->
   <property name="hibernate.show_sql">true</property>

   <!-- manage automatic database creation -->
   <property name="hibernate.hbm2ddl.auto">create</property>

   <!-- mappings for annotated classes -->
   <mapping class="testHibernate.Category"/>
   <mapping class="testHibernate.Product"/>
  </session-factory>
</hibernate-configuration>
  • hibernate.dialect : 어떤 DB를 사용할 것인지 설정하는 부분
  • hibernate.show_sql : hibernate가 생성하는 sql를 console에서 볼 것인지 아닌지 설정하는 부분
  • hibernate.hbm2ddl.auto : 애플리케이션을 실행시킬 때마다 기존에 있는 것을 지우고 db와 table를 새롭게 만든다.(create라고 설정하면, 개발하는 경우에 주로 사용), update라고 주면 지워지지 않음 

mapping class 예시

@Entity
public class Product {

  @Id
  @GeneratedValue
  @Column(name=“product_id”)
  private int id;

  private String name;

  private int price;

  private String description;

  @ManyToOne(cascade=CascadeType.ALL)
  @JoinColumn(name="category_id")
  private Category category;
}
  • ★Class를 Table로 mapping을 하고 싶다면 반드시 @Entity라는 annotation을 붙여야 한다.
  • 내가 primary키 값인 id를 부여하는 것이 아니라 Hibernate에게 시킨다면 @GeneratedValue를 사용하여 자동생성을 한다.
  • @Column으로 table에 들어갈 속성 이름을 바꿀 수 있다.
  • 외래키를 설정하려고 한다면 포인터를 사용(객체를 넣어줌)하여 참조시킨다.
    ->@ManyToOne, JoinColumn을 사용해주어야 한다. 

2. SessionFactory Object

  • session 생성
  • SessionFactory Object는 Application에서 하나만 만들어진다.(싱글톤)
    -> SessionFactory는 Spring IOC Container에서 만들어준다.
  • request가 들어올 때마다 Thread를 만들고 여러 개가 들어오면 멀티 쓰레딩이 된다.
  • 스레드들은 SessionFactory로 들어가 공유자원을 사용하는데 상호 배제가 필수다.
    -> SessionFactory는 상호배제가 잘 되어있어서 멀 티쓰 레딩 환경에서도 잘 돌아간다. (Thread safe object)
  • Session Factory는 무거운 객체기 때문에 application이 시작할 때 만들어지고 계속 사용.

SessionFactory Object

  • SessionFactory.openSession()
    -항상 새로운 세션을 연다.
    -Spring famework이 session의 close와 flush를 해주기 때문에 따로 안해줘도 된다.
  • SessionFactory.getCurrentSession()
    - Context에 바인딩된 세션을 반환
    - 처음 호출 시에 새로운 세션이 열린다.

예시 코드

public class PersonService { 

     @Authowired
     private SessionFactory  sessionFactory;

     public Person edit(Integer id, String firstName, 
                            String lastName, Double money) {

        Session s= sessionFactory.openSession();
        Transaction tx= s.beginTransaction();

        Session session = sessionFactory.getCurrentSession();        Person person = (Person) session.get(Person.class, id);
        person.setFirstName(firstName);
        person.setLastName(lastName);
        person.setMoney(money); 

        session.update(person);

        tx.commit();
        return person;
}


@Transactional

public class PersonService { 

    @Authowired
    private  SessionFactory sessionFactory;

     public Person edit(Integer id, String firstName, 
                            String lastName, Double money) {

        Session session = sessionFactory.getCurrentSession();
        Person person = (Person) session.get(Person.class, id);
        person.setFirstName(firstName);
        person.setLastName(lastName);
        person.setMoney(money); 

        session.update(person);

        return person;
}
  • Container에 의해서 SessionFactory에 주입이 일어나고.....
  • session.get(Person.class, id) 디비에 접근에서 id값을 primary로 가진 data를 가져와 매핑하여 person에 넣어준다.
  • session.update(person)를 하면 중간에 수정된 값들이 db에 적용된다.
  • @Transactional annotation을 사용하면 sessionOpen과 transaction시작 그리고 transaction commit 코드를 생략해도 된다. --> Before adviceafter advice가 @Transactional annotation(AOP annotation)을 사용하면 자동으로 삽입.

3. Session Object

  • application과 database 간의 "대화"를 나타낸다.
  • database와 physical connection 하는 데 사용
  • 일반적으로 스레드 안전하지 않기 때문에 오랫동안 열어 두면 안된다.
  • 맵핑된 엔티티 클래스의 인스턴스에 대한 작성, 읽기, 저장 및 삭제 오퍼레이션 제공
  • 객체의 1 단계 캐시 유지
    • 데이터베이스에 커밋하기 전에 개체를 자체 권한으로 유지
      • CRUD를 하면 바로 DB에 반영되는 것이 아니라 Cache에 저장해 두었다가 반영한다.  
        -> flush를 하게 되면 Cache에 있는 것이 DB에 반영이 된다.
      • Cache는 performance optimization을 위해 사용된다.

Object States

객체 상태

- 처음에 객체를 만들게 되면 객체는 Transient 상태가 되고 save나 saveOrUpdate()로 디비에 저장이 되면 이 객체는 Persistent상태가 된다. 또 세션을 close()하게 되면 Detached 상태가 된다.

 

예시 코드

public class HibernateTest {
    public static void main() {

	Person person = new Person();	// Transient Object
	person.setUserName("Test User"); 
		
	SessonFactory sessionFactory = 
		new Configuration().configure().buildSessionFactory();
	Sesson session = SessionFactory.openSession();
	session.beginTransaction();
		
	session.save(person);  // Persistent Object
		
	// Any Changes made to the persistent object get reflected in the DB
	person.setUserName("Updated User");
	person.setUserName("Updated User Again");
		
	session.getTransaction().commit();
	session.close();
		
	// Detached Object; hibernate is not going to track the changes
	person.setUserName("Updated User After session close");		
    }
}
  • session.save(person)을 한 상태에서 -> DB 반영됨
  • person.setUserName으로 값을 변경하게 되면 DB 반영된다.
  • session.close()를 하게 되면 이제 DB에 완전히 반영되어 setUserName을 해도 반영되지 않는다. 

 

Session Methods( Hibernate를 사용하면 sql 대신 session Methods 사용 )

  • get()
    : 값을 읽어옴 , 없으면 NULL 값이 넘어온다.
  • save
    : 객체를 디비에 저장한다, 참조한 테이블들도 cascade로 저장해준다.
  • saveOrUpdate()
    : identifier가 존재하면 update가 호출되고 없다면 save가 호출된다.
  • delete()
    : 객체에 해당되는 DB의 Record가 사라진다.
  • flush()
    : session memory(cache)에 저장되어있는 객체를 database에 동기화시켜주는 작업을 해준다.
    flush mode
    : always, commit, manual(수동),
    Auto(default, 쿼리가 실행되기 전에 트랜잭션이 완료됐을 때... 계속 실행됨
    -> 너무 자주 flush를 시켜주어 performance가 좋지않다, 초보용) 
    flush 과정
    Step 1: Begin transaction
    Step 2: Create employee A
    Step 3: Create employee B
    Step 4: Associate A with its manager C
    Step 5: Look up all employees reporting to C
    Step 6: Associate B with its manager D
    Step 7: Look up all employees reporting to D
    Step 8: Commit transaction
    => Step 5에서 디비를 확인해보면 반영이 안되어있다.(cache에서 아직 db로 반영이 안되어있다.)
    따라서 Step 4 다음에 flush를 해주어야 한다.
  • close()
    : JDBC 연결을 해제하고 정리하여 세션을 종료 but 스프링이 알아서 해줌
    예시 코드
@Repository
@Transactional
public class ProductDao {

    @Autowired
    private SessionFactory sessionFactory;

    public Product getProductById (int id) {
        Session session = sessionFactory.getCurrentSession();
        Product product = (Product) session.get(Product.class, id);

        return product;
    }

    public void addProduct (Product product) {
        Session session = sessionFactory.getCurrentSession();
        session.saveOrUpdate(product);
        session.flush();
    }
}
  • createQuery()
    : 내가 쿼리를 만들 수 있다. -> HQL
    HQL(Hibernate Query Language)
    • SQL similarity
    • 테이블, 칼럼 이름이 아니라 클래스와 프로퍼티 이름을 사용한다.
    • 테이블 이름이 아니라 클래스 이름이기 때문에 첫 문자를 대문자로 표현한다.
    • Java 클래스와 그 속성의 대소 문자 구분 이름을 고려한다.
    • SQL과 마찬가지로 HQL의 키워드는 대소 문자를 구분하지 않는다.

 

예시 코드

//List Query
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Product");
List<Product> productList = query.list();


//Search Query
String hql = "from Product where category.name = 'Computer‘ ";
Query query = session.createQuery(hql);
List<Product> listProducts = query.list();
...

//Update Query
String hql = "update Product set price = :price where id = :id";
 
Query query = session.createQuery(hql);
query.setParameter("price", 500.0);
query.setParameter("id", 45);
 
int rowsAffected = query.executeUpdate();
...

//Delete Query
String hql = "delete from OldCategory where id = :catId";
 
Query query = session.createQuery(hql);
query.setParameter("catId", new Long(1));
 
int rowsAffected = query.executeUpdate();
...

※ hql로 참조하여 검색하는 경우 내부적으로 join이 일어나 검색을 해준다.

 

References

https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-one-to-one-using-annotations-1.html

 

Hibernate Mapping One-to-One using Annotations Tutorial - DZone Database

In this example you will learn how to map one-to-one relationship using Hibernate Annotations. Consider the following relationship between Student and Address...

dzone.com

https://roadtoprogramming.com/hibernate-architecture/

 

Hibernate Architecture - RoadToProgramming %

Hibernate Architecture -

roadtoprogramming.com

https://www.slideshare.net/joseluismms/hibernate-an-introduction-43883660

 

Hibernate an introduction

Hibernate - an introduction

www.slideshare.net

 

'Spring > 이론' 카테고리의 다른 글

Hibernate with Spring  (0) 2020.04.19
Entity Relationships  (0) 2020.04.17
Apache Tiles  (0) 2020.04.08
Logging (SLF4J and Logback)  (0) 2020.04.01
Spring Security  (0) 2020.03.30

댓글